← All writing
AI

ACP and MCP: two sides of an agent

The Agent Client Protocol connects coding agents to the apps you work in. The Model Context Protocol connects them to tools and data. What ACP is, how it works on the wire, how it differs from MCP, and when you need which.

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.

WITHOUT A PROTOCOLWITH ACPeditorsagentseditorsagentsZedClaudeJetBrainsCodexNeovimGeminiEmacsCopilotACPZedClaudeJetBrainsCodexNeovimGeminiEmacsCopilot4 × 4 = 16custom integrations4 + 4 = 8implementations of one protocol
Figure 1. Without a shared protocol, every editor integrates with every agent. With ACP, each side implements the protocol once.

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.

WHERE EACH PROTOCOL SITSACP · Agent Client Protocolfaces the personprompts, cancelstreamed repliesplans and diffspermission asksfiles, terminalsmodes, commandsyoueditor / appACP clientagentACP agentMCP clientGitHubPostgresdocs searchMCP serversMCP · Model Context Protocolfaces the worldtools/callresources, promptsstateless callsany agent can useSame JSON-RPC,opposite sidesof the agent.
Figure 2. ACP faces the person, through the app they work in. MCP faces the tools and data the agent uses. The agent sits in the middle and speaks both.
  • 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.

editorACP clientagentACP agentMCP servertoolsSETUPPROMPTTURNyou approveinitializecapabilitiessession/new · cwd, mcpServerstools/listsessionIdsession/prompt · your requestsession/update · plantools/call · queryresultsession/update · diffsession/request_permissionallow oncefs/write_text_filesession/update · replystopReason: end_turn
Figure 3. A prompt turn in ACP. The agent streams its plan and progress, asks before it writes, and has the editor do the write. The turn ends with a stop reason.

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

DirectionMethodPurpose
Editor → agentinitialize, authenticateAgree the version and capabilities, sign in
Editor → agentsession/new, session/load, session/resumeStart, replay or reattach to a conversation
Editor → agentsession/prompt, session/cancelSend a message, stop the current work
Editor → agentsession/set_mode, session/listSwitch modes (for example ask or code), list past sessions
Agent → editorsession/updateStream text, thoughts, plans, tool calls, diffs, usage
Agent → editorsession/request_permissionAsk the user before acting
Agent → editorfs/read_text_file, fs/write_text_fileRead and write through the editor
Agent → editorterminal/create, terminal/output, terminal/kill …Run commands in an editor-managed terminal
Agent → editorelicitation/createAsk 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)
ConnectsAn app you work in, to an agentAn agent, to tools and data
Who is the “server”The agentThe tool or data provider
Started byZed Industries, August 2025. Co-led with JetBrains since February 2026Anthropic, November 2024. Under the Agentic AI Foundation (Linux Foundation) since December 2025
Message formatJSON-RPC 2.0JSON-RPC 2.0
Transportstdio, agent runs as a subprocess. HTTP and WebSocket being specifiedstdio and Streamable HTTP
StateLong-lived sessions with history, load and resumeStateless per request since the 2026-07-28 revision
Who calls whomBoth directions, all the timeThe agent calls the server; the server can ask for more input during a call
StreamingRich: text, reasoning, plans, tool calls, diffs, terminal outputProgress notifications; results per call
Human in the loopBuilt in: permission requests, questions, cancelElicitation, relayed by the host
Typical messagesession/prompttools/call
Best analogyLSP, for agentsA universal plug for tools
Ecosystem (Oct 2026)About 50 agents and more than 100 clients and tools listedThousands of servers
ACP · A CONVERSATIONMCP · A FUNCTION CALLeditoragentsession/promptsession/cancelset_modesession/update ×Nrequest_permissionfs/write_text_fileterminal/createone long-lived sessionagentMCPservertools/callresources/readresultprogress, inputeach request stands alone
Figure 4. Who starts the conversation. In ACP both sides call each other through the whole session. In MCP the agent asks and the server answers.

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.

CONFIGURE TOOLS ONCE, USE THEM WITH ANY AGENTeditormcpServersgithubpostgresdocssession/new { mcpServers }ACPany ACPagentClaudeCodexGeminigithubpostgresdocsMCP, directyou set this up once
Figure 5. The user configures MCP servers once, in the editor. ACP hands that list to whichever agent is running, and the agent connects to the tools directly.
  1. 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.
  2. 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.
  3. 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.

AIDeveloper tools
Working through something similar?
I'm Udara. I build these tools and I like talking about how they get built. Tell me what you're stuck on, or let's work on it together.
Write to me
Keep reading