Skip to main content

symm-mcp

Symm is a lightweight asynchronous MCP channel for handing work between independent agents and re-entering it with fresh attention.

The work persists. The viewpoint changes.

Any MCP client can dispatch a task to another agent, keep working, and later observe the result and record a resolution. Symm does not decide who implements and who reviews; that is expressed in the prompt.

Symm sits between two protocols: toward its caller it is an MCP server, and toward the agents it launches it is an Agent Client Protocol (ACP) client.

+-----------+      +----------------------+      +--------------+
| Dispatch  | ---> | Observe              | ---> | Resolve      |
|spawn_task |      |get_events / get_task |      |resolve_task  |
+-----------+      +----------------------+      +-------+------+
       ^                                                 |
       |        Repeat: next task / new viewpoint        |
       +-------------------------------------------------+

Status

0.1.3, the latest release (changelog), published on PyPI as symm-mcp. The package is classified as Alpha.

How a Task Flows

spawn_task returns as soon as the agent process is running. ACP session setup and the prompt continue after that; the caller can do other work and later read only events it has not seen yet.

Caller                     Symm server                Agent
  | spawn_task(...)              |                       |
  |----------------------------->| start process group   |
  |<-- task_id, status=running --|                       |
  |                              | initialize, new or    |
  |                              | load session, settings|
  |                              |---- prompt over ACP ->|
  |                              |<- updates/permission -|
  | wait_tasks([task_id])        |                       |
  |----------------------------->|                       |
  |                              |<-- turn ends ---------|
  |<-- completed, terminal ------|                       |
  | get_events(after_seq=0)      |                       |
  |----------------------------->|                       |
  |<-- page, cursor, has_more ---|                       |
  | get_events(after_seq=cursor) |  ... until has_more   |
  |----------------------------->|      is false         |
  | resolve_task(...)            |                       |
  |----------------------------->|                       |
  |<-- task with resolution -----|                       |

The next task may swap the agent and caller roles.

Two Independent Axes

Execution status says what happened to the process. Resolution says what a caller concluded about the result. They never change each other: succeeded does not mean accepted, and a resolution can be revised later.

Execution status (owned by Symm)

created --> starting --> running --success--> succeeded
  |           |             |-- failure --> failed
  |           |             +-- cancel/shutdown --> cancelled*
  |           +-- start failure -----------------> failed
  |           +----------------------------------> cancelled
  +----------------------------------------------> cancelled

* If process-group cleanup cannot be confirmed, the task ends as failed.

Resolution (recorded by callers, finished tasks only)

unresolved --resolve_task--> judged
judged --resolve_task--> judged
judged = accepted | rejected | needs_followup | superseded

For a clean completion, succeeded means exit code 0 (agy) or stop reason end_turn (ACP agents).

Symmetric by Design

There is no built-in implementer or reviewer role. The same seven tools cover every direction work can travel:

Implement, then verify yourself
  You -- implement X --> Agent
  You <-- result ------ Agent

Implement yourself, ask for review
  You -- review workspace --> Agent
  You <-- findings ------- Agent

Chain or fan out
  Agent A -- implement --> Agent B -- find flaws --> Agent C
      |-- review 1 --> Reviewer 1
      +-- review 2 --> Reviewer 2

initiator_id and executor_id are descriptive labels only. They never grant ownership or authority.

Tools

Tool What it does
spawn_task Dispatch a prompt to a registered agent. Returns as soon as the agent process is running.
get_task Current state: execution status, exit_code, session_id, resolution, timestamps.
get_events One bounded page of events after after_seq; returns cursor, has_more, status.
wait_tasks Wait up to 30 seconds for any given task to end; returns summaries and a reason.
list_tasks This server's tasks, newest first, as summaries; recovers task ids a caller lost.
resolve_task Record accepted, rejected, needs_followup, or superseded for a finished task. No side effects; may be revised.
cancel_task Stop a running agent's process group; failed cleanup ends the task as failed. A finished task is unchanged.

Registered agents. Symm is the ACP client for six agents. The Claude Code and Codex adapters launch the user-installed claude-agent-acp and codex-acp executables. Copilot, Cline, Cursor, and opencode are launched through their ACP commands. Antigravity has no ACP mode and runs as a CLI.

model and effort reach each agent through the interface it accepts. Claude Code requests model and effort as ACP session settings. Codex requests model and maps effort to the ACP setting reasoning_effort. Symm checks ACP settings against values advertised by the agent before sending the prompt. Cursor and opencode accept model only, as the ACP setting model. Cline takes model as an ACP session setting and passes effort as --thinking=<effort>. Copilot takes model and effort as joined launch flags (--model=<model> and --reasoning-effort=<effort>), which Copilot checks. Antigravity passes model as --model <model> and effort as --effort=<effort>.

Agent Executable Symm launches options
claude_code claude-agent-acp allow, model, effort
codex codex-acp allow, model, effort
copilot copilot --acp allow, model, effort
cline cline --acp allow, model, effort
cursor cursor-agent acp allow, model
opencode opencode acp allow, model
antigravity agy --print=<prompt> model, effort, mode, sandbox, dangerously_skip_permissions, print_timeout

Permissions

Symm does not widen an agent's permissions on its own.

  • ACP agents ask Symm, and Symm answers. The allow option lists ACP tool kinds approved for this task: read, edit, delete, move, search, execute, think, fetch, and other. For an allowed kind, Symm chooses a one-time allow option; otherwise it chooses a one-time reject option. It never chooses an option that applies always. Mode-switch requests are never granted. If the one-time option it needs is not offered, Symm cancels the request. A decision Symm cannot record is cancelled, and the task fails. Each prompt-turn decision is recorded as a permission event. A request outside the prompt turn is cancelled without an event.
  • Permission settings are pinned. Before the prompt, Symm sets Claude Code mode=default, Codex mode=read-only, Copilot allow_all=off, and Cline auto_approve=false. Cursor is pinned to mode=agent only when allow contains edit; otherwise its mode is plan. The opencode adapter is pinned to mode=build and configured to ask before edits, shell commands, and web fetches. Callers cannot set these pinned settings. If an ACP agent does not offer a setting or value Symm must pin, the task fails before the prompt and records an error event naming it. The opencode adapter's ask rules are supplied through OPENCODE_CONFIG_CONTENT: Symm preserves the user's other inline configuration, overrides those permission entries, and rejects a value that is not a JSON object.
  • Cursor has a mode-specific limitation. In agent mode Cursor creates and edits files without asking, so Symm selects it only when allow includes edit. Cursor labels file deletions as edit, so allowing edit also allows deletion. In plan mode Cursor writes no files and runs no commands, so an execute grant cannot be used without edit.
  • Antigravity has no ACP permission exchange. Symm cannot pin its settings and adds no permission flag by default, so its headless default applies and unapproved actions are denied. effort accepts low, medium, high, xhigh, or max and is passed as --effort=<effort>. A caller can set mode to accept-edits or plan, enable terminal restrictions with sandbox=true, or set dangerously_skip_permissions=true to auto-approve every tool request. print_timeout accepts a positive integer followed by s, m, or h; its default is five minutes. Its prompt is one --print=<prompt> argument, limited to 100,000 bytes and rejected if it contains a NUL character.
  • Launch-only choices are checked by the agent. Copilot effort accepts none, minimal, low, medium, high, xhigh, or max. Cline effort accepts none, low, medium, high, or xhigh. Launch-option model values must be safe tokens that cannot be read as options. The CLI checks launch-only choices; a value it rejects fails the task. ACP model and effort settings are checked against the agent's advertised values before the prompt. Boolean options accept only JSON true or false.

Events

Every created task records task_created; each status transition is a status_changed event. When the agent process starts, Symm records process_started; after supervision finishes, it records process_exited. A start failure records error without a process start or exit. Depending on what happens, a task can also record task_cancelled and task_resolved. The full event type set is: task_created, process_started, stdout, stderr, status_changed, process_exited, task_resolved, task_cancelled, error, session_started, agent_message, agent_thought, tool_call, tool_call_update, plan, permission, and agent_update.

For ACP agents, process_exited also carries the turn's stop_reason; only end_turn counts as succeeded.

For ACP agents, session_started records the agent session id, stores it in the task's session_id, and makes it available to spawn_task for a later resume. All six ACP adapters support resume; Antigravity does not. A resumed task starts a new agent process and loads the existing session in the same workspace. It records only updates from the new prompt turn, not history replayed while loading. It uses only the model and effort choices supplied to that spawn_task call; Symm does not reapply choices from an earlier task. An agent that cannot load sessions fails before the prompt. If it rejects or does not recognize an id, the error event names the id and says it may not exist for that agent or workspace.

Each ACP request before the prompt (initialize, new or load session, and setting changes) has a 120-second timeout. If the agent does not answer, the task fails before the prompt and the error event names the unanswered request, such as load_session. An invalid or unavailable model/effort session setting fails before the prompt and records an error naming the setting; it also names the value when the agent offers that setting but not that value.

ACP message and thought chunks are coalesced and recorded at least once per second. tool_call and tool_call_update omit content blocks; raw output whose JSON encoding exceeds 4,000 characters is truncated to a preview. CLI stdout and stderr are recorded as output events. ACP stdout carries the protocol and is not recorded as output; ACP stderr lines are recorded.

Invalid requests and lookup or transition failures are MCP tool errors whose text has the form <code>: <message>. Codes are unknown_agent, invalid_request, task_not_found, and invalid_transition. Failed starts return a failed task with an error event. ACP session and supervision failures may also record error; a CLI's nonzero exit is shown by its process_exited event and failed status.

Use

Requires uv, plus the CLI of each agent you use on PATH (agy, claude-agent-acp, cline, codex-acp, copilot, cursor-agent, opencode). Symm does not install, update, or document the installation of agents: install and authenticate each one following its vendor's instructions (ADR 0005). Symm runs the first match for each executable name in the absolute entries of the server's own PATH; which copy and version that is, is up to you.

Register Symm with an MCP client, for example Claude Code:

claude mcp add symm -- uvx symm-mcp@0.1.3

Or in a client's JSON configuration:

{
  "mcpServers": {
    "symm": {
      "command": "uvx",
      "args": ["symm-mcp@0.1.3"]
    }
  }
}

A typical loop, as the calling agent sees it:

  1. spawn_task(agent="claude_code", prompt="Review the authentication changes in this workspace and list concrete flaws.", workspace="/path/to/repo") returns a task_id while the reviewer starts working.
  2. Continue with other work.
  3. wait_tasks([task_id]) until the task is in completed (each call waits at most 30 seconds). Then read its events with get_events(task_id, after_seq=<cursor>), passing the returned cursor back while has_more is true; event_types narrows what is returned.
  4. resolve_task(task_id, resolution="needs_followup", note="two findings to fix"), then dispatch the next task, possibly with the reviewer and implementer roles swapped.

Agents run in the given workspace with the server's environment. Pass workspace explicitly: the default is the server's working directory, which your MCP client chose and may be your home directory. Symm does not copy, isolate, or clean workspaces.

Architecture

MCP client
   | stdio
   v
+------------------------ symm-mcp process -------------------------+
| MCP server (seven tools, errors, signals)                         |
|    |                                                              |
|    v                                                              |
| TaskService (orchestration, per-task locks)                       |
|    +--> Task store (JSON strings in memory)                       |
|    +--> Agent registry (adapter option allowlists)                |
|    +--> TaskSupervisor (process groups, argv without a shell)     |
|              |                                                    |
|              +-- launches agy and ACP agents in shared workspace  |
|              +-- reads agy stdout/stderr and ACP stderr, exits    |
|              +-- runs ACP session client for ACP agents           |
|                    (sessions, settings, permissions)              |
|                         |                     ^                   |
+-------------------------|---------------------|-------------------+
                          | ACP over stdio      | session updates,
                          v                     | permission requests
                 ACP agent processes (own process groups)

Logical state (tasks, events, resolutions) lives in the store and is always serializable. Runtime state (processes, pipes, process groups) lives only in the supervisor. The MCP layer stays a thin transport. These boundaries are part of the project contract in PROJECT.md.

v0.1 Scope and Limitations

  • stdio only, process-local task ledger. Each MCP client launches its own Symm server, and each server sees only the tasks it dispatched. stdio + in-memory = process-local task ledger.

    Agent A
       | stdio
       v
    Symm for A [ledger: task 1]
       | task 1
       v
    Agent B
       | stdio
       v
    Symm for B [ledger: task 2]
       | task 2
       v
    Agent C
    

    Agent A sees task 1 but not task 2, which B dispatched through its own server. A shared ledger needs an HTTP gateway with a shared store, which is deferred.

  • In memory only, by design. Task state is lost when the server process exits, and Symm does not persist it. For ACP agents, the agent keeps its own session; to continue later, even from a new client or server process, keep the task's session_id and pass it to spawn_task. spawn_task usually returns before an ACP session exists, so read session_id with get_task or from the session_started event. Resume with the same agent and workspace. The ledger and earlier task's events do not resume with it.

  • No total output cap or retention policy. Stdout and stderr lines longer than 1 MiB are split into pieces no larger than 1 MiB. There is no cap on total output; output remains in the process-local ledger, so long, high-output tasks grow server memory.

  • Tools only. No MCP resources or subscriptions yet. Wait for completion with wait_tasks, which returns within 30 seconds; read progress with get_events and its cursor.

  • Bounded process cleanup. Before reporting completion, Symm tries to end every remaining member of the agent's process group. When canceling a running ACP task, it first asks the agent to cancel its prompt turn, then uses SIGTERM and, if needed, SIGKILL with bounded waits. If Symm cannot confirm the group is empty, the task ends as failed and the supervisor stops managing it; a process can remain alive. On client disconnect or handled SIGTERM, SIGINT, or SIGHUP, the server applies the same bounded cleanup before it exits. A task requested after shutdown begins fails rather than starting. A server killed with SIGKILL or one that crashes cannot run its shutdown handling, so its agents may survive.

  • Your environment, your permissions. Symm does not install agent software or widen permissions by itself. Dependencies, secrets, sandboxing, and workspace cleanliness are the caller's responsibility; callers can request only the permission changes in adapter options.

  • POSIX only. Process groups and signals; Windows is not supported.

Rationale: ADR 0003 and ADR 0007. Security assumptions and how to report a vulnerability: SECURITY.md.

Run Locally

From a checkout:

uv run symm-mcp

The command speaks MCP over stdio, so it is meant to be launched by an MCP client rather than used interactively.

Development

This project uses OpenSpec; read AGENTS.md before changing anything. The Definition of Done:

uv sync
uv run python -m compileall -q src tests
uv run python -m pytest
uv run ruff check .
uv run ruff format --check .
./scripts/changelog-guard.sh

License

Licensed under either of Apache-2.0 or MIT, at your option.

Metadata

Release files for symm-mcp 0.1.3

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for symm-mcp 0.1.3
File Size Uploaded
symm_mcp-0.1.3.tar.gz 53.4 kB Details

Built distribution (wheel)

Table of built distributions (wheels) for symm-mcp 0.1.3
File Interpreter ABI Platform
symm_mcp-0.1.3-py3-none-any.whl Python 3 none any Details

Total release size: 113.8 kB

Release files / symm_mcp-0.1.3.tar.gz

Download URL symm_mcp-0.1.3.tar.gz
Size 53.4 kB
Tags Source
SHA-256 checksum
How to use checksums
343fca2b69323cd33ac50ccf93571281104bc3428b840a06e75972bc5ec1a3ef
BLAKE2b-256 checksum
How to use checksums
7e9bca812505e913ade3fa470fece6f4b5abb0d120fe63b7a7b25e8572002dcd
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.14

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Oct 8, 2026.

Transparency log

Release files / symm_mcp-0.1.3-py3-none-any.whl

Download URL symm_mcp-0.1.3-py3-none-any.whl
Size 60.4 kB
Tags Python 3
SHA-256 checksum
How to use checksums
b81275db9b6e01185b1598c0520168a0d91f8294b265cb94f3898e460c5e5143
BLAKE2b-256 checksum
How to use checksums
e65a30c9991808b8067ba92d88331cc00d9176dc2ed8997011e84044153b441a
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.14

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Oct 8, 2026.

Transparency log

Release history Release notifications | RSS feed

This release

0.1.3 This release

2 release files

0.1.2

2 release files

0.1.1

2 release files

0.1.0

2 release files

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page