← Learn

MCP vs A2A: Which Agent Protocol Should You Ship?

MCP vs A2A is almost always the wrong framing: one gives a single LLM a toolbox, the other lets two agents talk to each other, and half the people arguing about them are comparing a screwdriver to a phone call.

If you're shipping an agent or building something that integrates with one, the decision is not "which protocol is better." It is: what is the thing on the other end of the wire, and who is driving the conversation? Pick that axis first, and the protocol falls out of it.

We index 165,000+ ERC-8004 agents across 25 chains over at The Spawn, and we probe every one of them the same way: can we reach it, does it respond, does it return something a caller can actually use. That vantage gives a pretty clear read on where each protocol earns its keep and where it falls on its face. So let me skip the marketing deck and tell you what actually matters.

The distinction that actually decides it

Forget "tools vs agents." The honest split:

  • MCP (Model Context Protocol) is a way to expose a toolbox that any LLM can drive. The LLM is the thinker. The MCP server is a dumb pipe to tools, files, prompts. One-shot calls. The server does not reason. It answers.
  • A2A (Agent-to-Agent) is a way for one agent to delegate work to another agent. The caller hands off a task and expects the callee to think, possibly for a long time, possibly over many turns, possibly streaming updates back. Both sides reason.

That's it. MCP is for augmenting an LLM. A2A is for composing agents.

If the thing behind your endpoint is a function, MCP. If the thing behind your endpoint is another agent that will plan, think, maybe call its own tools, and hand you back a result, A2A. When you find yourself arguing about which to use, re-ask: is the other side a tool, or is it a thinker?

Everything below is elaboration on that one sentence.

MCP, concretely

MCP is an open protocol from Anthropic, now spoken by Claude, Cursor, Zed, and a growing list of clients. Under the hood it is plain JSON-RPC 2.0, carried either over stdio (for local servers) or over Streamable HTTP (for remote ones). It has three primitives worth knowing:

  • Tools are callable functions. This is 90% of why people use MCP.
  • Resources are readable blobs the model can pull in as context: files, URLs, database rows.
  • Prompts are reusable prompt templates the server offers to the client.

Most MCP servers in the wild only implement tools. That is fine.

A tools/list response looks like this:

{
  "jsonrpc": "2.0",
  "id": 1,
  "result": {
    "tools": [
      { "name": "search_flights", "description": "Find flights between two cities", "inputSchema": { "type": "object", "properties": { "from": {"type": "string"}, "to": {"type": "string"} } } }
    ]
  }
}

The LLM picks a tool, the client sends tools/call with arguments, the server runs it, returns a result. No state machine, no back-and-forth planning. One request, one answer.

The transport has one sharp edge that trips every new client. Streamable HTTP requires an initialize handshake before anything else will work. If you fire a tools/list at a cold endpoint, you'll often get a 400 or 406 back, sometimes with a bizarre content-type error. The fix is: POST initialize, grab the Mcp-Session-Id header off the response, and put it on every subsequent request. We hit this so often while probing agents that both our discovery path and our execution path share one session cache, keyed by an MD5 of the endpoint URL. If you're writing a client, save yourself the afternoon: handle 400/406 by retrying through initialize with the session header.

The MCP ecosystem on ERC-8004 skews practical. Some agents expose a dozen tools, some expose two. The useful ones have short, grounded descriptions and inputs that read like a function signature, not a marketing paragraph. Our quality checker probes a live endpoint the same way a real client would: initialize, list tools, invoke a safe one, check the response actually decodes. A tools/list that returns seventeen tools but every tools/call 500s does not score well, and it shouldn't.

A2A, concretely

A2A is Google's protocol for agents to call other agents. Also JSON-RPC, also HTTP, also simple on paper. Each A2A-capable agent publishes an agent-card.json that describes who it is, what skills it offers, and where to reach it:

{
  "name": "ResearchAgent",
  "description": "Runs deep research tasks and returns a report.",
  "url": "https://example.com/a2a",
  "skills": [
    { "id": "research", "name": "Deep research", "description": "..." }
  ]
}

Each skills[] entry is a first-class capability. The caller picks a skill and fires off a task. Unlike MCP, the expectation is that tasks can be long-running: streaming progress, multi-turn clarification, eventual completion. This is the part that makes A2A feel different in practice. You are not calling a function. You are handing another agent a job.

Here's where it gets ugly, and where most broken clients break. A2A has drifted across spec versions, and the method name changed. The 0.2.x spec uses tasks/send. The 0.3.x spec renamed it to message/send and added a required messageId on every message. Both are in the wild right now. A sane client tries one, catches -32601 (method not found), falls back to the other, and injects a messageId when it falls through to the 0.3.x path.

curl -X POST https://example.com/a2a \
  -H "Content-Type: application/json" \
  -d '{
    "jsonrpc": "2.0",
    "id": 1,
    "method": "tasks/send",
    "params": {
      "task": { "skillId": "research", "input": { "query": "solid-state battery patents 2025" } }
    }
  }'

If that 404s on the method, swap to message/send and add messageId. That's the whole compatibility story in one paragraph, and it's more than most implementations document.

One honest thing about A2A in the wild: fewer agents implement it than claim to. Plenty of agent-card.json files promise skills that 500 on invocation, or return a task ID that never resolves. From our auditing work we learned not to trust tools/list-style responses as proof of anything. The only signal that counts is a completed tasks/send (or message/send) that actually returns a usable payload. If you're building something that has to call A2A agents, probe with a real task before you trust the card.

When to use which

The decision tree is shorter than the protocols deserve.

Use MCP when the thing you're building or exposing is a set of capabilities for an LLM to use. Your users will write something into a chat, your LLM will decide when to call get_weather or query_db, and your server's job is to answer that call quickly and cleanly. MCP is also the right answer when you want broad client compatibility. Claude speaks it, Cursor speaks it, a growing list of local LLM runtimes speak it. If you ship an MCP server today, ten different clients can use it tomorrow without you lifting a finger.

Use A2A when you are composing a system of agents that hand off work. An orchestrator agent asks a research agent to go dig for an hour. A trading agent asks a risk agent to review a position. The callee is not a function, it is a coworker with its own planning loop. You want streaming status, multi-turn back-and-forth, task lifecycles. MCP technically can carry these, but the moment you start building "task status" and "session resumption" on top of tools/call, you are reinventing A2A with worse ergonomics.

The single question that resolves almost every case: does the callee need to think, or just respond? If it just responds, MCP. If it thinks, A2A.

When to use both

Yes, both. An agent can expose an MCP server and an A2A endpoint, and a lot of the good ones on The Spawn's Base catalog already do. They are not competing protocols, they are different doors into the same building.

A concrete pattern: your agent is a research assistant. It exposes an A2A endpoint so other agents can hand it "research this topic, report back in an hour" tasks. Internally, it drives an LLM that consumes tools through MCP, your own or somebody else's. The agent is an A2A callee at the top and an MCP client at the bottom. That stack is normal. That's what "agentic" means when you strip the marketing off.

The inverse also exists. An agent might expose its core capabilities as MCP tools for any LLM client to grab directly, and wrap those same capabilities behind an A2A skill for other agents that want a task-oriented interface. Same business logic, two doors, two audiences. Ship both if you can, because the cost is mostly in routing and schema, not in rebuilding anything.

Gotchas from the field

A few things we've learned from probing a lot of endpoints, in no particular order:

Session caching actually matters. Both MCP (via Mcp-Session-Id) and some A2A implementations carry state across calls. If your client re-initializes on every request, you'll feel it as latency and sometimes as outright breakage when a server rate-limits session creation. Cache the session by endpoint URL and share it across both discovery and execution.

Paid tools need proof of payment written somewhere durable. If you've wrapped an MCP tool behind x402 or any other payment layer, the tool result plus the transaction hash must persist into the conversation state. Otherwise the next turn re-invokes the paid tool and the user pays twice. We learned this the expensive way.

tools/list is not proof of anything. An agent can advertise thirty tools and fail on every invocation. Same for A2A skills on an agent-card.json. If you're grading quality, probe with a real call. If you're using an agent, probe before you trust it. Reputation on agent cards is currently worth about what reputation on random websites is worth, which is: read it, then verify it.

Free public RPCs and free public endpoints lie. Rate limits, 503s, silent CORS proxies returning the wrong content-type. Budget for retries, budget for timeouts, and treat every remote agent as a flaky dependency until it earns your trust over a few hundred successful calls.

The spec will keep drifting. A2A already has two method-name generations and MCP's transport has changed more than once. Write your client so the protocol version is a thin adapter, not a hardcoded assumption. You will thank yourself in six months.

The wrap

MCP is a toolbox protocol. A2A is a delegation protocol. You can ship one, or both, and neither choice is a religion. If you want to see how this plays out across a live population of agents, rather than in the abstract, the cleanest way is to run a real endpoint through the quality checker and watch what it does: initialize, probe, invoke, verify. That's also, roughly, our take on what "quality" even means for an onchain agent: reachable, responsive, returns something real. If your agent clears that bar on either protocol, it's in the 5% that do.

Pick the protocol that matches what's actually on the other side of the wire, handle the two or three sharp edges in the transport, and get back to building the thing that makes your agent worth calling in the first place. The protocol is the easy part.