Skip to content

Repository files navigation

subagent-gateway

An MCP server (stdio transport) that delegates subtasks from Kimi Code — or any MCP-compatible agent — to a locally running opencode CLI. It gives your main agent a "coworker powered by a different vendor's model": opencode executes the task with whatever model it has configured (DeepSeek, GLM, …), while the gateway handles session continuity, permissions, timeouts and result retrieval.

中文版 README · AI 安装指南(给 Agent 看的)

Install with your AI agent (one sentence)

Don't want to install manually? Paste this to your AI coding agent (Kimi Code, Claude Code, etc.) and it will do everything for you:

Read https://github.com/SiliconLight/subagent-gateway/blob/main/给Kimi的安装指南.md and follow it to install the subagent-gateway MCP server on this machine: clone the repo to a permanent location, install dependencies, run the smoke test, register it in my MCP client config (merge into existing entries, never overwrite the whole file), then tell me to restart the client.

Only prerequisites: Node.js ≥ 18 and the opencode CLI with a configured model provider — the agent will verify these for you. Manual installation is documented below.

Using it day to day

Once installed, just talk to your main agent naturally:

  • Delegate work — "hand this to the sub-agent: …". Note each call starts cold unless you keep using the same task thread (agent_id), so make the task self-contained.
  • List available models — "what models can the sub-agent use?" (runs list_models).
  • Change the model — say "change the sub-agent model": the agent lists opencode's configured models for you to pick, then asks for a reasoning effort — pick Auto to let the agent decide per task. (Requires the AGENTS.md snippet below.)
  • Iterate in one conversation — keep critiquing the result; the same conversation keeps full context across rounds (session continuation).
  • Permission gate — outside YOLO mode, when the sub-agent tries to edit files or run shell commands you get back an error describing the blocked action; approve it and the agent retries with auto_approve: true, continuing in the same session.
  • Reset a sub-agent — delete its entry in <projectDir>/.kimi-code/external-agents.json to start a fresh session next time.

Features

  • Session continuation — pass the same agent_id to reuse the same opencode session with full history; ideal for iterative multi-round tasks.
  • Per-call model & reasoning effortmodel is forwarded to opencode -m, variant to --variant (e.g. deepseek/deepseek-v4-pro supports high/max). Both can change between rounds of the same session.
  • Permission bridgingauto_approve: true runs opencode with --auto (YOLO-style); by default, permission asks are auto-rejected by headless opencode, the gateway detects the resulting stall (~60 s), aborts the session, and returns the rejected tool call so the main agent can ask its user and retry.
  • Robustness — auto-starts opencode serve with an identity check (not just "port open"), fail-fast polling when the server dies or a session 404s, timeout that actually aborts the serve-side session (no orphaned token burn), per-agent_id mutex, process-tree kill on Windows, UTF-8-safe argument passing (no cmd.exe codepage mangling).

Requirements

  • Node.js ≥ 18
  • opencode CLI installed and in PATH, with at least one model provider configured (opencode models lists models). Developed and tested against opencode 1.18.x on Windows; other platforms should work via the PATH fallback (see OPENCODE_EXE below).
  • An MCP client. Kimi Code is documented below, but any client that speaks MCP over stdio works.

Install

git clone <your-fork-url>
cd subagent-gateway
npm install

Smoke test

  1. Protocol handshake + tool list (no opencode needed):
printf '%s\n' \
  '{"jsonrpc":"2.0","id":1,"method":"initialize","params":{"protocolVersion":"2025-03-26","capabilities":{},"clientInfo":{"name":"smoke","version":"0.0.1"}}}' \
  '{"jsonrpc":"2.0","method":"notifications/initialized"}' \
  '{"jsonrpc":"2.0","id":2,"method":"tools/list"}' \
  | node server.js

You should see the initialize result and two tool definitions (delegate_task, list_models).

  1. End-to-end delegation (really calls the configured model; auto-starts opencode serve on first run):
printf '%s\n' \
  '{"jsonrpc":"2.0","id":1,"method":"initialize","params":{"protocolVersion":"2025-03-26","capabilities":{},"clientInfo":{"name":"smoke","version":"0.0.1"}}}' \
  '{"jsonrpc":"2.0","method":"notifications/initialized"}' \
  '{"jsonrpc":"2.0","id":2,"method":"tools/call","params":{"name":"delegate_task","arguments":{"task":"Reply with exactly the two characters OK.","model":"deepseek/deepseek-v4-flash","agent_id":"smoke-test"}}}' \
  | node server.js

After success, <projectDir>/.kimi-code/external-agents.json should contain a smoke-testses_… session mapping.

Client configuration (Kimi Code)

Add the following snippets manually — this repo never touches your config files.

1. ~/.kimi-code/mcp.json

{
  "mcpServers": {
    "subagent-gateway": {
      "command": "node",
      "args": ["<absolute-path-to>/subagent-gateway/server.js"],
      "startupTimeoutMs": 60000,
      "toolTimeoutMs": 600000
    }
  }
}

The server's project directory defaults to its process working directory (i.e. where Kimi launched it). To pin a project directory, add "env": { "SUBAGENT_PROJECT_DIR": "/path/to/project" }.

2. ~/.kimi-code/config.toml — permission rule (skip per-call approvals)

[[permission.rules]]
decision = "allow"
pattern = "mcp__subagent-gateway__*"

Heads-up: this rule makes the main client auto-approve every delegation. What the sub-agent may then do is governed by opencode's permission config, not the client's — see Permission model.

3. Project AGENTS.md — delegation policy (optional, helps the main agent use the tools correctly)

## Sub-task delegation policy

- Prefer the MCP tool `mcp__subagent-gateway__delegate_task` (opencode + external model)
  over the built-in Agent tool when delegating subtasks.
- Call `mcp__subagent-gateway__list_models` first to confirm available models.
- Use a fast model (e.g. deepseek/deepseek-v4-flash) for simple tasks and a stronger one
  (e.g. deepseek/deepseek-v4-pro) for complex tasks; set `variant` for reasoning effort.
- In YOLO / fully autonomous mode pass `auto_approve: true`; otherwise omit it. If a call
  fails with a permission-rejected error, ask the user first, then retry with
  `auto_approve: true` (the same `agent_id` session is preserved).
- When the user says "set/change the sub-agent model" or similar: run
  `mcp__subagent-gateway__list_models` and let the user pick a model, then let them pick
  a reasoning effort — always offer "Auto" first (you decide per task: omit `variant` for
  simple tasks, use a supported variant for complex ones), followed by the variants the
  chosen model actually supports (skip this step if none). Remember the choice for all
  subsequent `delegate_task` calls until the user changes it again.
- Do not call the same `agent_id` in parallel (the server serializes, but sequential
  semantics are clearer); use one `agent_id` for context-preserving multi-round work.

Restart the client, run /mcpsubagent-gateway should show as connected. Then ask the agent to call mcp__subagent-gateway__list_models to confirm the link.

Tools

delegate_task

Parameter Type Required Description
task string yes Self-contained task description
model string no Forwarded to opencode -m; default deepseek/deepseek-v4-pro
agent_id string no Sub-agent identifier; passing it enables session continuation
auto_approve boolean no true: run opencode with --auto (auto-approves everything not explicitly denied — YOLO scenarios). Default false: opencode auto-rejects asks; the gateway detects the stall (~60 s), aborts the session, and returns the rejected tool call as an error
variant string no Reasoning effort, forwarded to opencode --variant (provider-specific). deepseek/deepseek-v4-pro and deepseek/deepseek-v4-flash support high/max; omit for the model default

list_models

Runs opencode models and returns the list verbatim.

Permission model

Read this before delegating real work.

  • The sub-agent runs under opencode's own permission config (~/.config/opencode/opencode.jsonc), completely outside the MCP client's permission system.
  • In headless run mode (how this gateway invokes opencode), an ask rule is instantly auto-rejected by opencode — no pending request ever exists, so nobody can answer it interactively (observed on 1.18.x).
  • Recommended opencode config for use with this gateway:
{
  "permission": { "*": "allow", "edit": "ask", "bash": "ask" }
}

With this, file edits and shell commands flow through the gateway's bridging logic:

  • auto_approve: true → opencode gets --auto, nothing is ever asked.
  • default → the ask is auto-rejected, the gateway's stall detection aborts the session within ~60 s and returns exactly which tool call was rejected, so the main agent can ask its user and retry with auto_approve: true.
  • Setting a tool to deny in opencode config also works (the task fails immediately), it just bypasses the gateway's stall detection.
  • auto_approve: true is deliberately dangerous — it approves everything not explicitly denied. Only use it in YOLO / fully autonomous contexts you trust.

How it works

  • On first call the gateway checks http://localhost:4096 (GET /config must return JSON, so a random process occupying the port is not mistaken for opencode). If unreachable it spawns opencode serve --port 4096 and waits up to 30 s (concurrent calls share one startup).
  • It then runs: opencode run --attach http://localhost:4096 -m <model> --dir <projectDir> --format json [--auto] [--variant v] "<task>"
  • Adaptations to observed opencode 1.18.x behavior (Windows):
    • stdin is closed on spawn — opencode run waits for stdin EOF when stdin is not a TTY;
    • opencode.exe is spawned directly (default %APPDATA%\npm\node_modules\opencode-ai\bin\opencode.exe, override with OPENCODE_EXE) to avoid cmd.exe codepage mangling of non-ASCII arguments;
    • in --attach mode --format json prints only a step_start line and exits — the gateway takes the sessionID from that first line, then polls GET /session/{id}/message until this round's last assistant message completes (finishtool-calls) and extracts its text parts.
  • The agent_id → session mapping lives at <projectDir>/.kimi-code/external-agents.json (contains task summaries — local material, gitignored by this repo's .gitignore):
    • mapping exists → append --session <sessionID> to continue;
    • no mapping → append --title <agent_id> to create a named session and record its sessionID (ses_…);
    • "session not found" errors → automatically retry without --session and update the mapping.
  • One mutex per agent_id: concurrent calls are serialized. Total timeout is 10 minutes per call; on timeout the serve-side session is aborted via POST /session/{id}/abort and the local process tree is killed.
  • Stall detection: if the latest assistant message completed with finish=tool-calls and no new message appears within 60 s, the turn is considered dead (typical cause: permission auto-rejected in headless mode); the session is aborted and the tool errors are returned.
  • Poll failures (serve down, session 404, non-200) throw immediately instead of waiting out the timeout.

Environment variables

Variable Purpose Default
SUBAGENT_PROJECT_DIR Pin the project directory (drives --dir and the mapping file location) server process cwd
OPENCODE_EXE Path to the opencode executable %APPDATA%\npm\node_modules\opencode-ai\bin\opencode.exe, falling back to opencode from PATH

Set them in the server entry's "env" block in mcp.json.

Troubleshooting

  • Server logs go to stderr (startup, executed commands, errors); check /mcp status on the client side.
  • Port 4096 occupied by something else: the identity check (GET /config returning JSON) will refuse to treat it as ready — free the port or change PORT in server.js.
  • Delete an agent_id entry from .kimi-code/external-agents.json to start a fresh session for it next time.
  • After an opencode major upgrade, re-run the smoke tests — this server adapts to 1.18.x behavior (sessionID casing, --format json output shape, headless ask auto-rejection) that may change.

License

MIT

About

MCP server that delegates subtasks from Kimi Code (or any MCP client) to a local opencode CLI sub-agent — with session continuation, permission bridging, and per-call model/effort selection

Topics

Resources

Stars

1 star

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages