Agent architecture: A2A vs MCP — tools inside your agent vs agents across the org chart, and where the line falls now that MCP went stateless and gained Tasks
MCP connects an agent to tools, data sources and context: vertical, inside one agent. A2A connects agents to other agents: horizontal, across teams, departments and organisations. They are complementary, not competing.
That definition is the easy part. The decision it implies is where teams get it wrong, and the 2026 revisions removed the evidence they used to defend it.
A long-running MCP tool call and an A2A task are now the same HTTP request. Both encode calls as JSON-RPC 2.0 over stateless HTTP — MCP as its base encoding, A2A as one of three bindings (MCP specification, A2A specification). Both return a server-generated ID. Both can sit waiting for input, both offer polling plus a push path, and both run behind an ordinary load balancer with no session affinity.
If your architecture decision rests on transport, statefulness or duration, the specifications deleted the evidence you were arguing from. Here is the claim, and a practitioner can disagree with it: MCP and A2A no longer differ in anything you can observe on the wire. They differ in ownership — and if you cannot name the owner of the far end, you should not be reaching for A2A.
The "MCP vertical, A2A horizontal" split is still right about semantics and now wrong about the wire
Both projects still say the short line. MCP, per the A2A and MCP topic page, "standardizes how AI models and agents connect to and interact with tools, APIs, and other external resources" — vertical, deepening one agent. A2A "standardizes how independent, often opaque, AI agents communicate and collaborate as peers" — horizontal, connecting agents across "another team, another department, or a partner organization." The A2A v1.0 announcement repeats it.
The framing survives because semantics is the one place the two projects have not converged. Tools remain, in the topic page's words, "primitives with well-defined, structured inputs and outputs" performing "specific, often stateless, functions." Agents remain "autonomous systems" that "reason, plan, and use multiple tools."
What changed is that semantics is now the only place the distinction survives. The MCP 2026-07-28 specification lists "Stateless, self-contained requests" as a base-protocol property; the A2A v1.0 release describes a "stateless, layered architecture." Yang et al. (2025) split the field on two axes — context-oriented versus inter-agent, general-purpose versus domain-specific — and the first is the one that has gone invisible on the wire. Ehtesham et al. (2025) proposed a phased path outward from MCP toward agent-to-agent protocols; it is a sensible adoption order that predates both 2026 specs.
Anyone still arguing "MCP for quick stateless calls, A2A for long stateful ones" is arguing from the 2025 specs. That argument is over.
Stateless MCP did not delete state — it moved it into your application and handed your caller the bill
The release-candidate post is blunt about the scale: locked on May 21, 2026, final spec July 28, 2026, breaking changes. The initialize/initialized handshake was removed (SEP-2575), and the protocol-level session with it — the Mcp-Session-Id header is gone (SEP-2567). The consequence is one sentence worth reading twice: "any MCP request can land on any server instance."
State did not vanish. It relocated. The post's own example is a server minting "an explicit handle (a basket_id, a browser_id) from a tool and having the model pass it back as an ordinary argument on later calls." Read that as architecture, not as a code sample: conversational continuity is now the caller's model remembering a string and threading it through function arguments. The protocol will not do it for you.
Every Streamable HTTP POST now carries MCP-Protocol-Version, Mcp-Method and, for tool calls, Mcp-Name. A header/body mismatch is rejected with 400 and JSON-RPC error -32020, which the spec names HeaderMismatch. Servers no longer send their own JSON-RPC requests; elicitation and sampling come back inside an InputRequiredResult (SEP-2322).
POST /mcp HTTP/1.1
MCP-Protocol-Version: 2026-07-28
Mcp-Method: tools/call
Mcp-Name: create_basket
Content-Type: application/json
{"jsonrpc":"2.0","id":17,"method":"tools/call",
"params":{"name":"create_basket","arguments":{"sku":"A-1183","qty":2}}}
No cookie, no session header, no server-side memory. Every byte the server needs is in the request.
The stake is operational and it lands on the client team. A 2025-era deployment that drew continuity from a sticky session loses it on upgrade. The fix is not a config flag; it is application code that persists handles across calls and survives your model dropping one from context. Teams that read "stateless" as free scalability discover the cost in their own retry logic.
Tasks closed the duration gap, then the spec removed the one feature that made it a peer
Tasks is the honest reason this question is worth an article. The extension — ID io.modelcontextprotocol/tasks, in the ext-tasks repository — adds tasks/get, tasks/update and tasks/cancel. It has statuses. It supports mid-flight input. It offers polling and a push path. The MCP Tasks overview confirms creation is server-directed: the client opts in once per request and the server decides whether to materialise a task.
Then you read the constraints, and the resemblance stops.
| property | MCP Tasks (2026-07-28) | A2A tasks (1.0) |
|---|---|---|
| wraps | one tools/call | a message inside a contextId |
| who creates | server decides per request (overview) | server, in response to a message |
| list operation | none — removed | List Tasks, scoped to the authenticated caller |
| refuse | no state; JSON-RPC error or an error-flagged result | a rejected state |
| mid-task auth | no state | an auth-required state |
| push | notifications/tasks via subscriptions/listen | streaming, webhooks |
The only task-augmentable method is a single tool call. There is no conversation identifier, because MCP expects conversational state to travel as explicit handles. The A2A specification defines eight task states against MCP's five, with server-generated task IDs, a context identifier grouping tasks and messages, and List Tasks returning only what the authenticated client may see.
The enum difference is not cosmetic. A2A has a rejected state: the agent declines the work. MCP Tasks has no such state, because a refusal is either a JSON-RPC error or a tool result with its isError flag set — which the ext-tasks repository still counts as completed, while failed is reserved for JSON-RPC errors. A tool can fail. Only an agent can say no.
Now the reversal, because the first version of this story was wrong.
When Tasks shipped as an experimental core feature in 2025-11-25, the obvious reading was that MCP now covered long-running work end to end and A2A's task lifecycle had lost its reason to exist. The 2026-07-28 release went the other way, deliberately. Tasks moved from core to an extension. And the client-side listing was removed for a reason the release-candidate post states plainly: "because it can't be scoped safely without sessions."
That is the strongest thing in this material. The feature would have let an MCP server behave like a counterparty: a client enumerating the work outstanding with it. The same statelessness that makes the server cheap to scale deleted it. You cannot scope a listing to a caller when you have agreed not to remember who the caller is. MCP chose scale and gave up the peer view; A2A kept the peer view and pays for it with a session-shaped task lifecycle over stateless HTTP.
The stake is concrete. Any dashboard or retry policy built on the 2025-11-25 core-Tasks shape is now wrong. A client that wants to know what is in flight keeps its own handle store — the same application-side state burden the session removal pushed onto you. Two breaking changes, one theme.
One word of restraint: the Tasks overview says host support varies by client. Read the table as a claim about what the specifications permit, not about what is deployed in your toolchain.
A2A publishes an identity for the counterparty; MCP publishes one for an endpoint you already configured
The identity difference is not the strength of the mechanism — both rest on OAuth and TLS at bottom. It is about who the identity is about.
MCP's server/discover returns versions, capabilities and serverInfo, and then the specification undercuts its own field: "serverInfo is self-reported by the server and is not verified by the protocol … Clients SHOULD NOT rely on it for security decisions."
MCP's real trust anchor sits elsewhere. Per MCP authorization, a protected MCP server is an OAuth 2.1 resource server that MUST publish Protected Resource Metadata (RFC 9728); clients MUST send Resource Indicators (RFC 8707) and MUST validate iss per RFC 9207. That is the identity of an endpoint, configured by whoever installed the server. It answers "is this the server I think I configured," not "should I trust this third party."
A2A answers the second question. The A2A specification puts an Agent Card at a well-known URI (RFC 8615) listing skills and securitySchemes, optionally signed with JWS (RFC 7515) after RFC 8785 canonicalisation. The v1.0 announcement frames signing as "establishing trust before interaction across organizational boundaries." That is a document about someone else's service, readable by someone who did not install it.
Then the caveat, in the same breath: A2A agent discovery states that "the current A2A specification does not prescribe a standard API for curated registries." Discoverable today still means a URL somebody handed you. Signed Agent Cards are a mechanism you can adopt, not an ecosystem you can shop in.
The real test is not how long the call runs — it is who is allowed to choose the operation
Strip everything away and one question decides the boundary. Can the caller's model be trusted to pick the operation and fill in its arguments? If yes, the far end is a tool, however long it runs. If the owning team must keep that decision, it is an agent, however fast it answers.
| condition | evidence in the specs | result |
|---|---|---|
| caller's model can pick the operation | inputs publish as full JSON Schema | MCP server |
| owning team must keep the plan | opacity is the point | A2A agent |
| far end must be able to decline | a rejected state exists; MCP has none | A2A agent |
| credentials that are not the caller's must flow | MCP forbids token transit | A2A agent |
MCP is built on the first answer. The release-candidate post confirms tool inputs are published as full JSON Schema 2020-12, and a schema exists so the caller's model can construct arguments. The MCP specification puts consent on the same side: "Hosts must obtain explicit user consent before invoking any tool." Planning and permission both sit with the caller.
A2A is built on the second. Its specification says agents collaborate "without needing to share their internal thoughts, plans, or tool implementations." The A2A and MCP topic page supplies the worked example: an auto repair shop where the shop manager agent delegates to a mechanic agent, and the mechanic agent calls MCP tools the manager never sees. The caller sends a goal in natural language plus parts — not arguments to a function it inspected.
This is why the wire convergence is a red herring. Two endpoints can look identical in headers and JSON bodies and still differ in who is allowed to decide what happens. Habler et al. (2025) threat-modelled A2A precisely because delegating to an opaque peer creates trust questions a caller-driven tool protocol never has to answer — with the authors' own limitation, since the paper predates A2A 1.0 and Signed Agent Cards. Read it as a map of the problem, not of the current spec.
Only one of the two protocols has a state for waiting on someone else's credentials
MCP models re-authorization as the same client getting a wider scope on the same server. MCP authorization requires a step-up flow through 403 insufficient_scope. It also draws a hard line: "MCP servers MUST NOT accept or transit any other tokens." Over stdio, authorization does not apply at all — credentials come from the environment.
Read those two facts together and the consequence is structural. MCP has no slot for delegation. A token belonging to somebody else cannot pass through an MCP server, and a stdio server is trusted by whoever launched it. That is the correct design for a tool protocol: one user, one client, many servers, no forwarding.
A2A has the slot. Section 7.6 of the A2A specification defines In-Task Authorization: an agent that needs "an OAuth access token to call an API or another agent" or "human approval before a destructive action" moves the task to the auth-required state and the credentials arrive out of band. The Agent Card can declare several schemes at once and authorization can be per skill. The enterprise-ready topic page is explicit that identity is handled at the protocol layer rather than in A2A semantics — payloads "don't carry user or client identity information directly."
That difference is the one people skip, and it is usually why a design fails late. Your workflow may need credentials that are not the caller's, for a task that is not the caller's either. No amount of MCP Tasks expresses that. If your workflow is a deterministic operation behind one owner's audience check, In-Task Authorization is machinery you will configure and never use.
The strongest case against A2A is stronger than its advocates admit, and it still loses on three words
The advocate's case deserves stating without a straw: A2A is redundant engineering, and this is protocol theology dressed up as architecture. Once MCP is stateless and has Tasks, everything A2A calls horizontal can be modelled as a task-augmented tool call on a remote server — server-generated handles, an input-required state, polling plus push, an endpoint at a known URI, trust bound to an OAuth audience. Adding A2A buys a second authorization model, a second discovery mechanism and a second task lifecycle to instrument, for a boundary MCP already crosses. And A2A delegates nothing on its own terms: its own specification handles identity at the HTTP layer, and its discovery page admits there is no standard registry API. Two protocols, one boundary, twice the surface.
That is largely right about the wire, and the specs concede it. Both are stateless. Both sit behind ordinary load balancers. Both have an input-required state. JSON-RPC 2.0 is MCP's base encoding and one of A2A's three bindings. MCP Tasks genuinely covers CI jobs, batch work and approval gates, so long runtime alone is no longer a reason to delegate. A2A's discoverability promise is soft today.
Three things survive the attack. First, MCP's own spec tells clients not to trust serverInfo for security decisions — you get the identity of an endpoint you configured, not of a counterparty you did not. Second, MCP authorization forbids transiting any other token, so there is no delegation slot. Third, and decisively, there is no MCP state in which the far end declines the work.
The convergence is real on the wire and absent in what the far end is permitted to decide. If you never need a far end that decides anything, the advocate is right and you should not adopt A2A.
Picking one protocol for everything costs you a capability you cannot express
Four shapes are available. Each has a bill.
MCP only, with Tasks for anything long. One agent, many servers; long work becomes a task-augmented tool call. Cost: no refusal state, no mid-task authorization state, no counterparty identity, tokens cannot transit, no client-side task listing. Failure mode: a capability whose owning team will not publish a schema cannot be expressed at all, so it gets modelled as a tool the caller's model is trusted to drive blind. Right when: one owner, a describable argument schema, and the caller may choose the operation.
A2A for everything, including the deterministic parts. Every capability becomes a signed, discoverable, per-skill-authorized remote agent. Cost: a planning round trip on work the caller could have called directly; per-skill authorization against multiple declared schemes; registry work the specification does not standardise. Right when: the far end is another team, vendor or organisation, and the plan is genuinely theirs to keep.
Both, split by capability subset. A team publishes an A2A agent for other teams and an MCP server exposing the deterministic subset for its own agents — the pattern the A2A and MCP topic page anticipates when skills "can be called in a tool-like, stateless way." Cost: two surfaces to secure, version and observe, plus drift between what the agent does and what the tool contract promises. Right when: a platform team has internal consumers on its own cadence and external consumers it does not control.
Compile the repeated step into a versioned tool instead of delegating it. The only measured numbers in this entire body of material come from here, and they are not about protocols. A production Fulfillment Center alarm-triage agent diagnosing a 44-node SOP cut p50 latency by 42% by calling pre-compiled tools rather than regenerating code. On 1,500 historical alarms it reduced end-to-end error rate by up to 53%, and a controlled ablation of a direct-call architecture cut p50 latency by a further 62% (Kujanpää et al., 2026). Transfer argument: that measures a stable compiled tool against an inference-time loop — the same judgement the MCP-side checklist makes — so it is evidence for compiling the step, not evidence about MCP versus A2A. Right when: the work is high-volume, repetitive and procedural. Cost: upfront synthesis, validation and versioning, and the tools will expose your specification gaps rather than hiding them.
Split for a reason you can name. "It's an agent" is not a reason.
What this is based on
Every claim above is read, not run. Nothing here was measured: the specifications, the ext-tasks repository and the topic pages are documents, and no authorization flow, discovery lookup or task lifecycle was exercised. The numbers from Kujanpää et al. come from a production alarm-triage system, not from an MCP or A2A deployment. Where the material is silent — client support for Tasks, registry adoption at scale, retry and idempotency behaviour — this article is silent too.
Map protocol boundaries to ownership boundaries
Expose a capability as an MCP server when the caller's model should decide which operation runs and with what arguments, and JSON Schema can describe them (MCP specification); when one owner's policy behind an OAuth audience check is the whole trust story and nothing downstream needs the caller's token (MCP authorization); and when long runtime is the only reason it feels agent-like, because Tasks now cover CI jobs, batch work and approval gates.
Expose it as an A2A agent when the owning team must keep the plan, the prompts and the tool list private (A2A specification); when it must be able to refuse, or to pause for someone else's authorization; when callers who did not install it must be able to verify it through a signed Agent Card (A2A v1.0 announcement); or when the exchange is a multi-turn conversation grouped under a contextId rather than a handle the caller's model threads through calls.
Run that in the design review, not after the integration. Write the answer to each question down: who plans; who owns the refusal; whose credentials flow; whether the caller can verify the far end or merely trusts an endpoint it installed; whether long runtime is the only reason you are splitting; and whether you can actually write the schema. If you cannot write the schema, MCP cannot express the capability, and you are about to hide a planner inside a tool.
The Linux Foundation reported more than 150 supporting organisations on April 9, 2026 and described A2A and MCP as complementary projects (Linux Foundation). Context only. An organisation count is not an argument about architecture.
FAQ
What is the difference between A2A and MCP? MCP connects an agent to tools, data sources and context. A2A connects agents to other agents. Different layers, complementary rather than competing.
What does it mean that MCP is stateless? The protocol-level session and the session header were removed in the 2026-07-28 specification, so any request can land on any server instance. State did not disappear — it moved to explicit handles the caller's model passes back as ordinary arguments.
What are Tasks in MCP? An extension that wraps exactly one tool call with a server-generated ID and the statuses working, input_required, completed, failed and cancelled. It shipped as an experimental core feature in 2025-11-25 and moved to an extension in 2026-07-28. There is no client-side task listing. If you see "MCP primitive," it is out of date. (Tasks overview)
When should I use A2A instead of MCP? When the far end has its own owner who must keep the plan, must be able to decline the work, or must be able to pause for credentials or approval that are not the caller's.
Can they be used together? Yes, and that is the common shape: an A2A agent published for other teams, an MCP server exposing the deterministic subset for your own.
The seam belongs where the ownership changes
The one-liner survives because it was never really about protocols. It was about who is allowed to decide — and that question has an answer outside the spec, in your org chart.
What the 2026 revisions did was remove the excuse to avoid asking it. Statelessness, Tasks and signed Agent Cards mean you can no longer justify the choice with latency, duration or transport, because those no longer separate the two. What remains is the part you cannot refactor later: whether the thing on the other end has an owner who can say no, and whether you are prepared to be told so.
Pick the boundary first. The protocol is the easy part.
