Xiaomi's terminal coding agent, run headless by the OpenAgents daemon.
- Registry entry:
registry/mimo-code.json - Adapter:
packages/agent-connector/src/adapters/mimo-code.js(extendsopencode.js), with its pure helpers inmimo-code-runtime.js - Project: https://github.com/XiaomiMiMo/MiMo-Code
MiMo Code is a fork of OpenCode. mimo run --format json streams OpenCode's
JSON events (step_start, text, tool_use, step_finish, error) and
--session resumes a session, so the adapter reuses OpenCodeAdapter for stream
parsing and the failure taxonomy, and overrides how the CLI is found,
configured, stopped and how its failures are read.
Checked against CLI 0.1.14 and 0.1.15 (Linux x64) — the only two releases the
download server still serves. 0.1.14 is the registry's min_version.
Xiaomi's own script, not npm:
| Platform | Command |
|---|---|
| macOS / Linux | curl -fsSL https://mimo.xiaomi.com/install | bash |
| Windows | irm https://mimo.xiaomi.com/install.ps1 | iex |
Both install one native binary (Bun-compiled, ~135 MB):
| Platform | Location |
|---|---|
| macOS / Linux | ~/.mimocode/bin/mimo |
| Windows | %USERPROFILE%\.mimocode\bin\mimo.exe, or %MIMOCODE_INSTALL_DIR%\mimo.exe when that is set |
The scripts reach PATH through a shell rc edit or the user Path value, which
an already-running app never sees, so paths.js lists both directories. The
binary is spawned directly; there is no wrapper.
The first command on a machine migrates MiMo's database and imports the user's
Claude Code transcripts (~/.claude/projects) into it — about a minute for
~1,600 transcripts. Later runs rescan quickly.
Two ways in: a Xiaomi-account sign-in, or an API key. All three fields are optional, because the sign-in needs none of them.
| Variable | Required | Meaning |
|---|---|---|
LLM_API_KEY |
no — blank runs on the sign-in | A MiMo API key (https://platform.xiaomimimo.com), a MiMo Token Plan key, or a key for the endpoint below |
LLM_BASE_URL |
no, prefilled | https://api.xiaomimimo.com/v1. Token Plan: https://token-plan-cn.xiaomimimo.com/v1 (also -ams, -sgp). Any OpenAI-compatible endpoint works. |
LLM_MODEL |
for a non-MiMo endpoint | Model id, e.g. mimo-v2.6-pro; sent upstream as written, a gateway's vendor/model ids included. Blank on MiMo's endpoints: the first -pro model mimo models xiaomi lists |
The workspace's model choice, when set, replaces LLM_MODEL.
- A MiMo endpoint (any
*.xiaomimimo.comhost) runs on MiMo's built-inxiaomiprovider with this key and base URL, which keeps what MiMo's catalog knows about each model (context window, reasoning round-trip). With no model set the adapter asksmimo models xiaomionce and runs the first plain-promodel it lists (see below). A model that is set is declaredstatus: "beta", because MiMo drops every model its catalog marks deprecated — the v2.5 line now — even when the run names it, while the key's API still serves them and the picker lists them from it. - Any other endpoint is declared for the run as an
@ai-sdk/openai-compatibleprovider namedopenagents; a model is required.
With no key, the run is on the Xiaomi-account sign-in. The adapter reads the
xiaomi entry of auth.json (its key, and its Token Plan base_url if any)
and declares it for the run exactly as it declares a form key, on the xiaomi
provider. XIAOMI_API_KEY is not a way in: it is removed from the CLI's
environment, and a signed-in agent's core environment drops it too.
What MiMo will run is its catalog minus what it marks deprecated: on 0.1.15
that is mimo-v2.6-flash, mimo-v2.6-pro and mimo-v2.6-pro-ultraspeed
(signed in or not — it is not per account), and mimo-v2.5-pro is Model not found unless declared beta (see above). So there is no fixed default: with
no model the adapter asks mimo models xiaomi once per agent and pins the
first plain -pro it lists. Leaving the pick to MiMo instead hands it to the
user's own model / agent.*.model settings, which name other providers and
fail. The sign-in tab lists the same command (ids written xiaomi/<model>)
and preselects the same model.
- Each run's credential is declared in its own config — the form key, or the
sign-in's key read from
auth.json— withheaderscleared, and the run starts withMIMOCODE_AUTH_CONTENT={}, which the CLI reads in place ofauth.json. So nothing MiMo has stored or the user configured in MiMo (aprovider.xiaomi.options.apiKey, anXIAOMI_API_KEY, the sign-in under a key run) stands in for it. A wrong form key failsauth_failedon a signed-in machine; a sign-in runs even with a wrongapiKeyin the user's MiMo config. Only a model on a provider of the user's own MiMo setup (provider/model) readsauth.json, as theirs. - A sign-in run gets no form key: an agent set up on the sign-in tab carries
OPENAGENTS_AUTH_MODE=cli_login, and the core drops every key and base URL (andXIAOMI_API_KEY) from its environment, whatever the type has saved. - The launcher labels each by its own path (a key reads "API key" whether or not the CLI is signed in; a sign-in agent is judged on the sign-in only), and the model lists come from different places: the key's endpoint, or the CLI.
- Neither writes to the other: a key run leaves
auth.jsonas it was. - The launcher's Sign in counts only MiMo's
Login successful: a failed login exits 0, and an earlier sign-in still on disk would satisfy the probe.
mimo providers login -p xiaomi is not an OAuth session. It opens
https://platform.xiaomimimo.com/authorize?pk=… with an X25519 public key; after
the user signs in, the platform issues the CLI an API key of its own (listed on
the platform as mimo-code-cli-key-…) encrypted to that key, and the CLI stores
it in ~/.local/share/mimocode/auth.json (Windows too, under the user profile):
{ "xiaomi": { "type": "api", "key": "…", "metadata": { "uid": "…", "base_url": "…" } } }base_url is set for a Token Plan account and the CLI routes there itself. The
key does not expire; deleting it on the platform signs the agent out, which a
run reports as auth_failed.
The page reaches the CLI one of two ways: a redirect to a server the CLI starts
on localhost, or a code shown on the page to paste back at
Paste code here if prompted > (three tries; then Decryption failed, max retries reached and exit 0). Those two "Decryption failed" lines are in the
language MiMo picks from the time zone, then LANG (Chinese in China); the
prompt, Login successful and Login failed are always English. The CLI opens
the browser only with xdg-open, and the URL it prints is always the
paste-a-code one.
The launcher's Sign in runs that command over pipes with CI=1, which stops the
CLI opening a browser, opens the printed URL itself — one tab on every platform
— and takes the code in the app. Only Login successful counts as
signed in: a failed login exits 0 too, and an earlier sign-in on disk would
satisfy a probe. Sign-in is read from auth.json (a xiaomi entry holding a key), by the
launcher and by the core's check_ready (creds_file + creds_key). Like the
CLI, both look under $XDG_DATA_HOME/mimocode/ when that is set
(creds_xdg_data_file; an older core reads only the default path).
mimo run --format json --dangerously-skip-permissions --dir <workdir> --model <provider>/<model>
[--title "OpenAgents - <channel>" | --session <id>] [--agent plan]
Environment:
| Variable | Why |
|---|---|
MIMOCODE_CONFIG_CONTENT |
The run's config (below). MiMo loads it after the user's global and project config and removes it from the environment of the commands its tools run |
MIMOCODE_EXPERIMENTAL_CRON=0 |
A one-shot run cannot fire a schedule; with the scheduler on, MiMo creates .mimocode/ in the project and the next run installs its plugin package there |
MIMOCODE_DISABLE_AUTOUPDATE=1 |
A run does not replace the binary |
LLM_API_KEY itself is removed from the CLI's environment: the key travels in
the config, where the model's shell commands cannot print it.
The config:
provider— the key and base URL as configured (key mode only).enabled_providers: [<that provider>],disabled_providers: [], andmodel,small_model,model_groups.{lite,standard,ultra}andagent.{general,explore}.modelall set to the run's model. Without this the user's own MiMo config reroutes parts of a run under the user's own keys: asmall_modelreceived the first message of every session for titling, and a subagent ran on the user'sagent.general.model. Providers MiMo picks up from the environment (DASHSCOPE_API_KEY,XIAOMI_API_KEY, …) or auth.json are likewise switched off.test/mimo-code-cli.test.jsreproduces both against a real CLI.mcp.openagents-workspace— the workspace MCP server (agent-connector mcp-server), its token in the server's own environment. MiMo names its toolsopenagents-workspace_<tool>.instructions: [<briefing file>]— the workspace briefing, rewritten before every run under~/.openagents/agents/<agent>/mimo-code/. MiMo puts it in the system prompt each turn, so mode changes and pinned decisions are current; it holds no key or token. MiMo concatenatesinstructionswith the user's own.
Nothing is written into the project or into the user's MiMo config.
- The prompt goes on stdin.
runreads stdin to EOF whenever it is not a TTY, so it is always closed. --dangerously-skip-permissions: a permission MiMo would ask for (writing outside the project, for one) is otherwise refused on the spot and the turn ends with no reply. The daemon is the approval boundary, as for every other agent here. Plan mode uses MiMo'splanagent, whose edit ban is a hard permission the bypass does not lift.--titleon a new session saves a request: without it MiMo asks the model to title the session, sending it the first message a second time.- The session id is stored per channel as soon as the stream names it, so a stopped turn's session is resumed by the next message.
MiMo has no signal handling: SIGINT or SIGTERM ends it at once. Each shell
command its tools start runs in a session of its own, outside the CLI's process
group, so signalling the group leaves those commands running. Stop therefore
reads the process tree first (ps -A -o pid=,ppid=,pgid=), then signals the
CLI's group and every descendant's group — SIGTERM, then SIGKILL after 1.5 s.
On Windows taskkill /T /F is run straight away, while the CLI is still alive
to walk the tree from.
A stopped session resumes; its first event is the interrupted tool call with
status error.
Nearly everything exits 0:
| Situation | What the CLI does | Category |
|---|---|---|
| Key rejected (401) | error event on stdout |
auth_failed |
| Model MiMo's API does not serve | HTTP 400 Unsupported model <id>, an error event at once |
model_not_found |
| Unknown model on another endpoint (404) | retries for about three minutes, then an error event |
model_not_found |
| Server error (5xx) | retries without end, nothing on stdout | timeout (idle watchdog, 5 min) |
| No credential | stderr error: MiMo free API service has ended. Sign in or configure a third-party API. |
credential_missing |
--session it does not have |
stderr NotFoundError … Session not found: <id> |
session_missing — the adapter drops the session and runs the turn again in a new one |
On stderr the error line follows a chunk of the CLI's bundled source and is followed by a stack trace; only the error line is classified.
- A Token Plan key. A MiMo API key on
api.xiaomimimo.com/v1withmimo-v2.6-prowas run end to end (reply, multi-turn on one session, an MCP tool, Stop and continuing after it), as was a Model Studio key through the custom-provider route.GET /v1/modelswith the key lists what the model pickers offer — including MiMo's ASR/TTS models, which are not chat models. - Windows: install layout read from install.ps1; the Stop path there is
taskkill /T, not exercised. - A Xiaomi-account sign-in of a plain (non-Token Plan) account. A Token Plan
account was signed in for real on 0.1.15 — the authorize page, the pasted
code,
Login successful, the storedtp-key with its Token Planbase_url— and run end to end with no key onxiaomi/mimo-v2.6-pro(reply, multi-turn on one session, an MCP tool, Stop and continuing after it) and with no model (MiMo pickedmimo-v2.6-flash). The core'scheck_readyand the launcher probe both read thatauth.jsonas signed in.