mcp-opencode

🧰 Tools

The tools mcp-opencode exposes — query a model, list models, talk to a live opencode session, and run headless jobs on private instances.

mcp-opencode exposes ten tools: query and list_models for one-off questions; list_sessions, send and read for holding a conversation with a live opencode session; and start_instance, task, wait, list_instances and stop_instance for running headless jobs on a private opencode server.

⚡ query

Send a prompt to an opencode model and return the response.

ParameterTypeRequiredDescription
promptstringyesThe prompt to send.
modelstringnoModel in provider/model format. Defaults to github-copilot/gpt-4.1.

Each call creates a temporary opencode session, sends the prompt, returns the text response, and deletes the session. If the requested model is blocked by your allow/block filters, the call returns an error explaining the model isn't allowed — see Model filtering.

The tool description surfaced to your assistant includes the default model and a summary of the active filters, so the assistant always knows what's available.

🔍 list_models

List the models available for use.

ParameterTypeRequiredDescription
providerstringnoProvider name to filter by (e.g. anthropic, openai). Omit for all.

Returns one provider/model ID per line. The list reflects your opencode configuration — exactly what opencode models prints in your terminal — passed through your allow/block filters. Pass a provider to narrow the list to a single provider.

If no models match (for example, an unknown provider name), the tool returns a clear No models found message rather than an empty response.

🗂️ list_sessions

List the sessions on opencode's background service (shared by every TUI window) and on the headless instances this MCP started, most recently updated first.

ParameterTypeRequiredDescription
directorystringnoOnly list sessions for this project directory.

Returns one session per line: ID, title, directory, the port(s) of the servers serving it, and last update. Use it to find the session you have open in the opencode TUI.

💬 send

Send a message to an existing session and return its reply.

ParameterTypeRequiredDescription
session_idstringyesSession ID from list_sessions.
promptstringyesThe message to send.
agentstringnoopencode agent for the session (e.g. build, plan).
modelstringnoModel for the session, provider/model. Must pass the allow/block filters.
portnumbernoServer to use. Defaults to the server that owns the session.
timeout_secondsnumbernoHow long to wait for the reply. Defaults to MCP_OPENCODE_SEND_TIMEOUT (600).

The session keeps its history, and the exchange appears live in any TUI showing it. send never creates or deletes a session. opencode 2 sets agent and model on the session rather than per message, so passing either switches the session for later turns too, as picking it in the TUI does. Model allow/block filters apply only when you pass a model.

If the reply takes longer than the timeout, send returns a "still running" note instead of hanging. opencode keeps working; call read later for the answer.

📜 read

Read a session's recent messages as a condensed transcript.

ParameterTypeRequiredDescription
session_idstringyesSession ID from list_sessions.
limitnumbernoNumber of most recent messages to return. Default 20.
portnumbernoServer to read from. Defaults to the one that owns the session.

Each message is headed by its role, with text in full and tool calls summarised as [tool: name (status)].

🏗️ Headless instances

The five tools below run jobs on a private opencode server per job, away from the background service your windows use. An instance is started for one project directory, takes one or more tasks, and is stopped when you are done (or reaped once idle).

start_instance

ParameterTypeRequiredDescription
directorystringyesAbsolute path of the project the instance works in.

Spawns opencode serve --port 0 --hostname 127.0.0.1 in directory, reads the port from its server listening on … line (10s timeout), registers it with the password it was started with, and returns { port, url }. With MCP_OPENCODE_SANDBOX=srt the same command is wrapped as srt --settings <generated file> opencode serve … (or your MCP_OPENCODE_SANDBOX_SETTINGS file verbatim), and the registry row records "sandbox": "srt" (else null). The server runs with a hardened environment:

  • GIT_TERMINAL_PROMPT=0, GIT_SSH_COMMAND=false and an empty credential.helper, so git cannot authenticate to a remote;
  • GH_TOKEN and GITHUB_TOKEN removed, and a fresh random password in OPENCODE_PASSWORD (basic auth, user opencode);
  • OPENCODE_CONFIG_CONTENT in opencode 2's native shape: model and agents.title.model pinned to MCP_OPENCODE_MODEL, the permission rules below in permissions so every agent in the instance, subagents included, follows them, and every deny repeated in policies.

It refuses to start if the default model fails the allow/block filters.

task

ParameterTypeRequiredDescription
portnumberyesPort from start_instance.
promptstringyesWhat the job should do.
modelstringnoprovider/model. Defaults to MCP_OPENCODE_MODEL; filtered.
agentstringnoopencode agent. Defaults to build.
titlestringnoSession title.

Creates a fresh session with the guard rails, prompts it in the background and returns at once with { session_id, port, attach }. It never deletes the session. A port that start_instance didn't register is refused.

The job tries [its model, ...MCP_OPENCODE_MODEL_FALLBACK] once each, in order (deduped, allow/block-filtered). When the session stays in retry for MCP_OPENCODE_RETRY_TIMEOUT_SECONDS (default 90) or its reply ends in a provider error, a per-job watchdog interrupts the session, switches its model and re-prompts the same session with the same prompt on the next model, so history and worktree edits carry over — even when nobody calls wait. When the list is exhausted the job settles as error.

wait

ParameterTypeRequiredDescription
portnumberyesPort of the instance.
session_idstringyesSession ID from task.
timeout_secondsnumbernoDefault and maximum 570.

Polls every 2s until the session is idle or the timeout hits, then returns its status (idle, busy, retry or error), the current model, the fallbacks taken so far ([{ from, to, reason, at }]), the last assistant text, and the files the job changed with line counts. The changed-files diff spans the whole session, so fallback re-prompts don't re-base it. The 570s cap keeps it under a 600s tool timeout; on busy or retry, call wait again.

list_instances

Reaps first, then returns every registered instance with the sessions task started on it, each with its live status, current model and fallbacks — plus each instance's sandbox mode ("srt" or null).

stop_instance

ParameterTypeRequiredDescription
portnumberyesPort of the instance.

Interrupts busy sessions, sends SIGTERM (then SIGKILL if needed), deregisters the instance and confirms the port has stopped listening.

🛡️ Guard rails

Every task session gets a ruleset with no ask anywhere, because nobody is there to answer and a headless session would wait forever. opencode applies the last matching rule, so the shell denies come after the shell allow:

ActionEffect
read, edit, glob, grep, subagentallow
external_directory, question, webfetch, websearchdeny
shell *allow
shell *git* push*, *git* remote*, *git* reset*--hard*, gh *, * gh *, *npm* publish*deny

opencode 2 allows external_directory by default; the instance keeps it denied. Each deny is also a tool.use policy (shell:*git* push*, webfetch:*, …). Policies run after every rule and can only deny, so a repository's own opencode config cannot allow these back.

📦 Sandboxing (opt-in)

Set MCP_OPENCODE_SANDBOX=srt to wrap each instance in an OS-level sandbox via srt (npm i -g @anthropic-ai/sandbox-runtime; macOS sandbox-exec, Linux bubblewrap). Unset, everything behaves as before. Anything else fails start_instance with the supported values, and a missing srt fails the start rather than running unsandboxed — never silently.

The generated settings file allows writes only to the instance directory, its git dir(s) (common and per-worktree, so worktrees work), opencode's own data/config/cache/state dirs and the temp dir; denies reads of credential paths (~/.ssh, ~/.aws, ~/.config/gh, ~/.gnupg, ~/.netrc, ~/.npmrc, **/.env, **/.env.*, ~/Library/Keychains, ~/.config/gcloud, ~/.kube, ~/.docker/config.json, and the background service's service.json files, which hold its password); and allows only the provider, github, npm and loopback domains plus local binding. MCP_OPENCODE_SANDBOX_SETTINGS points at your own settings file to use instead of the generated one. Sandboxed instances additionally deny rm -rf*, sudo * and piped curl/wget in their opencode permission config — deny, never ask, because a headless session would wait forever on a prompt.

This is defence in depth, not a guarantee: see the README's Sandboxing section for the verified-vs-assumed allowlist and the limits.

🧹 Reaping

The reaper runs at MCP start-up, on start_instance and on list_instances. It drops instances whose process is gone and stops instances where every session has been idle for longer than MCP_OPENCODE_INSTANCE_TTL seconds (default 1800). It never stops a busy instance and never touches a server it didn't start. When the MCP exits, it stops the instances it started.

🔭 Watching a job

task returns an attach command. Run it in a terminal to open the session in the TUI against the instance, with the instance's password, and watch it live or step in:

OPENCODE_PASSWORD=… opencode --server http://127.0.0.1:53817 --session ses_…

On this page