← back
00 · opening
Last updated May 31, 2026

Four protocols, one conversation.

How an API call, MCP, and the two unrelated ACPs talk to each other. Each one connects a different pair of programs and uses the words “client” and “server” in its own way. All four send their bytes over the same wire.

API app server MCP agent tools ACP · zed editor agent ACP→A2A agent agent DNS · TLS · HTTP the shared wire · every contract becomes bytes here
Each protocol is a contract between two parties. I'll call the place where two parties meet a seam. The names for the parties change from seam to seam, but all four protocols end up as the same kind of packets on the same wire.
scroll
01 · the wire

Before any protocol runs, a request has to reach the server.

Whenever one of these protocols crosses a network, it travels as an HTTP request, sent inside a TLS connection, which runs on top of TCP. Before the first byte reaches a server, three things happen. A name is turned into an address (DNS). A secure channel opens (TLS). Then a structured message is sent through it (HTTP). Step through one request below to see each layer added in turn.

one https request, end to end
01 URL parse 02 DNS resolve 03 TCP connect 04 TLS handshake 05 HTTP request 06 200 response name → address open a secure channel ask & answer
01 / url
You have a name. The network needs an address.
Step through to follow one https:// request from a typed URL to a rendered response.
in essence
# one https GET, peeled open layer by layer
ip   = dns_resolve("api.example.com")        # name → IP, cached by TTL
sock = tcp_connect(ip, 443)               # SYN · SYN-ACK · ACK
tls  = tls_handshake(sock, sni="api.example.com")  # 1 round trip; verify cert vs a CA
tls.send("GET /v1 HTTP/2 · Host: ... · Authorization: ...")
resp = tls.recv()                          # 200 OK + headers + body
the contrast: Everything else on this page is a variation on this round trip. Agree on a fixed format for the body and you have an API. Add a session, plus a handshake where both sides list what they can do, and you have MCP. Replace the network with a local subprocess and you have Zed's ACP.
02 · the api

An API is an agreed-on shape for requests and responses.

An API is a contract: send a request shaped like this, and you get a response shaped like that. On the web that usually means REST over HTTP. A request has a method, a path that names a resource, headers (one of them says who's asking), and a JSON body. The server answers with a status code and a body. Every agent protocol is built on this layer. Underneath, MCP, both ACPs, and A2A are all structured API calls.

request / response, part by part
CLIENT the caller POST /v1/messages HTTP/2 Host: api.example.com Authorization: Bearer sk-… ← who Content-Type: application/json { "model": "…", "messages": […] } ← body REQUEST HTTP/2 200 OK ← status Content-Type: application/json { "id": "msg_01…", "content": […] } ← body the caller asked for RESPONSE SERVER the resource
The method and path say what you want. A header proves who you are. The status code tells you how it went.
reading the status code 2xx it worked (200 ok, 201 created). 3xx look elsewhere (301 moved). 4xx your fault: 401 means not authenticated, 403 means authenticated but not allowed, 429 means you're rate limited. 5xx the server's fault (500).
the contrast: REST gives you nouns (resources) and verbs (methods). The agent protocols keep the same envelope but use their own, richer set of verbs: instead of GET /users you'll see tools/call or session/prompt. If you can read an HTTP exchange, you can read all of them.
03 · mcp · agent ↔ tools

MCP is USB-C for models: one standard way to plug in many tools.

The Model Context Protocol is a standard way for an app to give a model tools, data, and prompts. A host (Claude Desktop, an IDE) runs one or more clients. Each client holds a 1:1 session with one server. The messages are JSON-RPC 2.0, sent over stdio (for a local server) or Streamable HTTP (for a remote one). The host keeps servers isolated: a server can't read the whole conversation or see what other servers are doing.

a tool call, message by message
CLIENT in the host / agent SERVER exposes tools initialize → ← result · capabilities notifications/initialized → tools/list → ← the tools, each with a JSON Schema tools/call → the model chose this ← result · content (or isError) handshake, then a stream of typed calls one stateful session
message 01 / initialize
The client opens the session.
Step through one round trip: a handshake where both sides say what they support, then the model calling a tool.
on the wire · the tool call
// the model decided to call a tool → the client sends:
{"jsonrpc":"2.0", "id":2, "method":"tools/call",
 "params":{"name":"get_weather", "arguments":{"city":"NYC"}}}

// the server runs it and replies with the same id:
{"jsonrpc":"2.0", "id":2,
 "result":{"content":[{"type":"text", "text":"72°F, clear"}]}}

A server exposes three primitives, split by who decides to use them:

model-controlled

Tools

what
Functions the model can invoke: query a DB, call an API, run code.
how
tools/list, then tools/call. Each tool has a JSON Schema.
app-driven

Resources

what
Context the app can read: files, DB schemas, records. Each has a URI.
how
resources/read; the app decides what to attach.
user-controlled

Prompts

what
Reusable templates, shown to the user as slash commands.
how
prompts/get when the user picks one.
the two transports stdio runs the server as a local subprocess and sends JSON-RPC messages over the pipe, one per line. Streamable HTTP is for remote servers. It uses a single endpoint: the client sends messages with POST, and the server can optionally stream replies back with SSE. It replaced the older HTTP+SSE transport in 2025. The client can also offer features of its own to the server: sampling, roots, and elicitation.
the contrast: An API call is one exchange: a request, a response, and it's over. MCP is a session. It starts with a handshake where both sides list their capabilities, then continues as a stream of typed requests and notifications. The host can ask the user for consent at every tool call.
04 · acp · editor ↔ agent

The first ACP, Zed's Agent Client Protocol, is LSP for coding agents.

Two unrelated protocols share the initials ACP. The first is Zed's Agent Client Protocol. It sets a standard for how a code editor (the client) talks to a coding agent like Claude Code or Gemini CLI (the agent). This is the same idea as the Language Server Protocol, which let any editor work with any language. The editor starts the agent as a subprocess, and they exchange JSON-RPC 2.0 messages over stdio. The editor owns the UI and the files. The agent does the thinking, and it asks permission before it acts.

acp on the left, mcp on the right: one agent, two roles
EDITOR the ACP client owns UI + files CODING AGENT ACP server · MCP client does the thinking MCP SERVER files · git MCP SERVER search · db ACP · stdio MCP
The agent is an ACP server to the editor on its left, and an MCP client to its tools on the right. It is the same process in both roles.
on the wire · an acp session
editor → agent   initialize            # negotiate versions + auth methods
editor → agent   session/new           # a fresh conversation, its own history
editor → agent   session/prompt        # "fix the failing test"
agent  → editor  session/update        # stream tokens + tool calls back to the UI
agent  → editor  session/request_permission  # may I run this command?
agent  → editor  fs/read_text_file     # the editor owns the files; the agent asks
the contrast: In MCP the agent is a client of its tool servers. In ACP-Zed the agent is the server, and the editor is the client. So the same program plays opposite roles in the two protocols. Zed's ACP also reuses MCP's JSON shapes on purpose, so the two fit together cleanly: one ACP session can wrap several MCP sessions.
05 · acp · agent ↔ agent

The second ACP, IBM's, merged into Google's A2A.

The other ACP is IBM's Agent Communication Protocol (from IBM Research and BeeAI, March 2025). It solves a different problem: letting agents talk to other agents. It also uses a different wire format: plain REST over HTTP. Its designers chose not to use JSON-RPC, so you can drive it with curl. A month later, Google shipped A2A to solve the same problem. Rather than split the ecosystem, in August 2025 IBM's ACP wound down and merged into A2A under the Linux Foundation. So of the two ACPs, only Zed's is still alive and independent. IBM's is now part of A2A's history.

how two agent-to-agent standards became one
ACP · IBM Mar 2025 REST · BeeAI A2A · Google Apr 2025 same problem ACP → A2A Aug 2025 ACP winds down A2A lives now Linux Foundation
Two competing agent-to-agent standards merged into one, and that one is A2A.

A2A works like this. Each agent publishes an Agent Card at /.well-known/agent-card.json. The card is a machine-readable business card that lists the agent's skills and the auth schemes it accepts. Agents then exchange Tasks, Messages, and Artifacts over JSON-RPC (or gRPC, or REST). They use SSE for streaming and webhooks for push notifications.

alive · independent

ACP · Zed

agent client protocol
editor ↔ agent
wire
JSON-RPC over stdio
status
Active, its own thing.
wound down

ACP · IBM

agent communication protocol
agent ↔ agent
wire
REST over HTTP
status
Merged into A2A, Aug 2025.
current standard

A2A · Google

agent2agent
agent ↔ agent
wire
JSON-RPC / gRPC / REST
status
Active, Linux Foundation.
why two ACPs? It's a coincidence. Zed's Agent Client Protocol (editor ↔ agent) and IBM's Agent Communication Protocol (agent ↔ agent) were named independently and share nothing but the three letters. When someone says “ACP,” ask which one they mean: editor to agent, or agent to agent.
the contrast: MCP connects an agent to its tools. A2A connects an agent to its peers. ACP-Zed connects an editor to its agent. That makes three seams in one system, and a single prompt can cross all three.
06 · the whole conversation

One prompt can pass through all four protocols.

Say you type a request to a coding agent inside your editor. That one request can pass through all four protocols, each at its own seam, and each one ends up as bytes on the wire. Step through the trace and watch the words client and server change meaning at every boundary.

one request, traced through four protocols
YOU the user EDITOR ACP client AGENT server to the editor, client to its tools, peer to other agents MCP SERVER tools EXTERNAL API someone else's server PEER AGENT another team's agent DNS · TLS · HTTP for remote hops; local hops use stdio type ACP · session/prompt · stdio MCP · tools/call HTTPS A2A · message/send session/update ↩
hop 00 / you → editor
You type.
Step through the trace to follow one prompt across all four protocols, and watch who counts as “client” and “server” at each seam.
the contrast: It's natural to think of “client” and “server” as kinds of software. They are roles, and they only make sense at a particular seam. The agent is a server to the editor, a client to its tools, and a peer to other agents, all at the same time, in one process. When the words get confusing, ask “which seam?”
07 · trust boundaries

Every seam is also a place to get fooled.

Each protocol boundary is a trust boundary: a place where data from a less trusted source meets a program with more authority. Get the boundary wrong and low-privilege input gets to use a high-privilege capability. The most important boundary in any agent sits between its trusted instructions and the untrusted data it reads. Three kinds of failure keep showing up.

the lethal trifecta · dangerous only when all three meet
AGENT one context ① PRIVATE DATA repos, secrets, mail ② UNTRUSTED INPUT web pages, tool output trust boundary ③ EXFIL CHANNEL a way to send data out private data + untrusted input + a way out = an attacker reads your secrets
A poisoned web page or tool result is just text. The model can't tell that text apart from your instructions, so it follows it. Remove any one of the three legs and the attack fails.
borrowed authority

Confused deputy

what
A middleman with special access (a gateway, an MCP proxy) is tricked into using its authority for a caller who doesn't have that authority.
where it bites
OAuth proxies with a shared client ID and a cached consent cookie. CSRF is the web's classic case.
the fix
Ask for consent per client, and match redirect_uri exactly. Never act on ambient authority, like a cookie the browser attached on its own.
crossed boundary

Prompt injection

what
Untrusted text contains instructions, and the model obeys them. The root cause is the same as SQL injection: trusted and untrusted text mixed in one stream.
where it bites
Any tool result or fetched page that ends up in the model's context. The lethal trifecta is the worst case.
the fix
Cut one leg of the trifecta. Treat tool metadata as untrusted unless the server is trusted.
wrong audience

Token passthrough

what
A server forwards a token it received straight to a downstream API, instead of getting its own.
where it bites
It breaks rate limits, audit trails, and trust boundaries. A stolen token turns the server into a proxy.
the fix
Audience-bind every token. MCP's spec forbids passthrough outright.
in essence
# the lethal trifecta: risky only when all three are true at once
danger = has_private_data and reads_untrusted_input and can_exfiltrate
# there is no 95%-reliable filter. the only sturdy defense is structural:
assert not danger   # remove a capability until one leg is gone (least privilege)
the contrast: The protocols give you the seams where safety can be enforced, but they don't make you safe by themselves. MCP can demand consent at each tool call, limit a token to one audience, and keep servers isolated from each other. But the spec says plainly that it “cannot enforce these security principles at the protocol level.” Enforcing them is the host's job.
08 · who are you

Four ways to prove you're allowed.

Every request so far carried proof of identity in the same place: the Authorization header. The exception is mTLS, where the proof is part of the TLS handshake itself. Here are the four schemes you'll meet, roughly from weakest and simplest to strongest and most work to set up. Pick one to see how it travels.

authentication · pick a scheme
api key
A static secret in a header.
in essence · oauth 2.1 authorization code + pkce
verifier  = random()                 # the client's one-time secret
challenge = sha256(verifier)         # sent up front, so the code can't be stolen mid-flight
code  = authorize(challenge, scope, redirect)   # the user consents in a browser
token = exchange(code, verifier)     # proves it's the same client → access token (+ refresh)
call(api, auth=f"Bearer {token}")   # token is audience-scoped to this one api

At a glance, weakest secret to strongest identity:

a shared secret

API key

proves
which app is calling
lifetime
long; rotate to revoke
risk
copyable; leak = full access
a signed claim

Bearer · JWT

proves
signed claims (aud, exp)
lifetime
short; expiry limits the damage
risk
still bearer; needs TLS
delegated consent

OAuth 2.1

proves
the user said yes, to this scope
lifetime
short token, refreshable
risk
more parts; auth server is a big target
a held key

mTLS

proves
possession of a private key
lifetime
the certificate's validity
risk
certificates are a lot of work to manage
the contrast: The four schemes run from “a secret anyone could copy” to “a key only you hold.” API keys are bearer secrets. OAuth limits what a token can do with scope and audience. mTLS binds the token to a certificate, so a stolen token is useless. MCP chose OAuth 2.1 for its HTTP transport for this reason, and it forbids passing the resulting token anywhere it wasn't issued for.
09 · closing

Every protocol here is a contract at a seam.

A protocol is an agreement about the shape of a message and what its words mean. The API, MCP, Zed's ACP, IBM's ACP, and A2A each set up a contract at a different seam. The parties at each seam take the names “client” and “server” depending on where you stand. Underneath, they all come down to the same DNS lookup, TLS handshake, and HTTP request you met at the start.

So the interesting questions are about the seams. Who is allowed to ask? Which token proves it? What happens when an untrusted message crosses a trusted boundary? Get those right and the bytes take care of themselves.

The facts here come from the RFCs, the official protocol specs, and the foundations that now maintain them. Where a protocol changes fast (MCP, A2A), I cite the spec version I read. MCP's current revision is 2025-11-25, and IBM's ACP merged into A2A in August 2025.