# modelctl Interfaces: CLI, TUI & MCP

> Three ways to drive the modelctl model toolchain — scriptable CLI, interactive terminal UI, and an MCP server for coding agents — and when to use each.

`modelctl` can be driven three ways. All three run the **same underlying CLI
commands** (`convert` / `validate` / `quantize` / `package`), so results are
identical — pick the interface that matches how you work:

| Interface | Best for | Install extra |
| --- | --- | --- |
| **CLI** (`modelctl`) | Scripting, CI pipelines, one-off commands | core (none) |
| **TUI** (`modelctl-tui`) | Interactive use — forms, live output, no flag memorizing | `[tui]` |
| **MCP server** (`modelctl-mcp`) | Coding AI agents (Claude Code, Cursor, …) calling typed tools | `[mcp]` |

The TUI and MCP server are thin subprocess wrappers over the CLI and share the
same [auto-routing](#auto-routing-picking-the-right-environment): they detect
your `venv-torch` / `venv-tf` environments and send each command to one that has
the needed framework, so you never run a step in the wrong environment. Neither
imports `torch`/`tensorflow` itself, so both are dependency-conflict-free.

This page covers the **interfaces**. For the end-to-end model preparation
workflow (convert → validate → quantize → package) and flag details, see
[Preparing Models with modelctl](/configure/prepare-models/).

## CLI (`modelctl`)

The canonical interface. Four subcommands, plain exit codes, ✓/✗ check output —
made for shells and CI.

```bash
# Convert a TorchScript model to ONNX
modelctl convert --source model.pt --format pytorch --output model.onnx \
  --input-shape 1,3,224,224 --opset 18

# Validate (schema · opset vs edge runtime · runtime load · test inference)
modelctl validate --model model.onnx --target-ort 1.18

# Dynamic INT8 quantization
modelctl quantize --model model.onnx --output model.int8.onnx

# Package into a checksummed offline bundle
modelctl package --model model.onnx --name pump-anomaly-v1
```

<figure class="diagram-card">
  <img
    src="/diagrams/modelctl-cli-session-placeholder.svg"
    alt="Placeholder — terminal screenshot of a modelctl CLI session running convert, validate, quantize, and package with ✓ check lines"
    width="1042"
    height="360"
  />
  <figcaption>A typical CLI session: each command prints ✓/✗ checks and exits non-zero on failure.</figcaption>
</figure>

CLI-specific points:

- **Config file**: `modelctl convert --config model.yaml` reads a YAML config;
  precedence is **CLI flags > YAML > defaults**.
- **Exit codes** map to failure stages (3 = missing dependency, 4 = convert,
  5 = validate, 6 = package, 7 = quantize) — gate CI on them.
- **Reproducible bundles**: pass `--timestamp` (or set `SOURCE_DATE_EPOCH`) so
  `metadata.json` / `checksums.sha256` are byte-identical across runs.

## TUI (`modelctl-tui`)

An optional [Textual](https://textual.textualize.io/) front-end: pick a command,
fill in its form, and watch the ✓/✗ output stream live with a success/error badge.

```bash
pip install 'modelctl[tui]'
modelctl-tui        # run from the directory containing venv-torch / venv-tf
```

### Screen anatomy

```text
┌ modelctl-tui ──────────── envs: venv-torch[core, pytorch, quantize]  venv-tf[core, quantize, tensorflow] ┐
│ Command                          │ Output                                                  │
│  ( ) convert                     │ $ venv-torch/bin/modelctl validate --model model.onnx   │
│  (•) validate                    │ ✓ ONNX schema valid                                     │
│  ( ) quantize                    │ ✓ Opset supported                                       │
│  ( ) package                     │ ✓ ONNX Runtime load successful                          │
│ Options                          │ ✓ Test inference successful                             │
│  --model *   [model.onnx      ]  │                                                         │
│  --target-ort [1.18           ]  │                                                         │
│  [ Run ]                         │                                                         │
│  → will run in: venv-torch [...] │  ● SUCCESS exit 0                                       │
└──────────────────────────── q quit  ctrl+r run  ctrl+e re-detect envs ────────────────────┘
```

- **Header** — detected environments and their capabilities (`core` / `pytorch` /
  `tensorflow` / `quantize`).
- **Command + Options (left)** — the form changes per command; `*` marks
  required fields.
- **Run hint** — the line under **Run** says which environment the command will
  run in, or why it can't run yet.
- **Output (right)** — live ✓/✗ stream, with a `● SUCCESS exit 0` /
  `● FAILED exit N` badge.

<figure class="diagram-card">
  <img
    src="/diagrams/modelctl-tui-screen-placeholder.svg"
    alt="Placeholder — screenshot of the modelctl-tui screen with command list, option form, live output pane, and status badge"
    width="1042"
    height="360"
  />
  <figcaption>The real TUI, showing a validate run routed to venv-torch.</figcaption>
</figure>

### Keybindings

| Key | Action |
| --- | --- |
| `ctrl+r` | Run the selected command (same as **Run**) |
| `ctrl+e` | Re-detect environments (after creating/editing a venv) |
| `q` | Quit |

If the header says `envs: none detected`, launch `modelctl-tui` from the
directory that contains `venv-torch/` / `venv-tf/`, or press `ctrl+e` after
creating them.

## MCP server (`modelctl-mcp`)

An optional [Model Context Protocol](https://modelcontextprotocol.io/) stdio
server that exposes the same commands to coding agents as **typed tools**, so an
agent can prepare models for the edge box without shell access.

```bash
pip install 'modelctl[mcp]'
```

Register it with your agent — for Claude Code, in `claude.json` (Cursor is
analogous):

```json
{
  "mcpServers": {
    "modelctl": {
      "command": "modelctl-mcp",
      "cwd": "/path/with/venv-torch-and-venv-tf"
    }
  }
}
```

The `cwd` must contain the `venv-torch/` / `venv-tf/` directories (the router's
default search roots) — or have the agent call `list_environments` first to
confirm what is routable.

### Tools

| Tool | Purpose | Key parameters |
| --- | --- | --- |
| `list_environments` | Discover environments and their capabilities | — |
| `convert` | PyTorch / TensorFlow → ONNX | `source`, `format`, `output`, `input_shape` |
| `validate` | Schema, opset, runtime load, test inference | `model`; opt. `target_ort`, `no_runtime` |
| `quantize` | Dynamic INT8 quantization | `model`; opt. `output`, `input_shape` |
| `package` | Validate + bundle into a checksummed offline release | `model`, `name`; opt. `version`, `quantize`, `out_root` |

Every tool returns the same result shape: `command`, `env_label`, `exit_code`,
`ok`, `stdout`, `stderr`, and `artifact_path` (the produced file/bundle on
success) — an agent should check `ok` and then use `artifact_path`.

<figure class="diagram-card">
  <img
    src="/diagrams/modelctl-mcp-agent-session-placeholder.svg"
    alt="Placeholder — screenshot of a coding-agent session calling list_environments, convert, and validate MCP tools with JSON results"
    width="1042"
    height="360"
  />
  <figcaption>A coding-agent session: list_environments → convert → validate, each returning a typed result.</figcaption>
</figure>

## Auto-routing: picking the right environment

The `quantize` and `tensorflow` extras cannot share one Python environment (an
`ml_dtypes` version conflict), which is why the tooling keeps two venvs. The TUI
and MCP server probe each environment's capabilities and route per command:

| Command | Needs capability | Typically routed to |
| --- | --- | --- |
| `validate`, `package` (without quantize) | core | any environment |
| `convert --format pytorch` | pytorch | `venv-torch` |
| `convert --format tensorflow` | tensorflow | `venv-tf` |
| `quantize`, or any `--quantize` flag | quantize | `venv-torch` |

One combination is unsatisfiable by design: **TensorFlow convert together with
quantize**. Both interfaces surface this instead of failing mid-run — the TUI
disables **Run** with a hint, MCP returns an error — do it in two steps:
`convert` (tensorflow) to `.onnx` first, then `quantize` that `.onnx`.

## Next steps

  - [Preparing Models with modelctl](/configure/prepare-models/) — The full convert → validate → quantize → package workflow and flag reference.
  - [modelctl Best Practices](/configure/modelctl-best-practices/) — Do/don't guidance: profiles, validation gating, reproducible bundles, CI.
  - [Deploy the model](/configure/models/) — Upload the ONNX bundle, pair it with a datasource, activate.
