メインコンテンツへスキップ

ACP Integration

The Agent Client Protocol is to agents what LSP is to language tooling: one JSON-RPC vocabulary so an editor and an agent speak without either knowing the other's implementation. It exists because the fragmentation it removes — every editor reimplements every agent's integration; every agent reimplements every editor's surface — is the same fragmentation LSP solved.

This guide adopts ACP as the default outward transport for any conforming agent system. Internally the core emits AI SDK v6 chunks (foundations / streaming substrate); ACP wraps the chunks for delivery to an external client. A host that does not need editor interop MAY skip the adapter; a host that does adopts it without re-shaping the core.

What ACP is

  • JSON-RPC 2.0 over stdio by default; HTTP, WebSocket, or SSE for remote agents.
  • Two roles: the client (the editor / IDE / outer host) and the agent (the AI backend). The client launches and supervises the agent as a subprocess, then drives it through JSON-RPC.
  • Capability negotiation at initialize time. Each side declares what it can do (loadSession, resumeSession, filesystem read/write, terminals, MCP transports). Neither side assumes; both check.
  • Distributed authority. The client typically owns the filesystem and the terminal; the agent owns the model loop and the conversation state. Either side MAY publish capabilities the other consumes.
  • Wire naming convention is camelCase for JSON property keys and snake_case for discriminator values — opposite this guide's internal convention. See Naming seam.

How the agent system maps to ACP

The agent system core hosts the agent side of ACP. The host process ships an ACP transport that translates between this guide's internal shapes and ACP messages.

┌─ ACP client (editor) ─────────────────────────────────────────────┐
│ initialize / session/new / session/prompt / session/cancel │
│ responds to: session/update, session/request_permission, │
│ fs/* requests, terminal/* requests │
└─────────────────────────┬─────────────────────────────────────────┘
│ JSON-RPC over stdio (default) /
│ HTTP / WebSocket / SSE (remote)

┌─ ACP adapter (in the host) ───────────────────────────────────────┐
│ - dispatches incoming JSON-RPC to core APIs │
│ - translates AI-SDK-v6 chunks → session/update notifications │
│ - translates watchdog `ask` → session/request_permission │
│ - bridges fs/terminal client-provided capabilities back to │
│ the core's runtime when negotiated │
└─────────────────────────┬─────────────────────────────────────────┘
│ internal API (snake_case)

┌─ Agent system core ───────────────────────────────────────────────┐
│ session, message, part / locked tools / loop / runtime │
└───────────────────────────────────────────────────────────────────┘

Method mapping

ACP methodGuide equivalentNotes
initializeCapability handshakeAdapter declares which capabilities the core supports.
authenticateProvider credential resolutionAdapter forwards to the host's credential store. Out of guide scope.
session/newCreate a chat_sessions rowAdapter creates the row, returns its id as ACP's sessionId.
session/loadReplay messages as session/update notificationsThe adapter walks DB rows and re-emits each as the corresponding ACP update type.
session/resumeAttach to a session without replayMaps to opening an existing session row; the chat panel hydrates from DB on its own.
session/closeMark the adapter handle inactive; the session row staysThe DB row persists; only the in-memory handle goes.
session/promptA new user message + a fresh turn through the loopBody parts (text, resource) map to compositor parts. See compositor.
session/cancelInternal abort(session_id)Adapter calls the core's abort; the run-state machine handles the rest.
session/set_modePlan/build agent swap (opinionated pattern)The host MAY route ACP modes onto the per-message agent override; see subagents / plan-build. If the host has no mode concept, advertise no modes at initialize and never receive this call.
session/request_permissionWatchdog askAdapter wraps the watchdog hook; user reply lands as a session-scoped permission rule.
session/update (notification)AI SDK v6 chunksAdapter translates per-chunk; see Update mapping.
fs/read_text_file (agent → client)Locked tool read when fs delegation is negotiatedClient-provided filesystem capability; the runtime calls back into the client.
fs/write_text_file (agent → client)Locked tool write when fs delegation is negotiatedSame.
terminal/* (agent → client)Locked tool bash when terminal delegation is negotiatedClient owns the shell; agent drives it via the terminal/* methods.

Update mapping

ACP's session/update notification is a discriminated union over a sessionUpdate field. The adapter translates AI SDK v6 chunks 1:1 where possible:

AI SDK v6 chunk typeACP sessionUpdateNotes
text-start / text-delta / text-endagent_message_chunkEach delta becomes one ACP update with the partial text content block.
reasoning-start / reasoning-delta / reasoning-endagent_thought_chunk (where supported)When the client cannot render thought, the adapter drops or coalesces.
tool-input-start / tool-input-delta / tool-input-availabletool_callFirst chunk emits the tool_call with status: "pending", then in_progress.
tool-output-availabletool_call_update (status completed)Carries the tool's output content blocks.
tool-output-errortool_call_update (status failed)Carries the error text in a content block.
file / source-url / source-documentcontent blocks inside agent_message_chunkMapped per ACP's content block taxonomy.
finish-step / finishterminates the session/prompt reply with stopReasonMapped: end_turn, max_tokens, cancelled, refusal.

The adapter is a translator, not a buffer. It does not introduce its own state — every update it emits is sourced from one chunk or a finalize event the core already produced.

Tool kind mapping

ACP tags every tool call with a kind from a small enum (read, edit, delete, move, search, execute, think, fetch, other). The kind drives client UI — icon, inline diff renderer, terminal pane.

Locked toolACP kind
readread
writeedit
editedit
globsearch
grepsearch
bashexecute
todoother
taskother
questionother
web_searchfetch
web_fetchfetch
skillthink
tool_searchsearch
MCP toolskind from the MCP server's declaration; default other

The kind taxonomy is small on purpose. Implementors that want richer UI hints stash them in ACP's _meta field; clients that understand the host's meta render fancier.

Capability matrix

The adapter advertises these on initialize:

ACP capabilityRequired when
loadSessionPersistence is on (default).
sessionCapabilities.resumeThe implementor ships an in-flight stream registry. Required for renderer-disconnect resume.
sessionCapabilities.closeAlways.
mcpCapabilities.httpThe core's MCP layer supports remote HTTP MCP servers.
mcpCapabilities.sseThe core's MCP layer supports remote SSE MCP servers.
promptCapabilities.imageThe provider supports multi-modal image input.
promptCapabilities.audioAudio attachments supported.
promptCapabilities.resource_linkAlways; file references are core to this guide.

The client advertises in return:

Client capabilityWhat it means for the adapter
fs.readTextFileThe client provides fs/read_text_file. The core's read tool MAY delegate to it.
fs.writeTextFileSame for write / edit.
terminalThe client provides terminal/*. The bash tool MAY delegate.

Delegation is opt-in per host. A daemon-mode host with its own sandboxed filesystem ignores the client's offers. An editor-embedded host delegates so the editor's write-tracking and approval UI is the single source of truth. Both modes are conformant.

Where ACP and the guide diverge

Three seams where the protocol's shape and the guide's shape do not line up 1:1.

Filesystem authority

The guide assumes the agent runtime owns the filesystem capability (foundations / locked tools). ACP allows the client to own it instead. The adapter handles either:

  • Host-owned fs (default): core's read / write / edit execute locally. The adapter emits tool_call / tool_call_update notifications so the client can render progress, but does not call fs/read_text_file / fs/write_text_file.
  • Client-delegated fs: the adapter routes core's read / write / edit through fs/read_text_file / fs/write_text_file. The watchdog still runs; capability scopes still apply. The client's response becomes the tool's output.

The decision is per-session at initialize based on the client's declared capabilities.

Permission UX

ACP's session/request_permission is synchronous — the agent waits for the client's response with a chosen optionId. The guide's watchdog is more general: a config policy with no human in the loop. The adapter bridges:

  • A watchdog ask is emitted as session/request_permission with the standard option kinds (allow_once, allow_always, reject_once, reject_always).
  • A watchdog allow / deny outcome is emitted as nothing — the client never sees the policy decision.
  • The client's reply is recorded as a session-scoped permission rule if allow_always / reject_always (see session / permission scopes).

Stream resume semantics

ACP's session/load replays the conversation history as session/update notifications before returning. The guide's resume endpoint (see session / resume across renderer disconnect) replays the live in-flight chunk log of an in-flight run. These are different operations:

  • session/load: rehydrate a finished session by re-emitting every past message as an update. The adapter walks the DB rows.
  • session/resume: rejoin a session without replay. The adapter hands the client whatever the in-memory registry has — the in-flight chunk log if a run is happening, nothing if it isn't.

The guide's resume and ACP's resume agree on shape (no replay); ACP's load adds the past-replay path.

Naming seam

Two conventions to reconcile:

  • This guide: snake_case fields, snake_case function names, PascalCase type names, kebab-case for path variables.
  • ACP wire: camelCase for JSON property keys (sessionId, toolCallId, optionId), snake_case for discriminator values (agent_message_chunk, tool_call_update).

The adapter owns the translation. Field rename at the wire boundary:

internal (guide) ACP wire
───────────────────── ─────────────────
session_id ↔ sessionId
tool_call_id ↔ toolCallId
provider_id ↔ providerId
model_id ↔ modelId
parent_message_id ↔ parentMessageId
started_at ↔ startedAt
error_text ↔ errorText

The adapter MUST NOT leak snake_case keys to the wire; the core MUST NOT receive camelCase keys. One rename table, applied at the seam, keeps both sides clean.

What ACP does not cover

The protocol is intentionally narrower than this guide. Where ACP is silent and the guide speaks:

  • Compaction. ACP has no notion of summarizing history. The agent does it internally; the client sees the effect on the next turn's output. See session / compaction.
  • Skills and project instructions. ACP does not standardize system-prompt assembly. Skills, AGENTS.md, memory — all internal to the agent. See skills.
  • Subagents. ACP does not define recursive agent-to-agent calls. The task tool runs subagents inside the host; the client sees one tool_call per spawn. Inspectable subagents (subagents / inspectability) MAY be modeled as separate sessions exposed via ACP — the adapter chooses.
  • Per-role token tracking and cost. ACP's recently-stabilized Session Usage and Context Status RFD adds usage; pricing remains out of scope on both sides.
  • Memory. Out of scope; built on top (ux / memory).
  • Watchdog policy. ACP has the wire (session/request_permission) but no policy model. The guide's watchdog hook is the policy seat; the adapter wires it.

These are not gaps in ACP — they are concerns ACP leaves to the agent. The guide fills them in.

What ACP adds for the guide

  • One adapter, many editors. Wire an ACP transport once and any ACP-compliant client (Zed, IDE plugins, third-party tools) can drive the agent.
  • Inspection / debug clients for free. An ACP-aware test client can record JSON-RPC traffic — a complementary format to the canonical inspection format (debugging).
  • Cross-host portability. A code agent that ships as an ACP server is usable from any host that speaks ACP, not just the host it shipped with.
  • A standard for delegated filesystem. ACP's fs/* and terminal/* give the guide a clean answer for "what if the editor wants to own the filesystem?"

Anti-goals

  • ACP is not the only outward transport. A host that ships a CLI or a daemon MAY not need ACP at all — the AI SDK v6 chunk stream over the host's own transport (HTTP+SSE, IPC, in-memory) is fully conformant.
  • ACP is not a replacement for AI SDK chunks internally. The core's stream shape stays AI SDK v6 (foundations). ACP wraps; it does not replace.
  • Adopting ACP does not adopt all of its RFD surface. Adopt the stable methods listed above; RFD-in-flight pieces (fork, multi-workspace roots, telemetry) land when both ACP and this guide stabilize the corresponding mechanism.

See also