native-agent-router
NAR is a model-free routing kernel that lets an orchestrator (or CLI) delegate coding tasks to native worker agents (ZCode/ACP) with deterministic control: workspace locking, scopes, verification, budgets, cancellation, and per-agent scoring.
agents: list configured agents and their capabilities, usage stats, and routing metrics (avg_score, success_rate, blocked_rate, tokens).
run: submit a complete work unit to a specific agent/workspace with goal, scope restrictions, verification command, mode, permission policy, timeout, token budget, optional session continuation, and idempotency key. Returns immediately (submitted), not completion.
wait: block (bounded) until the task reaches a terminal or blocked state; never busy-polls.
inspect: on-demand paged access to task details: status, summary, diff, verify output, log, raw data, usage, and a sanitized diagnosable report.
cancel: request cancellation and report whether the native runtime confirmed the stop.
Model-free kernel: no LLM calls for routing/verification; deterministic state machine, per-turn timeouts, recovery, and score tracking.
Native session resume via
session_reffor follow-up work on the same agent+workspace; cross-agent/workspace reuse rejected.```json{
Integrates with Codex (OpenAI) as an orchestrator agent, enabling it to use the MCP server's tools to submit, monitor, inspect, and cancel coding tasks delegated to native worker agents.
Click on "Deploy Server".
Wait a few minutes for the server to deploy. Once ready, it will show a "Started" state.
In the chat, type
@followed by the MCP server name and your instructions, e.g., "@native-agent-router Implement parse_flag() in flags.py per the docstring; only modify flags.py"
That's it! The server will respond to your query, and you can continue using it as needed.
Here is a step-by-step guide with screenshots.
Native Agent Runtime Router (NAR)
English | 简体中文
Route coding tasks from an orchestrator agent (Codex / opencode / Claude Code) to native CLI worker agents (ZCode, any ACP-compatible agent) through a small, deterministic, model-free kernel.
The kernel never calls a model. It handles task state, bounded waiting, workspace locking, fixed verification, one pre-authorized repair, cancellation with confirmation, budgets, recovery, usage accounting and per-agent scoring — so the orchestrator spends its tokens on judgment, not on polling and log-shuffling.
Orchestrator (Codex / opencode / Claude Code)
| MCP (5 stable tools) or CLI (`nar`)
v
Model-free kernel: tasks, locks, verification, repair, budget, recovery
v
Pluggable native adapters
├── zcode-native (ZCode app-server protocol 0.16, verified live on 0.16.5)
├── acp-generic (Agent Client Protocol, verified live on opencode 1.18.15)
└── your adapter (small class, see docs/BACKENDS.md)More native agents are being supported…
MCP:
nar-mcpexposes exactly five tools:agents,run,wait,inspect,cancel. New backends are added by config, not by new tools.CLI:
narshares the same kernel (debugging, scripting, humans).Token discipline: compact results by default; full logs/diffs on disk, paged via
inspect; deterministic scoring per task (zero tokens) feeds routing.
Install
Requires Python >= 3.10. For the ZCode adapter: Node.js >= 22 and the ZCode
desktop app (or its CLI on PATH). For ACP agents: any ACP server binary.
Install from the official GitHub source:
git clone https://github.com/BerineYang/native-agent-router.git
cd native-agent-router
python -m pip install .If git clone is unreliable, download the official
v1.0.0 branch source ZIP,
extract it, open a terminal in the extracted directory, and run:
python -m pip install .For development, replace the install command with
python -m pip install -e .. In the repository directory, pipx install .
also works.
On Windows, verify the installation with the module form first. It works even
when the Python scripts directory is missing from PATH:
python -m native_agent_router doctorIf the scripts directory is on PATH, the shorter command is equivalent:
nar doctorThe doctor command uses [OK], [X], and [i] markers for the ZCode bundle,
Node.js, Git, and configuration checks. It never prints credentials. A missing
ZCode entry matters only when you intend to use the zcode-native adapter.
Related MCP server: kagero
Quickstart
Create the config (optional — auto-discovery works without it):
mkdir -p ~/.native-agent-router
cp config.example.json ~/.native-agent-router/agents.jsonList agents, submit a complete work unit, wait, inspect:
nar agents
nar run zcode "Implement parse_flag() in flags.py per the docstring; only modify flags.py" \
/abs/path/to/project --mode build --policy allow --scope flags.py --verify "python -m pytest -q" --wait 600
nar wait <task_id>
nar inspect <task_id> summary
nar inspect <task_id> diagnose # sanitized, shareable error reportContinue the SAME native session for follow-up work on the same module:
nar run zcode "Now handle the --verbose edge case we discussed" /abs/path/to/project \
--session-ref <previous_task_id> --mode build --policy allow --verify "python -m pytest -q"Configuration (the teaching section)
Config file lookup order: --config flag → NAR_CONFIG env →
<home>/agents.json (home = NAR_HOME env or ~/.native-agent-router) →
built-in defaults + auto-discovery.
Top-level settings
Key | Default | Meaning |
| 120 | default bounded block for |
| 3600 | cap on a single |
| 1800 | default per-turn timeout |
| 600 | timeout per verification command |
| 12000 | reserved for configuration compatibility; current result fields have their own fixed bounds |
| 4000 | default page size for |
| true | one pre-authorized targeted repair per task |
| false | verify commands run without a shell (argv / |
| false | fail-closed: refuse to run a worker in a non-git workspace so changes are always auditable |
Agents
Every task must name an agent_id explicitly. There is no global
"selected backend" that other calls could silently change.
Key | Applies to | Meaning |
| all |
|
| all | set false to hide an agent |
| acp-generic | argv that starts the ACP server, e.g. |
| acp-generic | some ACP servers want |
| both | model selection (see below) |
| acp-generic | CLI-arg model injection template, e.g. |
| zcode-native | ZCode provider id from your own |
| zcode-native | e.g. |
| both | defaults to |
| both |
|
| zcode-native | native tool-set restriction, e.g. |
| zcode-native |
|
| zcode-native |
|
| both | extra environment variables for the agent subprocess; values are stored in |
Model selection
ZCode (
zcode-native): setprovider+model(+ optionalthought_level). NAR builds ZCode 0.16'sruntimeModelpayload from your own~/.zcode/v2/config.jsonand passes it inside the protocol only. No credential is ever copied to disk by default, no provider/base URL/billing channel is changed, and coldsession/resumeworks (verified live on 0.16.5).ACP agents (
acp-generic): NAR first tries the ACP-native path —session/set_config_option/session/set_model(works with opencode, whosesession/newadvertises amodelconfig option) — then falls back tomodel_argsCLI substitution (e.g.gemini --experimental-acp --model X).
ZCode discovery (how zcode.cjs is found)
Order: ZCODE_BIN env → zcode on PATH → Windows registry uninstall entries
(InstallLocation) → %LOCALAPPDATA%\Programs\ZCode → Program Files\ZCode on
every drive. If none match, nar doctor tells you exactly what to set. The
desktop app does not add its CLI to PATH — that is normal.
Credentials, honestly
credentials: "auto" reads (read-only) the provider you already configured in
the ZCode desktop app and passes the same provider/model/base-URL/key to the
headless app-server in the protocol payload only. NAR never logs keys,
never uploads them anywhere, never switches billing channels, and never
bypasses authentication or plan limits. If you prefer a private session store,
set zcode_home: "isolated" (documents trade-offs in docs/SECURITY.md).
MCP integration
Run nar-mcp over stdio. Examples:
Codex (~/.codex/config.toml):
[mcp_servers.native-agent-router]
command = "nar-mcp"
args = []opencode (opencode.json in your project or ~/.config/opencode/):
{ "mcp": { "native-agent-router": { "type": "local", "command": ["nar-mcp"], "enabled": true } } }Claude Code (.mcp.json):
{ "mcpServers": { "native-agent-router": { "command": "nar-mcp" } } }Then ask your orchestrator, in one sentence:
"Run agents, then run this task to zcode with scope and verify, and
wait for it." See skill/native-agent-router/SKILL.md for the delegation
rules you can drop into any agent's skill folder.
The five MCP tools
Tool | Purpose |
| configured agents + capabilities + score stats (routing data) |
| submit one complete work unit (goal/scope/forbid/verify/budget/mode/session_ref) |
| bounded block until terminal/blocked; never busy-polls |
| paged on-demand reads: status/summary/diff/verify/log/raw/usage/diagnose |
| request stop and report whether it was confirmed |
run returning means submitted, not done. Terminal states are
succeeded|failed|cancelled|interrupted. blocked is observable but nonterminal:
the kernel keeps the workspace lock and watches the native session until a real
terminal event arrives (or you cancel/kill). A token budget violation uses
status failed with code budget_exceeded.
CLI reference
nar agents | doctor | list
nar run <agent_id> <goal> <workspace> [--scope ...] [--verify "cmd"] [--mode build]
[--policy allow|deny] [--session-ref TASK] [--timeout N] [--wait N]
[--budget N] [--idempotency-key K] [--no-repair]
nar wait <task_id> [--timeout N]
nar inspect <task_id> [status|summary|diff|verify|log|raw|usage|diagnose] [--offset N] [--limit N]
nar cancel <task_id>
nar kill <task_id> --yes # force: terminate agent + release lock (user boundary)
nar stats [agent_id] # deterministic per-agent score statisticsTests
pip install -e ".[dev]"
pytest -q # 67 tests, no model calls, no network
pytest -q -m real # opt-in: hits your real installed agents (spends tokens!)Docs
ARCHITECTURE.md · BACKENDS.md · VERIFY.md · BENCHMARK.md · SECURITY.md · ROLLBACK.md · Skill
Status & honesty
Verified live on this machine: ZCode 0.16.5 (read-only, native resume, edit+verify, permission handling, real usage), opencode 1.18.15 over ACP (handshake, model switch, resume). Evidence paths in docs/VERIFY.md.
Not yet verified here: Gemini CLI (not installed), Claude Code as ACP (installed but currently unusable per user), Codex as orchestrator client.
No claim of "optimal" or fixed savings percentage: see BENCHMARK.md for what was measured and what wasn't.
MIT licensed. Independent community project; not affiliated with Z.AI/ZCode, OpenAI Codex, or SST/opencode.
Available Tools
5 toolsagentsA
List configured worker agents and their capabilities (adapter type, transport, native session resume support, usage accounting quality) plus per-agent SCORE STATS (avg_score, success_rate, repair_rate, out_of_scope_rate, blocked_rate, avg_tokens, median_duration_sec, last_tasks). Use the stats to route work: high-score agents for hard units, cheaper/lower-score agents for well-specified mechanical units. Use this before run() to pick a valid agent_id.
Inputs: no named parameters. Returns: result
cancelA
Request cancellation and report whether the stop was CONFIRMED by the native runtime (confirmed=true only on an observed terminal/exit). A cancel request alone is not a stop.
Inputs: task_id (required).
inspectA
Read task details on demand (logs/diffs stay on disk; read slices here). what: status | summary | diff | verify | log | raw | usage | diagnose.
diagnose returns a compact SANITIZED report (secrets/emails/blobs redacted, size-bounded) with a stable
codeand actionablehints— safe to share.
Inputs: task_id (required), what, limit, offset.
runA
Submit a complete work unit to a worker agent and return its task_id.
Requirements:
agent_id: exact id from agents(); every task is explicitly bound to one agent.
goal: full objective, constraints and context in ONE prompt (complete work unit; do not ping-pong per file/line).
workspace: directory path (canonicalized to an absolute path by the kernel).
scope_files: RELATIVE paths the worker may modify (absolute or escaping paths are rejected; enforced by diff audit).
forbid: extra prohibitions.
verify: commands executed by the kernel after the turn; the worker cannot pass by editing them. Prefer list form; string form is arg-split (no shell) unless config verify_allow_shell is on. Exit code 0 = pass.
mode: adapter-specific permission mode (zcode: plan|build|edit|yolo).
session_ref: task_id of a previous task on the SAME agent+workspace to continue its native session. Cross-agent/workspace reuse is rejected.
timeout_sec: per-turn timeout; on timeout the task becomes blocked (never silently resubmitted; the lock is held and a watcher keeps observing).
wait_sec: bounded block until finish (default: return immediately).
budget_tokens: hard token budget; exceeded -> task failed (budget_exceeded).
permission_policy: "deny" (default) or "allow" (auto-allow within this task).
repair_allowed: one pre-authorized targeted repair attempt (default true).
idempotency_key: resubmitting the same key returns the original task.
Returns a snapshot dict with ok/status/code. A run() success only means SUBMITTED, not done — follow with wait()/inspect().
Inputs: goal (required), agent_id (required), workspace (required), mode, +10 more.
waitA
Bounded blocking wait for a task to reach a terminal or blocked state. Does not busy-poll. Returns the snapshot dict; if it still runs when the timeout expires, wait_timed_out=true — call again later. 'submitted'/'running' is NOT completion; only status succeeded/failed/blocked/cancelled/interrupted is.
Inputs: task_id (required), timeout_sec.
Tool Schema Changelog
Recent tool additions, removals, and schema changes observed during successful MCP inspections.
Loading tool schema changelog…
TDQS
Scored across 5 tools
Each tool covers a distinct lifecycle stage: agents discovers workers, run submits a unit of work, wait blocks for completion, inspect reads task details, and cancel aborts. There is no meaningful overlap or ambiguity between them.
The verb-style names run, wait, inspect, and cancel are clear and consistently formatted. 'agents' is a minor deviation since it is a noun command rather than a verb_noun action like list_agents, but the pattern is still easy to predict.
Five tools is well-scoped for the server's purpose: agent discovery, task submission, waiting, inspection, and cancellation. Each tool has a clear role and none feel redundant.
The core agent-routing and task lifecycle is well covered: discover agents, submit tasks, wait, inspect, and cancel. A minor gap is the lack of a task-listing or search tool if a task_id is lost, but this does not prevent normal workflows.
Maintenance
Related MCP Connectors
Discover and call AI agents via MCP. Supports A2A agents and platform agents with async tasks.
- aXDeprecatedio.github.ax-platform
Agent-native collaboration network: orchestrate a team of long-running agents from any MCP client.
Your org's AI agents, tasks, runs, search, and brain files as MCP tools and resources.
Public MCP for agent verification, work discovery and governed interoperability.
Related MCP Servers
- AlicenseAqualityBmaintenanceEnables Cursor's main agent to delegate development tasks to Claude CLI subagents via MCP, supporting synchronous, asynchronous, and parallel task execution with monitoring and control.7ISC
- AlicenseNot gradedqualityBmaintenanceEnables unified access to multiple coding-agent CLIs via MCP, with tools for running prompts, managing sessions, and retrieving task outputs.MIT
- AlicenseBqualityBmaintenanceEnables any MCP host to delegate work to multiple coding-agent CLIs such as Codex, Claude Code, and Antigravity as subagents, preserving native sessions and supporting team-based supervision and agent-to-agent messaging.179 npmApache 2.0
- AlicenseNot gradedqualityAmaintenanceEnables any MCP-compatible client to run reusable, Markdown-defined coding agents (such as reviewers, test writers, or investigators) through a configurable coding CLI backend, returning results into the same conversation. Agent definitions and backend/model settings can be shared across clients, and sessions can persist across multiple calls for longer work.182 npmMIT