Every AI coding agent has two conversations going on at once. One faces you: the editor where you type a request, watch the agent think, review its diffs and say yes or no. The other faces the world: the files, databases, issue trackers and APIs the agent needs to get the work done.
Two open protocols now standardise those two conversations:
- ACP, the Agent Client Protocol, standardises how an app you work in (usually a code editor) talks to an agent.
- MCP, the Model Context Protocol, standardises how an agent talks to tools and data.
They are often described as rivals. They are not. They sit on opposite sides of the agent, and most serious setups will use both. This post explains ACP from the ground up, shows what actually goes over the wire, compares it with MCP point by point, and walks through the situations where each one earns its place.
The problem ACP solves
Before ACP, every pairing of editor and agent was a custom integration. If an agent wanted to live inside VS Code, JetBrains IDEs, Zed, Neovim and Emacs, it needed five plugins. If an editor wanted to offer five agents, it needed five more. Four editors and four agents meant sixteen integrations, each with its own bugs and its own idea of what a “diff” or a “permission prompt” looks like.
If that sounds familiar, it is the same problem the Language Server Protocol solved for code intelligence in 2016. Before LSP, every editor wrote its own Go support, its own Rust support and so on. After LSP, a language team writes one language server and every editor gets it. ACP applies that idea to coding agents: an agent that speaks ACP works in any editor that speaks ACP, and an editor that speaks ACP can host any agent.
Where each protocol sits
The simplest way to keep the two apart is to look at who is on each end of the line.
- In ACP, the client is the app you use (Zed, a JetBrains IDE, Neovim, an Obsidian plugin, a desktop app) and the agent is the AI program doing the work (Claude Agent, Codex CLI, Gemini CLI, GitHub Copilot CLI, Goose, Cursor’s agent, and many more).
- In MCP, the client is the agent (or its host) and the server is something that offers tools, resources or prompts: a GitHub server, a Postgres server, a browser, your company’s internal API.
Notice that the agent is the server in one protocol and the client in the other. That is the whole relationship in one sentence.
How ACP works
ACP is deliberately boring technology, which is a compliment.
- JSON-RPC 2.0, the same message format used by LSP and MCP. Every message is a request (expects a reply), a response, or a notification (fire and forget).
- stdio transport. The editor starts the agent as a child process and they talk over standard input and output, one JSON message per line. Logs go to standard error. No ports, no network listener, no extra service to secure.
- Long-lived sessions. One connection can hold several conversations at once, each with its own history and working directory.
- Both sides make calls. The editor sends prompts to the agent. The agent calls back to the editor to ask permission, read and write files, and run terminal commands.
- Markdown for text, and MCP’s JSON shapes for content wherever they fit, so integrators do not learn a second vocabulary.
One prompt, start to finish
Here is a complete exchange: the editor starts an agent, opens a session and asks for a change that needs a file edit.
1. Handshake. The editor sends initialize with the protocol version and what it can do. The agent answers with its own capabilities and any sign-in methods.
{"jsonrpc":"2.0","id":0,"method":"initialize","params":{
"protocolVersion":1,
"clientCapabilities":{"fs":{"readTextFile":true,"writeTextFile":true},"terminal":true}}}
2. Session. The editor calls session/new with the project folder and, importantly, the MCP servers the user has configured. The agent connects to those servers itself and returns a session ID.
{"jsonrpc":"2.0","id":1,"method":"session/new","params":{
"cwd":"/home/udara/code/shop",
"mcpServers":[{"name":"postgres","command":"/usr/local/bin/pg-mcp","args":["--stdio"],"env":[]}]}}
3. Prompt. The user’s message goes out as session/prompt. It can carry text, images and whole files as embedded context.
4. Streaming updates. While it works, the agent sends session/update notifications: chunks of its reply, its reasoning, a plan with steps, tool calls with their status, and diffs. The editor renders them live.
{"jsonrpc":"2.0","method":"session/update","params":{"sessionId":"sess_7f3a",
"update":{"sessionUpdate":"tool_call","toolCallId":"call_1",
"title":"Edit cart.go","kind":"edit","status":"pending"}}}
5. Permission. Before doing something risky, the agent calls session/request_permission with options such as Allow once or Reject. The editor shows a prompt and returns the user’s choice.
6. Editor-side actions. With permission, the agent asks the editor to write the file (fs/write_text_file) or run a command (terminal/create). The editor does it, so unsaved buffers, undo history and the terminal panel all stay consistent.
7. Done. The original session/prompt request finally gets its response with a stopReason: end_turn, max_tokens, max_turn_requests, refusal or cancelled. The user can cancel at any moment with a session/cancel notification.
The method map
| Direction | Method | Purpose |
|---|---|---|
| Editor → agent | initialize, authenticate | Agree the version and capabilities, sign in |
| Editor → agent | session/new, session/load, session/resume | Start, replay or reattach to a conversation |
| Editor → agent | session/prompt, session/cancel | Send a message, stop the current work |
| Editor → agent | session/set_mode, session/list | Switch modes (for example ask or code), list past sessions |
| Agent → editor | session/update | Stream text, thoughts, plans, tool calls, diffs, usage |
| Agent → editor | session/request_permission | Ask the user before acting |
| Agent → editor | fs/read_text_file, fs/write_text_file | Read and write through the editor |
| Agent → editor | terminal/create, terminal/output, terminal/kill … | Run commands in an editor-managed terminal |
| Agent → editor | elicitation/create | Ask the user a structured question |
The required core is small: an agent must handle initialize, session/new and session/prompt. Everything else is negotiated through capabilities, so a minimal agent and a feature-rich editor still work together.
ACP and MCP, side by side
| ACP (Agent Client Protocol) | MCP (Model Context Protocol) | |
|---|---|---|
| Connects | An app you work in, to an agent | An agent, to tools and data |
| Who is the “server” | The agent | The tool or data provider |
| Started by | Zed Industries, August 2025. Co-led with JetBrains since February 2026 | Anthropic, November 2024. Under the Agentic AI Foundation (Linux Foundation) since December 2025 |
| Message format | JSON-RPC 2.0 | JSON-RPC 2.0 |
| Transport | stdio, agent runs as a subprocess. HTTP and WebSocket being specified | stdio and Streamable HTTP |
| State | Long-lived sessions with history, load and resume | Stateless per request since the 2026-07-28 revision |
| Who calls whom | Both directions, all the time | The agent calls the server; the server can ask for more input during a call |
| Streaming | Rich: text, reasoning, plans, tool calls, diffs, terminal output | Progress notifications; results per call |
| Human in the loop | Built in: permission requests, questions, cancel | Elicitation, relayed by the host |
| Typical message | session/prompt | tools/call |
| Best analogy | LSP, for agents | A universal plug for tools |
| Ecosystem (Oct 2026) | About 50 agents and more than 100 clients and tools listed | Thousands of servers |
A few points the table cannot carry:
- They share DNA on purpose. ACP reuses MCP’s content types (text, images, resources) so that a tool result can flow from an MCP server, through the agent, to the editor without being translated.
- MCP is not “one-way”, as some comparisons claim. Servers can send progress and ask for input. But the centre of gravity is clear: the agent decides what to call, and the server answers.
- ACP is opinionated about the user experience. Diffs, plans, permission prompts, slash commands and modes are part of the protocol, because an editor needs to draw them. MCP deliberately says nothing about what a screen looks like.
How they work together
ACP was designed to carry MCP, not to replace it.
- MCP passthrough. You configure your MCP servers once, in your editor. When a session starts, the editor passes them to the agent in
session/new. Switch from one agent to another and your tools come with you. - The editor as a tool provider. An editor often has useful tools of its own: diagnostics, symbol search, the debugger. It can expose them as an MCP server and include that server in the list. An extension in active development, MCP-over-ACP, lets those tool calls travel over the ACP connection that already exists, with no second process or port.
- Separation of concerns. The agent vendor focuses on reasoning. Tool authors focus on tools. Editor authors focus on the interface. Nobody has to write the other two.
Practical scenarios
1. Use the agent you like in the editor you like
You prefer Claude for refactoring, a colleague prefers Codex, and the team standard is a JetBrains IDE. With ACP this stops being a debate. Zed and JetBrains IDEs install agents from the shared ACP Registry; Neovim, Emacs, Sublime Text, Qt Creator and Obsidian reach the same agents through plugins. In Zed, adding any ACP agent by hand is one block of settings:
{
"agent_servers": {
"my-agent": {
"type": "custom",
"command": "/usr/local/bin/my-agent",
"args": ["--acp"],
"env": {}
}
}
}
2. Ship your own agent to every editor at once
If you build an internal agent (one tuned to your codebase, your review rules or your deploy process), ACP is the cheapest way to put it in front of every developer, whatever editor they use. The protocol is small enough to speak by hand. This complete agent, in plain Go with no dependencies, answers every prompt with a greeting. We tested it against the official TypeScript SDK’s client:
// A minimal ACP agent: newline-delimited JSON-RPC on stdin/stdout.
package main
import (
"bufio"
"encoding/json"
"os"
)
type msg struct {
JSONRPC string `json:"jsonrpc"`
ID json.RawMessage `json:"id,omitempty"`
Method string `json:"method,omitempty"`
Params json.RawMessage `json:"params,omitempty"`
Result any `json:"result,omitempty"`
Error any `json:"error,omitempty"`
}
var out = json.NewEncoder(os.Stdout) // one JSON object per line
func send(m msg) { m.JSONRPC = "2.0"; out.Encode(m) }
func main() {
in := bufio.NewScanner(os.Stdin)
in.Buffer(make([]byte, 1<<20), 64<<20) // prompts can carry whole files
for in.Scan() {
var m msg
if json.Unmarshal(in.Bytes(), &m) != nil || m.ID == nil || m.Method == "" {
continue // notifications (session/cancel) and replies: ignored in this sketch
}
switch m.Method {
case "initialize":
send(msg{ID: m.ID, Result: map[string]any{
"protocolVersion": 1,
"agentCapabilities": map[string]any{},
"authMethods": []any{},
}})
case "session/new":
send(msg{ID: m.ID, Result: map[string]any{"sessionId": "sess_1"}})
case "session/prompt":
var p struct {
SessionID string `json:"sessionId"`
}
json.Unmarshal(m.Params, &p)
upd, _ := json.Marshal(map[string]any{
"sessionId": p.SessionID,
"update": map[string]any{
"sessionUpdate": "agent_message_chunk",
"content": map[string]any{"type": "text", "text": "Hello from a Go agent."},
},
})
send(msg{Method: "session/update", Params: upd})
send(msg{ID: m.ID, Result: map[string]any{"stopReason": "end_turn"}})
default:
send(msg{ID: m.ID, Error: map[string]any{"code": -32601, "message": "method not found"}})
}
}
}
Build it, point the Zed settings above at the binary, and it appears in the agent panel. A real agent replaces the greeting with a model call and a tool loop, and generates a unique ID per session. For production work, use one of the official SDKs (Rust, TypeScript, Python, Kotlin, Java), which handle cancellation, capabilities and the newer optional methods.
3. Give every agent your company’s tools, once
Your team has an issue tracker, a staging database and an internal docs search. Wrap each one as an MCP server, add them to the editor configuration, and every ACP agent anyone launches gets them automatically through passthrough. When a better agent comes out next quarter, nothing about your tools changes. This is where the two protocols pay off together: MCP makes the tools portable across agents, ACP makes the agents portable across editors.
4. Agents outside the editor
“Client” does not have to mean code editor. Because ACP is just a well-defined conversation, it shows up in note-taking apps (several Obsidian plugins), game engines (Unity), team chat bridges that put agents into Slack or Discord, desktop apps that run several agents side by side in separate Git worktrees, and command-line runners such as acpx for scripts and CI jobs. Any interface that wants to host “an agent, any agent” can speak ACP instead of wiring up each vendor’s CLI flags.
5. A policy layer between you and the agent
Because every action the agent takes on your machine goes through well-defined messages, you can put something in the middle. A draft extension called proxy chains formalises this: a component that sits between client and agent can inject context, log every tool call, or refuse writes to sensitive paths such as .git/hooks and editor settings. Security teams can enforce one policy for every agent instead of auditing each one’s configuration format.
6. Agents on a bigger machine
Running a large agent job on a laptop is slow and noisy. ACP’s next step is remote agents: the same protocol over HTTP or WebSocket, so the agent runs on a build server or a cloud machine while you drive it from your editor. A transports working group has been specifying this since April 2026, and the TypeScript SDK already includes HTTP and WebSocket transports ahead of the spec. Treat it as early: stdio is still the only transport in the stable spec.
Security: what ACP does and does not promise
ACP’s own security page is refreshingly direct, and worth repeating:
- ACP does not sandbox the agent. The agent runs with whatever permissions its process has. If it can read your SSH keys, so can a prompt-injected version of it.
- Permission requests are advisory. A well-behaved agent asks and honours the answer. The protocol cannot force a badly written one to ask. File and terminal operations that go through the editor are the ones the editor can actually control.
- Treat agent output as untrusted. Editors should not run scripts, open links or fetch remote images from agent text without a user action, and should reveal hidden Unicode in commands and diffs.
- Agent names are self-reported. Identify an agent by how you launched it, not by what it says it is.
Where things stand (October 2026)
- v1 is stable and still evolving additively through an RFD process: session listing, resume, close and delete, usage reporting, structured questions to the user and more have landed since launch.
- v2 is in draft (since July 2026). It lets agents send updates outside a user’s turn, for background work and steering, overhauls diffs into structured file changes, and makes almost every enum extensible.
- Governance: Zed’s Ben Brandt and JetBrains’ Sergey Ignatov are lead maintainers. The spec and SDKs are Apache 2.0.
- SDKs: Rust and TypeScript reached 1.0 in June 2026; Python, Kotlin and Java SDKs exist.
- The ACP Registry (stable since March 2026) lets clients discover and install agents from one curated catalogue.
- Adoption: Zed and JetBrains IDEs support it natively. GitHub Copilot CLI, Gemini CLI, Cursor, Goose, Cline, Junie, Kiro, OpenHands, Mistral Vibe and others speak it directly; Claude Agent and Codex CLI run through adapters. VS Code support comes from community extensions rather than Microsoft.
When to use which
- You are building a tool, data source or API integration for agents to use → MCP.
- You are building an agent → speak ACP to reach users in their editors, and act as an MCP client to reach tools.
- You are building an editor or any app that hosts agents → implement the ACP client side, and pass the user’s MCP servers through.
- You want to control or observe agents across a team → an ACP proxy for behaviour, MCP server permissions for data.
- You want agents to talk to other agents → neither, really. That is the space of agent-to-agent protocols such as A2A.
The takeaway
MCP made tools portable across agents. ACP makes agents portable across the places people work. Together they turn the agent from a product you are locked into into a component you can swap, the same way LSP turned language support into a component a decade ago. If you build agents, speak both. If you build tools, MCP is enough. If you just use agents, the practical win is already here: pick the editor and the agent separately.
Sources: the Agent Client Protocol specification and repository (v1 docs, v2 draft announcement, RFDs, changelog), Zed’s external agents documentation, the JetBrains ACP Registry announcement, the MCP 2026-07-28 release candidate notes and Anthropic’s announcement donating MCP to the Agentic AI Foundation.