6.2

serve mcp and serve acp

serve mcp exposes each agent as one MCP tool over stdio or streaming HTTP on port 8081, and serve acp speaks the Agent Client Protocol to an editor over stdio.

Your MCP client already calls tools, and your editor already talks to coding agents. Neither of them reads an agent file. serve mcp turns the agent into a tool for the first, and serve acp turns it into an editor agent for the second.

When you finish this section, you can serve an agent as an MCP tool, read its schema, and open an ACP session from an editor.

serve mcp: one agent, one tool

serve mcp: starts an MCP server that serves the agent over the Model Context Protocol, with stdio transport by default. --http switches to streaming HTTP on 127.0.0.1:8081. Without -a, every agent of a team file becomes its own tool, and --tool-name renames the tool when one agent is exposed.

--attach exposes the session of a running TUI, found by pid, address, or session id. --mcp-keepalive works over stdio only, while --auth-token, --insecure-no-auth, and --safety work with --http only. Over HTTP the safety policy defaults to restricted.

The capture ran serve mcp fixtures/agents/pong.yaml --http --listen 127.0.0.1:8081 --tool-name pong, with the file of serve api and serve chat:

initialize, tools/list, and tools/call against /mcphttpFull log
POST /mcp HTTP/1.1
…
  "method": "initialize",
  "params": {
    "protocolVersion": "2026-07-28",
…
Content-Type: text/event-stream
…
event: message
data: {"jsonrpc":"2.0","id":1,"result":{"capabilities":{"logging":{},"tools":{"listChanged":true}},"protocolVersion":"2025-11-25","serverInfo":{"name":"docker agent","version":"v1.149.0"}}}
…
data: {"jsonrpc":"2.0","id":2,"result":{"ttlMs":0,"cacheScope":"public","tools":[{"annotations":{"destructiveHint":false,"idempotentHint":true,"openWorldHint":false,"readOnlyHint":true,…"inputSchema":{…"properties":{"message":{"description":"the message to send to the agent","type":"string"}},"required":["message"],"type":"object"},"name":"pong","outputSchema":{…
…
data: {"jsonrpc":"2.0","id":3,"result":{"content":[{"type":"text","text":"{\"response\":\"pong\"}"}],"structuredContent":{"response":"pong"}}}
Headers, the client info, and the notifications/initialized exchange are cut. The tools/list line is cut inside the tool definition.

The client asked for revision 2026-07-28, which v1.129.0 added together with a stateless Streamable HTTP transport. The server answered 2025-11-25, so read the version from the result, never from your request. No response carried an Mcp-Session-Id header. Each answer came as one SSE message event, and the notification got 202 Accepted (the capture).

The tool takes one string, message, and returns structuredContent.response, with the same text as a JSON string in content. Its annotations mark it read-only and idempotent, and its title is the agent's description. The tools/call answer came as one event after the whole turn, with no progress notification before it. The kit README adds that the server answered on / as well as /mcp (the capture). The docs register the stdio form in Claude Code with claude mcp add --transport stdio, followed by -- docker agent serve mcp and the agent reference.

serve acp: JSON-RPC lines to an editor

serve acp: a server that talks over stdio, the standard input and output. The editor spawns docker-agent serve acp FILE, writes JSON-RPC requests to its stdin, and reads responses and notifications from its stdout. Sessions persist in <data-dir>/session.db unless -s names another file. A team file works as well, and the docs say its sub-agents need no change for ACP.

initialize and session/new over stdiojsonlFull log
>> {"jsonrpc": "2.0", "id": 1, "method": "initialize", "params": {"protocolVersion": 1, …}}
<< {"id": 1, "jsonrpc": "2.0", "result": {"agentCapabilities": {"auth": {"logout": {}}, "loadSession": true, …, "agentInfo": {"name": "docker agent", "title": "docker agent", "version": "v1.149.0"}, …, "protocolVersion": 1}}
>> {"jsonrpc": "2.0", "id": 2, "method": "session/new", "params": {"cwd": "$CAPTURE", "mcpServers": []}}
<< {"jsonrpc": "2.0", "method": "session/update", "params": {"sessionId": "<uuid>", "update": {"availableCommands": [{"description": "Summarize and compact session history", …, "name": "compact"}, {"description": "Display current context token usage and session cost", "name": "usage"}], "sessionUpdate": "available_commands_update"}}}
<< {"id": 2, "jsonrpc": "2.0", "result": {"configOptions": [{"category": "mode", "currentValue": "default", …}], "modes": {…, "currentModeId": "default"}, "sessionId": "<uuid>"}}
Lines marked >> were written to stdin, lines marked << were read from stdout. Each object is cut inside, at each mark.

initialize returns agentCapabilities: loadSession, MCP servers over HTTP and SSE, audio, image, and embedded-context prompts, and the session operations close, delete, list, and resume. It offers one auth method, host-credentials, which v1.144.0 added together with session deletion. Before the session/new result, a session/update notification lists the slash commands. Release v1.143.0 announced /compact, /usage, and /new, and the captured list has no new.

The session starts in mode default, which auto-approves read-only tools and asks for the rest. The mode list adds default to the four values of --safety. A client changes the mode with session/set_config_option or the older session/set_mode. The capture stops at session/new, so it shows no prompt turn over ACP. The docs sketch the host side with a method agent/run and call that code pseudocode. Use the method names of the capture.

Fig. 6.2one agent file served over ACP and over MCPflow
One agent file served over ACP and over MCP Top band: an editor spawns docker-agent serve acp pong.yaml and writes JSON-RPC lines to its stdin. initialize returns agentCapabilities and agentInfo docker agent v1.149.0 at protocolVersion 1. session/new first sends a session/update notification with the slash commands compact and usage, then the result with configOptions and the mode default. Bottom band: curl posts to serve mcp --http at 127.0.0.1:8081/mcp. initialize asks for 2026-07-28 and gets 2025-11-25, notifications/initialized gets 202 Accepted, tools/list returns one tool named pong with one message argument, and tools/call returns structuredContent.response pong. Both servers run the agent root of pong.yaml on dmr/ai/qwen3:4b. ACP OVER STDIO editor ACP client serve acp stdin and stdout stdio >> initialize, protocolVersion 1 << agentCapabilities, agentInfo docker agent >> session/new, cwd, mcpServers [] << session/update: commands compact, usage << result: configOptions, currentModeId default MCP OVER STREAMING HTTP curl MCP client serve mcp --http 127.0.0.1:8081/mcp HTTP POST >> initialize, asks 2026-07-28 << protocolVersion 2025-11-25 >> notifications/initialized, 202 Accepted >> tools/list << one tool pong(message) >> tools/call << structuredContent.response pong.yaml agent root dmr/ai/qwen3:4b temperature 0
The same pong.yaml answers an editor through stdin and stdout and an MCP client through HTTP POSTs to port 8081, with different method names on each path. Read each band left to right. The boxes under each band list the captured messages in order, with >> for a request and << for a response or notification.

For the protocol under serve mcp, read the MCP transports lesson.

Docker Sandboxes (sbx) and Docker Agent (docker-agent) sbx 0.47.0, docker-agent 1.149.0 · bf4169c · 2026-10-07 · Edition 2026.10