Back to blog

A2A vs. MCP: Comparing AI Agent Communication Methods

Share article:

MCP (Model Context Protocol) connects one AI application to tools, data, and APIs. A2A (Agent2Agent) lets independent agents delegate work across trust boundaries. A2A vs. MCP is a question of layers, not competition. The usual mistake is adding a second agent where better tools would be enough. This guide measures both and shows which to build on.

Alt text: An “AI” icon inside a rounded square, with a code dashboard above it and an “AI parser” dashboard to the left displaying “Converting HTML into structured data.”

TL;DR

  • MCP reaches down from one agent to tools, data, and APIs. A2A reaches across from one agent to another. If you're choosing today, start with MCP.
  • Both protocols can report failed work inside a successful response, so a blocked fetch looks like a success to any client that only checks for a JSON-RPC error. You'll encounter this whichever one you choose.
  • A2A released v1.0 and renamed every JSON-RPC method. Copy an older example that uses message/send, and a v1.0 agent rejects it.
  • Probing 487 registry endpoints found only 22 that implement the current discovery method. Of the 243 that responded only to the older initialize handshake, 78% support nothing newer than the 2025-11-25 revision and 22% already support 2026-07-28.

What is MCP (Model Context Protocol)?

MCP is an open standard released by Anthropic in 2024. It defines how an AI application reaches external tools, data sources, and APIs through one shared interface, instead of a custom integration for each client-server pair. It's now governed by the Agentic AI Foundation, which the Linux Foundation formed in 2025 with MCP as one of its founding projects.

An AI application embeds an MCP client, which connects to one MCP server exposing tools (functions the model can call), resources (read-only data fetched by URI), and prompts (reusable templates a user can invoke).

Connecting a client to a server makes those tools concrete. The screenshots here use MCP Inspector, the reference client, running npx -y @decodo/mcp-server. That server reads a SCRAPER_API_TOKEN from the environment and exits without one, so reproducing these screenshots needs a Decodo account:

The panel shows what the Decodo MCP server exposes, seen from the client side. Most clients load every definition here into the model's context before the first question.

Any compatible client can call any compatible server, so an integration written once keeps working as AI agents and models change underneath it, as long as both ends stay on a shared revision.

Connect your AI agents to live web data

Decodo’s MCP server links LLMs to our powerful web scraping infrastructure, giving them access to real-time information from any website on demand.

What changed in the 2026-07-28 revision

MCP names each revision by its release date, so strings like 2026-07-28 and 2025-11-25 are version numbers rather than timestamps. The 2026-07-28 revision makes MCP stateless at the protocol layer, and that changes how servers are deployed. Every request now carries its own protocol version and client capabilities in a _meta block, so there's nothing to establish upfront.

In practice, 4 changes matter:

  • Sessions are gone – the Mcp-Session-Id header and protocol-level sessions were removed. Servers that need cross-call state issue explicit handles and pass them as ordinary tool arguments.
  • The handshake is gone – the initialize and notifications/initialized exchange no longer exists. A new method, server/discover, advertises supported versions and capabilities, and servers must implement it.
  • Roots, Sampling, and Logging are deprecated – Roots lets a client tell a server which directories or URLs it may access, Sampling lets a server use the client's model to generate a response, and Logging lets a server send structured log messages back to the client. The specification keeps all 3 valid for a deprecation window of at least 12 months. That puts their earliest removal in a revision released on or after 28 July 2027.
  • Tasks moved out of core – support for long-running work moved from experimental core methods into an official extension, io.modelcontextprotocol/tasks.

Tasks is the one change that isn't settled yet. Its own specification says there's no clear rule for which caller can access which task. An earlier tasks/list method was removed rather than fixed, leaving the task ID as the only real access control. Cornelia Davis, building a client-side reference implementation at Temporal, reported that no other client had implemented the extension at all.

The operational consequence is the point. A remote MCP server on this revision is an ordinary HTTP service that scales horizontally, with no sticky sessions and no protocol-level session store. That simplifies deployment rather than changing the protocol for its own sake. If you're setting up your own MCP server, target this revision from the start.

What is the A2A (Agent2Agent) protocol?

The A2A protocol is an open standard that lets AI agents built on different frameworks, by different vendors, discover each other and delegate tasks. Google announced it in 2025 and donated it to the Linux Foundation the same year. The protocol is built so agents can coordinate without exposing internal memory, tools, or proprietary logic. What an agent shows is still its own implementation's choice. In 2026, backers confirmed to Axios that A2A is moving into the Agentic AI Foundation, placing both protocols under the same governance.

A2A defines 2 roles. A client agent initiates and coordinates work, and a remote agent advertises what it can do and executes. Each can play either role in a different exchange, which makes the relationship peer-to-peer rather than hierarchical.

The protocol has 4 core components. Agent Cards handle discovery. Messages carry parts with a declared type: text, files, or structured data such as JSON. Tasks track lifecycle state across the exchange. Artifacts return the outputs.

Version 1.0 was released in March 2026 and added enterprise multi-tenancy, so a single endpoint can host many agents. According to the first-year report published by the Linux Foundation, more than 150 organizations support the protocol, with production SDKs in Python, JavaScript, Java, Go, and .NET.

What is an Agent Card?

An Agent Card is a public JSON metadata document that an A2A agent publishes to describe itself. Other agents read it to decide whether to delegate work. It lists the agent's name, purpose, endpoint URL, supported transports and modalities, capabilities, skills, and any authentication requirements.

Clients usually fetch it from a well-known path. Running a v1.0 agent locally and requesting /.well-known/agent-card.json returns the card that a peer would use to route work. This agent needs no authentication, so it declares no security schemes:

{
"name": "price-checker",
"description": "Returns the current listed price for a product SKU.",
"supportedInterfaces": [
{
"url": "http://127.0.0.1:9999/rpc",
"protocolBinding": "JSONRPC",
"protocolVersion": "1.0",
}
],
"version": "1.0.0",
"capabilities": {"streaming": true, "pushNotifications": false},
"defaultInputModes": ["text/plain"],
"defaultOutputModes": ["application/json"],
"skills": [
{
"id": "price_lookup",
"name": "Price lookup",
"description": "Given a product SKU, return price, currency and stock state.",
"tags": ["ecommerce", "pricing"],
}
],
}

The supportedInterfaces array lets you migrate without breaking callers, because one agent can advertise a v0.3 endpoint and a v1.0 endpoint at the same time, for as long as you keep both running. Callers can then upgrade when they are ready. The signature field is often mistaken for a v1.0 addition, but it was already in v0.3. v1.0 added the canonicalization rule that makes a signature verifiable across implementations. The specification also says cards may be signed, not must, so an unsigned card is still compliant. Requiring a signature is a decision the calling client has to make and enforce.

A live card from an unrelated company, fetched in August 2026, uses the v0.3 format:

A production Agent Card is served from a well-known path. It reports protocol version 0.3.0 and describes its endpoint with url and preferredTransport, which is the v0.3 format that supportedInterfaces replaced.

A2A vs. MCP: the core differences

The A2A vs. MCP distinction reduces to the direction of the call. MCP is vertical, with an agent reaching down to a tool that executes and returns a value. A2A is horizontal, with an agent reaching across to a peer that reasons, holds state, and can answer with questions of its own.

Here's how the 2 compare on the attributes that affect a design decision.

Attribute

MCP

A2A

Purpose

Give one agent access to tools, data, and APIs

Let independent agents delegate work to each other

Created by

Anthropic, 2024

Google, 2025

Governance

Agentic AI Foundation, under the Linux Foundation

Linux Foundation, moving to the Agentic AI Foundation

What it connects

An AI application to a server exposing tools

A client agent to a remote agent

Direction of the call

Down, to a tool

Across, to a peer

Discovery mechanism

server/discover on the endpoint

Agent Card at /.well-known/agent-card.json

State model

Stateless per request since 2026-07-28

Stateful, tasks track lifecycle state

Transport

JSON-RPC 2.0 over stdio or streamable HTTP

JSON-RPC 2.0, gRPC, or HTTP+JSON

Call overhead

1 round trip to a function

1 or more round trips, plus the peer's own inference

Streaming support

Response stream over server-sent events (SSE)

SSE, plus push notifications, a webhook the agent calls back

The state model row is the clearest difference, because A2A treats a delegation as a task with a lifecycle both sides track, while MCP core treats a call as a function that returns. That asymmetry exists because a tool finishes and a peer negotiates.

The discovery row follows the same pattern. An MCP client already knows the endpoint it was configured with, and asks that endpoint what it can do. An A2A client may find a peer it has never contacted, so it needs enough metadata to decide whether that peer can be trusted.

That row also shows a larger asymmetry. MCP discovery is infrastructure: the 2026-07-28 revision requires every server to implement server/discover, and the official registry serves a versioned API listing 13,332 active remote endpoints. A2A documents 3 discovery strategies, mentions registries twice in the whole specification, and has no official registry. The largest community directory, A2A Registry, listed 164 agents in August 2026. So you can browse a catalog of MCP servers, while A2A endpoints you arrange directly with each partner.

That gap also limits what can be measured. An MCP client can list the servers in the registry and probe them, which is where most of the numbers in this guide come from. A2A has nothing equivalent to probe the way server/discover allows, so the claims about it here are based on the specification, one live card, and agents run on a local machine.

The registry gap is a difference in adoption, not in design. MCP is the established standard at the tool layer. A2A's layer is real, but 164 agents isn't yet an ecosystem, and its case depends on coordination problems that mostly appear once agents belong to different teams or vendors, or once subtasks run in parallel with progress the caller has to track.

The distinction is least clear at the wrapper. An A2A agent can be exposed as an MCP tool, and an MCP server can be backed by an agent that reasons before it answers. Tools and agents are getting harder to tell apart. MCP also gets confused with similar packaging formats, so being clear on how Claude Skills differ from MCP helps before you commit to either protocol.

When to use MCP vs. A2A

Deciding when to use MCP vs. A2A comes down to one test. If the thing on the other end is a function, use MCP. If it negotiates rather than simply returning a value, use A2A. Run these 4 diagnostics against your own system before deciding.

  • Memory – does the target need to keep state between calls?
  • Ownership – is the target owned by another team or vendor, with its own deployment and release cycle?
  • Duration – does the work take seconds or minutes, and does the caller need progress updates?
  • Negotiation – will the target ever need to refuse or ask a question rather than return a value?

A "no" across all 4 means you need a tool, not a peer.

A real A2A agent publishes an Agent Card, usually at /.well-known/agent-card.json, the same well-known path the local example above used. If a partner only offers a REST endpoint or a webhook with no card, A2A has nothing to negotiate with. Often that's a tool labeled as an agent, and MCP is the right wrapper either way, not A2A.

Those 4 are qualitative. A quantitative one decides it first. Most clients load every tool definition into the agent's context, so each server you attach costs tokens before the first user message. Across a sample of 168 live servers, drawn independently of the revision probe further down, the median server costs 2,264 tokens against claude-opus-5. The most expensive server costs 65,233 tokens across 119 tools, and attaching the 5 most expensive uses roughly 234,000 tokens before the agent reads anything.

Those same 168 servers exposed 2,432 tools. Anthropic's own token counter measured 2,339 of them, from all but 4 of those servers, at 902,062 tokens. The 4 servers it skipped, 93 tools between them, published schemas the API rejected.

An earlier count of those same schemas used OpenAI's o200k_base tokenizer, the way many context estimates still do, and returned 590,334 tokens. That's about a third below the Anthropic count. Anthropic's guidance says not to use it for Claude, and schema-heavy input is exactly the case that guidance warns about.

That cost isn't fixed. Anthropic's Tool Search Tool and Cloudflare's Code Mode both aim to load only what a task needs, and the implementation section covers how much each helps. Once the tool definitions stop fitting in the context window, splitting work across agents becomes arithmetic rather than preference.

Names collide too. Across those servers, 78 tool names appear on more than one, search on 13 and fetch on 7, so attaching 5 at random gives a 12% chance of a duplicate name and attaching 10 gives 42%. Those 2 odds come from sampling the observed name distribution, not a birthday-problem approximation. The specification tells clients they should make the names distinct themselves, and warns that server names aren't unique either, so that fix is yours to build.

Splitting the tool surface across agents can fix the token arithmetic inside any one context window. It doesn't solve consistency. Separately maintained agents can diverge, each keeping its own copy of a fact that changed elsewhere. That coordination cost adds to the token cost; it doesn't replace it.

More servers give an agent more reach, not more judgment. Neither protocol decides which tool to call for you.

You can measure that token cost against your own stack instead of trusting these numbers:

A smaller August 2026 run than the 168-server sample above. It used tiktoken, so counts are roughly one-third lower than Anthropic’s. Schemas vary, and tool_cost.py can query any remote MCP endpoint for its tools.

The table below maps common scenarios onto the function-versus-peer test.

Scenario

Recommended protocol

Why

Single agent needing database or API access

MCP

The target executes, it doesn't reason

Agent needing live web data

MCP

Retrieval is a function call with arguments

Coordinating specialists across frameworks

A2A

They share no process or memory

Delegating to a vendor's agent

A2A

The vendor keeps its logic and state private

Parallel subtasks with independent progress

A2A

Each task has its own lifecycle state

Deterministic function call

MCP

A reasoning layer adds cost and variance

Long-running work needing progress updates

A2A today

MCP polls a task handle, but only through a Tasks extension that few clients implement

The default for anyone starting out is MCP. Most systems that appear to need multi-agent coordination need 1 agent with better tools. That's a judgment rather than a measurement, and the 4 diagnostics above are how you test it. A2A adds operational cost. It's only worth that cost when the agents can't, or shouldn't, run inside the same program. Check the MCP servers worth knowing for AI workflows before you add a second agent. When the same lookup repeats across tasks and the answer changes slowly, build an index in advance instead of querying the live system every time.

When to use A2A vs. MCP becomes a real question under 3 conditions. Agents run on different frameworks or clouds. Agents belong to different teams or vendors. Or subtasks run in parallel with independent progress the caller has to track. Until one of those is true, an orchestration layer such as n8n can do the coordinating without a second protocol to run.

How MCP and A2A work in practice: message structures

Both protocols are built on JSON-RPC 2.0, an RPC format organized around a method name and a params object. The difference shows in the structure of what each protocol puts inside it, not the envelope. The payloads below came from a local MCP server on the 2026-07-28 revision and a local A2A agent on v1.0. Both used the official Python SDKs.

An MCP tool call names a function and its arguments. Calling a web data retrieval tool produces this request:

{
"jsonrpc": "2.0",
"id": 3,
"method": "tools/call",
"params": {
"name": "fetch_product",
"arguments": {"url": "https://example.com/dp/B0CX23V2ZK", "render_js": true},
"_meta": {
"io.modelcontextprotocol/protocolVersion": "2026-07-28",
"io.modelcontextprotocol/clientCapabilities": {},
},
},
}

Only 3 fields matter. The method field names the operation, here tools/call, and arguments contains the input values. The _meta block contains the protocol version and client capabilities that the removed handshake used to establish. The body isn't the whole request. This revision also expects MCP-Protocol-VersionMcp-Method, and Mcp-Name headers that repeat values from the body, and a conforming server rejects a mismatch. Omitting _meta produces an invalid-params error.

The server responds with typed content and an explicit error flag:

{
"jsonrpc": "2.0",
"id": 3,
"result": {
"content": [
{
"type": "text",
"text": "{\"sku\": \"B0CX23V2ZK\", \"price\": 249.0, \"currency\": \"USD\", \"in_stock\": true}"
}
],
"isError": false,
"resultType": "complete",
"_meta": {
"io.modelcontextprotocol/serverInfo": { "name": "web-data", "version": "1.0.0" }
}
}
}

The resultType field is new in this revision and matters for control flow. A value of input_required means the server needs more information before it can finish. The example above is based on a real web data tool. The Decodo MCP server implements the same tools/call method, but client and server agree on a revision when they connect, and the Decodo server settles on the older 2025-11-25 revision. Its results therefore contain typed content without the newer resultType and _meta fields. Most of the servers in the probe below that supported only the old handshake are on that revision too.

MCP Inspector reports the revision it negotiated in a badge beside the connection indicator, here 2025-11-25. That revision determines which fields a result contains, so check the badge before trusting any example.

The A2A equivalent looks different from the first line, because v1.0 renamed every method:

{
"jsonrpc": "2.0",
"id": 1,
"method": "SendMessage",
"params": {
"message": {
"messageId": "m1",
"role": "ROLE_USER",
"parts": [{"text": "price for SKU B0CX23V2ZK"}]
}
}
}

The A2A v1.0 specification requires PascalCase method names matching gRPC conventions. SendMessage replaced the message/send of v0.3, which had itself replaced the tasks/send of earlier drafts. Sending message/send to a v1.0 agent returns JSON-RPC error -32601, Method not found. That error only appears when the request also carries an A2A-Version: 1.0 header. Without it the official server defaults to v0.3 and rejects the call before checking the method name. The server has to enable backward compatibility explicitly. Check method names against the current specification before copying any example.

The client sends the v0.3 method name to a v1.0 agent, with the A2A-Version: 1.0 header set. The request is otherwise well formed and the agent is running, so the only thing wrong is the method name.

The response contains a task rather than a value:

{
"jsonrpc": "2.0",
"id": 1,
"result": {
"task": {
"id": "00b977f5-8cf0-40a7-a8f2-18cc15c48066",
"contextId": "a7d43475-75ce-4e77-b7b6-06efa6e595ff",
"status": {"state": "TASK_STATE_COMPLETED"},
"artifacts": [
{
"artifactId": "price-report-1",
"name": "price_report",
"parts": [
{
"data": {
"sku": "B0CX23V2ZK",
"price": 249.0,
"currency": "USD",
"in_stock": true,
},
"mediaType": "application/json",
}
],
}
],
}
},
}

MCP calls a named function on a server, and the server returns a result. A2A sends a peer a unit of work, and the peer returns a status plus artifacts. That's why A2A tracks lifecycle state and MCP core doesn't. The table below compares the 2 message structures field by field.

Message element

MCP

A2A

Base format

JSON-RPC 2.0

JSON-RPC 2.0, gRPC, or HTTP+JSON

Example method

tools/call

SendMessage

What the caller specifies

A tool name and its arguments

A message containing typed parts

Where the payload goes

params.arguments

params.message.parts

What is returned

Typed content plus an isError flag

A task containing status and artifacts

Lifecycle state

None in core, added by the Tasks extension

Built in, across 8 task states

Long-running work

Task handle, then poll tasks/get

Stream over SSE, or call back by push notification

When work runs long, the mechanisms differ. A2A streams progress over server-sent events, or calls back through a push notification. MCP, with the Tasks extension, returns a durable task handle that the client polls with tasks/get until the task reaches a final state.

What failure looks like, and why clients miss it

Both specifications separate a malformed request from work that ran and failed. The second case is where client bugs occur. MCP marks it with an isError flag on the result and A2A puts it in the task state, so a failure the server catches is returned inside a normal response on both. Running these failure paths against both local servers produced this:

What failed

MCP 2026-07-28

A2A v1.0

Work ran and failed, handled

result with isError: true, HTTP 200

result with state TASK_STATE_FAILED, HTTP 200

Work ran and failed, uncaught

result with isError: true, reason replaced by a generic string

JSON-RPC error -32603, Internal error, reason preserved

Unknown tool or invalid arguments

result with isError: true

Not applicable

Unknown method

JSON-RPC error -32601

JSON-RPC error -32601

When a retrieval tool catches a block from the target site, the failure comes back with HTTP 200 and no JSON-RPC error anywhere:

{
"jsonrpc": "2.0",
"id": 7,
"result": {
"content": [{"type": "text", "text": "Error executing tool fetch_blocked"}],
"isError": true,
"resultType": "complete"
}
}

A live server returns the same structure. Calling scrape_as_markdown on the Decodo MCP server with an invalid URL returns this:

The Inspector shows the red banner because it reads isError. A client that checks only the JSON-RPC error field never reads isError, so it records this call as a success.

That behavior is deliberate. The specification no longer treats argument-validation failures as protocol errors, because only tool-execution errors are passed to the model, and a model that never receives an error can't correct it. Blocks and 403s aren't transport failures either. A client that branches on the JSON-RPC error field records the block as a success and passes the model an error string as though it were page content. Check isError on MCP and the task state on A2A. Some failures never produce even that signal. A target that silently drops a request, rather than rejecting it, can leave every field looking normal. Treat an unusually clean success with the same suspicion as an explicit error.

A retrieval layer that handles blocks upstream means fewer of them reach the tool boundary. The Decodo MCP server is one such layer, exposed as MCP tools. It can reduce the number of silent failures rather than eliminating them, so keep the check either way.

One detail costs debugging time, and it's a choice rather than a constraint. The official Python SDK separates a failure you anticipated from an unhandled exception. Raise its ToolError, which is defined in mcp.server.mcpserver.exceptions, and the model receives your message. Raise anything else and the model receives only Error executing tool, with the original kept server-side for the logs.

Where MCP and A2A overlap and fit together

Running MCP and A2A together is the pattern the 2 protocols are designed for, with A2A between agents and MCP inside each one.

  1. A coordinating agent receives a request.
  2. The coordinator splits it into subtasks.
  3. It delegates each subtask over A2A to a specialist agent.
  4. Each specialist calls its own MCP servers for tools and data.
  5. Each specialist returns its artifacts over A2A to the coordinator.
  6. The coordinator assembles the final answer.

An MCP tool whose implementation is an A2A call is the layered pattern in its shortest form:

import httpx
from mcp.server.mcpserver import MCPServer
PEER = "http://127.0.0.1:9999/rpc"
mcp = MCPServer(name="a2a-bridge", version="1.0.0")
@mcp.tool()
def delegate_price_lookup(sku: str) -> dict:
"""Ask the peer pricing agent for a SKU and return what it produced."""
reply = httpx.post(
PEER,
headers={"Content-Type": "application/json", "A2A-Version": "1.0"},
json={
"jsonrpc": "2.0",
"id": 1,
"method": "SendMessage",
"params": {
"message": {
"messageId": "bridge-1",
"role": "ROLE_USER",
"parts": [{"text": f"price for SKU {sku}"}],
}
},
},
timeout=20,
).json()
if "error" in reply:
raise RuntimeError(reply["error"]["message"])
task = reply["result"]["task"]
return {
"state": task["status"]["state"],
"artifact": task["artifacts"][0]["parts"][0]["data"],
}
app = mcp.streamable_http_app()

The function signature is MCP and the implementation is A2A, so a caller speaking only MCP calls a peer agent without knowing A2A exists. Run it against the companion repository's peer with a lookup that succeeds, and the task state and artifact are returned unchanged. When the peer fails, its reason doesn't survive the hand-off.

Running that arrangement end to end shows what is lost where the MCP tool hands off to the A2A peer:

One command runs an MCP tool that makes an A2A call inside the tool, then runs the same call against a peer that fails. Reproduce it from the companion repository.

The A2A peer reports target returned 403 Forbidden, and the same failure through the bridge reads Error executing tool delegate_price_lookup. The bridge above raises RuntimeError, which the SDK treats as an unhandled exception, so the diagnosis is lost at the boundary between the 2 protocols. Raising ToolError with the peer's reason in its message carries that reason through to the model. The current revision also reserves traceparent and the other W3C trace context keys in _meta, so a trace ID propagated across both hops can keep a failure correlated even when its message is lost, provided both hops forward the key.

The coordinating agent calls no tool itself, which makes a specialist's data source replaceable without changing anything above the A2A boundary, as long as the artifacts it returns keep the same structure.

BeeAI is a documented example. IBM built it on its own Agent Communication Protocol and now runs A2A between agents with MCP for tool access, so a team that had a competing protocol still adopted the layered arrangement.

Both protocols move a unit of work to something running outside your own program, so the same capability can be exposed either way. The convergence is visible in the specifications themselves, because the MCP Tasks extension defines 5 task states, workinginput_requiredcompletedfailed, and cancelled. Every one maps onto an A2A task state, and A2A adds submittedrejected, and auth_required beyond them. A2A writes these in enum form on the wire, so failed appears as TASK_STATE_FAILED. MCP core has no equivalent state, so an agent blocked by an under-scoped credential can only guess, retrying or hunting for a different credential when it should stop and ask. auth_required is the pause state a client can branch on.

MCP is governed by the Agentic AI Foundation and A2A is joining it. They won't merge soon. They solve different problems, and where they do overlap their task models already match, so there's little left for a merger to fix. Watch instead for the remaining differences to narrow. MCP has an experimental server card proposal, and shared identity work would remove the last reason to keep 2 trust models.

Cost is the reason not to use A2A for everything, though not the cost most people name. On one machine with no model inference, 300 calls each, an A2A SendMessage cost roughly 1.3x an MCP tools/call when both returned the same result in one response. Streaming the full lifecycle, 4 events from submitted to completed, cost roughly 1.7x an MCP tools/call. At this scale an HTTP client's own overhead is comparable to the difference being measured, which is the finding rather than a caveat on it. The wire isn't the main cost. The real cost is the second model's inference, another authentication boundary, and another failure mode to trace, and none of that helps when the target is a deterministic function call.

Agent communication protocols beyond MCP and A2A

Most of the remaining confusion is over A2A vs. MCP vs. ACP, because the ACP acronym now covers 2 different protocols.

The IBM Research Agent Communication Protocol was released in 2025 for the BeeAI platform, and merged into A2A under the Linux Foundation later that year. Zed Industries released the Agent Client Protocol separately in 2025, and co-develops it with JetBrains. It solves a different problem, connecting an editor or CLI to a coding agent. If a vendor offers "ACP support", ask which one they mean. Assume nothing.

2 related efforts are worth knowing about, and neither replaces either protocol. AGNTCY, a Linux Foundation project building shared agent infrastructure, covers discovery, identity, and observability. AP2, the Agent Payments Protocol, handles agent-initiated payments as an A2A extension governed by the FIDO Alliance.

Security and identity in MCP and A2A

Both protocols let an agent trigger actions somewhere else on a user's behalf. Both therefore must answer the same 3 questions: who is calling, what may they access, and what did they do.

MCP handles this by making the server an OAuth 2.1 resource server. Servers that require authorization must implement OAuth 2.0 Protected Resource Metadata (RFC 9728), so a client can discover the right authorization server without manual configuration. That requirement dates from the 2025-06-18 revision, not the stateless 2026-07-28 rework. The current revision added issuer validation under RFC 9207, which prevents a class of mix-up attack where a client exchanges an authorization code for a token at the wrong server. The same revision deprecated Dynamic Client Registration in favor of Client ID Metadata Documents, where a client's ID is a URL serving its own metadata, so there's no registration call to make.

A2A puts its trust boundary in the Agent Card. A card can be signed with JSON Web Signature (JWS), and since v1.0 the content must first be canonicalized with the JSON Canonicalization Scheme so both sides hash the same bytes. The protected header carries the algorithm, the key ID, and optionally a URL pointing at the publisher's key set. An unsigned card is a claim, and a signed card is verifiable. The card schema is also stricter than the specification text around it, marking the implicit and password OAuth flows deprecated in favor of authorization code with PKCE (Proof Key for Code Exchange), which binds a code to the client that requested it, so an intercepted code alone is useless.

Security concern

MCP approach

A2A approach

Authenticating the caller

OAuth 2.1 bearer token per request, where the server requires auth

Scheme declared in the Agent Card

Discovering the authorization server

Protected Resource Metadata, RFC 9728

Declared in the card, with no separate discovery step

Verifying the other party's identity

Server identity asserted in result metadata, not signed

JWS-signed Agent Card, optional

Limiting scope

Token audience bound to one server

Credentials and skills scoped per agent

What stays private

Server internals behind the tool schema

Peer memory, tools, and proprietary logic

Auditability

Per-request logging at the tool boundary

Task and context IDs across the chain

The identity row is the asymmetry your design has to account for. A2A gives you a way to verify who published an agent, while MCP has no equivalent signature over server identity, so an MCP client trusts whatever endpoint it's configured with. Signing doesn't tell you whether the agent works well.

The authentication row has a second asymmetry. MCP defines one authentication mechanism, so 2 servers that follow the specification accept the same bearer token flow. A2A only requires a card to declare whichever scheme its agent uses. A client calling several A2A peers may need to support a different scheme for each.

Enterprise buyers ask one question before they ask which protocol, and neither protocol answers it yet. When an agent acts for a user, on a third-party system, using a credential delegated to it, who is accountable and how is that proven? Work is converging on an answer. MCP's August 2026 roadmap names DPoP (which binds a token to a key the client holds, so a stolen token alone is useless) and Workload Identity Federation as priorities. The IETF's identity chaining draft has passed last call, the final review before publication as an RFC. OpenID published working group drafts for MCP tool authorization in 2026. None of it's settled, so today the answer is whatever your own audit log can reconstruct.

Tool descriptions are passed to the model verbatim, and the specification says to treat them as untrusted. Pattern-scanning the 2,432 tool descriptions from that 168-server sample across 6 injection classes returned 17 candidates, and reading every one manually showed all 17 were harmless. The classes were concealment, instruction override, hidden markup, data theft, file reads, and text written as a direct instruction to the model. Snyk's Agent Scan, run separately across 595 tools on 56 of those servers, reached the same conclusion. It flagged capability risk and attacker-reachable input, but no injected instruction. Its lexical hits were words like ignore and bypass appearing in ordinary parameter documentation. Across these servers, the risk is in what a tool accepts as input, not in what it hides in its description.

Both specifications leave risks unsolved. Prompt injection can be passed to an agent through tool output or another agent's artifact. Tokens get over-scoped. A chain of delegations makes it hard to determine which agent took an action. Invariant Labs demonstrated the first two together against the GitHub MCP server in 2025 and named it a toxic agent flow. A prompt hidden in a public issue caused a connected agent to use its own over-scoped token. Data was copied from a private test repository into a public pull request. The researchers were explicit. This is not a flaw in GitHub's server code. It is a property of agent systems that no server-side patch alone can fix.

None of these 4 practices prevents a failure. They limit the damage a failure causes:

  • Scope credentials per server and per agent instead of sharing one set.
  • Verify an Agent Card's signature where one exists, and treat an unsigned card as a claim you accept deliberately.
  • Treat anything a tool or peer returns as untrusted input.
  • Log at the protocol boundary in both directions, because a failed tool and an unresponsive peer look identical from the coordinator's side.

Implementation tips: integrating and migrating

Starting from nothing, build 1 agent with MCP tools before considering A2A. Find or build MCP servers for the systems it needs, scope credentials per server, and only then measure whether a second agent is worth adding.

The tool surface stays cheap only if you manage it deliberately. 2 released fixes reduce the cost that the token counts above measure. Anthropic's Tool Search Tool defers loading a server's tool schemas until the agent searches for a tool. Anthropic's own example puts 5 typical servers at about 55,000 tokens, roughly 5 times what 5 servers at the median measured here would cost. In that example the tool cuts the 55,000 by 85%, loading only the schemas the agent asks for. 

Cloudflare's Code Mode replaces an entire API's tool list with 2 functions, search and execute, backed by generated code. It reduces the Cloudflare API's own tool definitions from 1.17M tokens to about 1,000. Those 2 are client-side. A server can also scope its own surface, which needs no client support at all. The Decodo MCP server reads a TOOLSETS environment variable over stdio and a ?toolsets= parameter on its remote endpoint, and setting it to web retrieval reduces its tool count from 30 to 2. Neither client fix requires MCP itself to change, because the specification never requires loading every schema upfront. That's a client choice, and it's the first one worth revisiting once you measure what the tool schemas cost.

Code Mode makes a larger claim. Models are trained on far more code than tool-call syntax, so generating a script that calls a typed SDK can outperform emitting structured calls. If that's true, tool calling becomes a discovery and authorization layer rather than the execution path.

Is any of this stable enough to build on? Build on MCP now. Its breaking changes have simplified deployment rather than redesigned the protocol, and the deprecation policy gives a feature at least 12 months before removal. Treat A2A v1.0 as buildable but pin the version, because it renamed every method once already and turns backward compatibility off by default. 

If you're building a new MCP server now, target the 2026-07-28 revision and skip the migration. That works on the Python SDK today. On TypeScript it needs the version 2 packages, @modelcontextprotocol/server and @modelcontextprotocol/client, because the 1.x @modelcontextprotocol/sdk line supports nothing newer than the 2025-11-25 revision. Client code still has to support both the old and new revisions. If you already run MCP servers, the checklist is short:

  • List which revision each server reports.
  • Find dependencies on session IDs and sticky sessions.
  • Identify any use of the deprecated Roots, Sampling, or Logging features.
  • Confirm your SDK supports the new revision.

How you probe determines what you find. The official MCP registry listed 13,332 active remote endpoints in August 2026. A probe of 487 of them, taken at roughly every 27th position in the listing, called server/discover on each and fell back to the older initialize handshake where that failed. Every request asked for the 2026-07-28 revision, so a server that negotiates responds with the newest revision it supports.

265 produced a usable response, and they divide into 2 groups that have to be counted separately.

22 servers implemented server/discover, which the current revision requires. Of those 22, 21 list 2026-07-28 among their supported versions, and 16 list nothing else.

The other 243 responded only to the old handshake, and each negotiated down to its own maximum:

Revision negotiated

Servers

Share of 243

2025-11-25

130

53.5%

2026-07-28

53

21.8%

2025-06-18

26

10.7%

2025-03-26

18

7.4%

2024-11-05

16

6.6%

Treat the 2026-07-28 row with care. Those 53 responded to a method that the 2026-07-28 revision removed, then named 2026-07-28 as the revision they support. Each was re-tested by requesting a revision that has never existed, described below, and each rejected that request, so they aren't simply accepting whatever they're sent. A server that echoes only plausible versions would still pass that check.

The first group measures what a server advertises. The second measures what it accepts.

2 control tests support these numbers. The first compared the two probe methods: probing with initialize alone misses migrated servers, because the current revision removed that method. The second is the echo control test. A server that echoes back whatever version you requested isn't negotiating, it's just accepting, so a follow-up probe requested 1999-01-01, a revision that has never existed. 9 servers reported supporting it, and they were excluded.

Treat all of these numbers as rough rather than exact. 487 endpoints out of 13,332 is a small sample, drawn at a fixed interval through the listing rather than at random. The other 222 produced nothing usable: 131 returning 401, 48 returning a JSON-RPC error, 27 refusing the connection, and 7 timing out or crashing the client, plus the 9 dropped by the echo control test above. These numbers also change weekly, and the companion repository re-runs this probe and commits each reading, so an out-of-date figure here can be checked against a current one there.

One structural fix applies beyond any single migration: keep protocol-calling code behind a thin adapter, separate from orchestration and state. When a method gets renamed again, the change stays inside that adapter instead of affecting the rest of the agent.

Adding A2A to an existing MCP setup puts a layer above it. The MCP servers stay where they are. Orchestrating agents across tools follows the same pattern.

Live web data is a common gap, because an agent's data is only as recent as the sources it can access. That gap matters most in AI data pipelines, where the retrieval step has to keep working without a person watching it.

What to watch

4 things are worth watching rather than treating as settled.

The Tasks extension will keep changing. It already had one breaking redesign, and the notification mechanism meant to replace polling inside that extension was added only after that. Expect another revision before clients implement it.

Tool-surface mitigation is becoming standard practice, not a workaround. Tool Search Tool and Code Mode are both available now, and both were released without requiring a specification change.

Agent security is becoming its own market, separate from application security. NeuralTrust, an AI-agent security startup, raised a $20M seed round in 2026. Its own survey of more than 160 CISOs found 73% very or critically concerned about agent risk, and only 30% reporting mature safeguards.

A2A's registry gap will take the longest to resolve. Set against 13,332 MCP endpoints, the A2A Registry's 164 agents look less like an early count than a sign that agent-to-agent links are arranged directly, between teams that own both ends.

Reproducing the server survey

The revision table above came from the registry, with each endpoint probed one at a time. The script below runs the same probe on the registry's first page. The version=latest parameter matters: without it the registry returns every historical version of every server, and roughly a third of the rows are duplicates.

#!/bin/bash
# server/discover is the current method. Probing with initialize alone
# hides servers that have already migrated away from it.
curl -s "https://registry.modelcontextprotocol.io/v0.1/servers?limit=100&version=latest" |
python3 -c 'import json,sys
for row in json.load(sys.stdin)["servers"]:
m = row.get("_meta",{}).get("io.modelcontextprotocol.registry/official",{})
if m.get("status") != "active": continue
for r in row["server"].get("remotes") or []:
if r.get("type") in ("streamable-http","http"): print(r["url"])' |
sort -u |
while read -r url; do
printf "%s " "$url"
curl -s --max-time 8 -X POST "$url" \
-H 'Content-Type: application/json' \
-H 'Accept: application/json, text/event-stream' \
-H 'MCP-Protocol-Version: 2026-07-28' \
-H 'Mcp-Method: server/discover' \
-d '{"jsonrpc":"2.0","id":1,"method":"server/discover","params":{"_meta":{
"io.modelcontextprotocol/protocolVersion":"2026-07-28",
"io.modelcontextprotocol/clientCapabilities":{}}}}' |
tr -d '\n' | grep -o '"supportedVersions":\[[^]]*\]' || echo "no server/discover"
done

Servers that return no supportedVersions are probably older, so retry those with the initialize handshake, and probe again with an impossible version to distinguish echoing from negotiating. Endpoints returning 401 need an Authorization header.

Final thoughts

The 2 protocols answer different questions, and only one of them is settled. MCP has an official registry, a discovery method the current revision requires, and SDKs that already support it. A2A released v1.0 by renaming every method it had, and its directory is still small. If you're choosing today, that asymmetry decides it.

Most systems that look like they need a second agent need 1 agent with better tools. Add A2A when agents cross teams, frameworks, or vendors, or when parallel subtasks each report progress. Until then, the second agent is the thing to justify.

Next steps

Start with 1 agent and MCP tools, built against the current revision so you don't begin with a migration already due. Set 3 defaults from the first commit:

  • Check isError on every MCP result and the task state on every A2A task. Neither is optional, and neither is set by a transport error.
  • Keep protocol-calling code behind a thin adapter, separate from orchestration and state, so the next method rename stays inside one file.
  • Scope credentials per server and per agent, and log at the protocol boundary in both directions.

Then measure your own stack rather than trusting registry numbers. The numbers above describe servers that responded to a public probe, not your setup. tool_cost.py runs against the endpoints you actually connect to and returns your own context cost in tokens, compare.py runs the same job through both protocols so you can compare the failure paths directly, and benchmark.py reproduces the wire-cost figures, all of them in the companion repository. When live web data is one of those endpoints, the Decodo MCP server exposes retrieval over MCP, and the Web Scraping API covers agents that call HTTP directly.

Skip the boilerplate

Decodo's Web Scraping API handles proxies, CAPTCHAs, and anti-bot detection so your code stays short and your requests actually land.

Share article:

About the author

Justinas Tamasevicius

Director of Engineering

Justinas Tamaševičius is Director of Engineering with over two decades of expertise in software development. What started as a self-taught passion during his school years has evolved into a distinguished career spanning backend engineering, system architecture, and infrastructure development.

Connect with Justinas via LinkedIn.

All information on Decodo Blog is provided on an as is basis and for informational purposes only. We make no representation and disclaim all liability with respect to your use of any information contained on Decodo Blog or any third-party websites that may belinked therein.

Frequently asked questions

What is the difference between A2A and MCP?

MCP connects one AI application to tools, data, and APIs. A2A connects independent agents to each other. They work at different layers of the same stack rather than competing for the same job, and both are governed under the Linux Foundation.

Does an AI agent need MCP?

Yes, in most cases. Once an agent has to use anything outside its own context it needs a way to integrate, and MCP is the standard one: a single interface for tools, databases, and APIs. A direct API call still works for one fixed endpoint. An agent that only reasons over text you supply needs neither.

Can A2A replace MCP?

A2A replaces MCP only in narrow cases, and even then it rarely pays. It could work where a remote agent already has access to the system you need. But you'd run a whole reasoning layer to perform one function call, paying in latency and answers that change between runs. Use MCP for tools, A2A for agent coordination.

Does MCP still use sessions?

No, the 2026-07-28 revision removed protocol-level sessions along with the Mcp-Session-Id header and the initialize handshake. Every request carries its own protocol version and capabilities, and servers that need state issue explicit handles as tool arguments. Many deployed servers still run older revisions.

Is ACP the same as A2A?

Only one of the 2 protocols called ACP is the same as A2A. The IBM Agent Communication Protocol merged into A2A in 2025. The Zed Agent Client Protocol shares the acronym but is a separate, active project, co-developed with JetBrains to connect editors to coding agents. Ask a vendor which one they mean.

How to Set Up MCP Server: Step-by-Step Guide

Over the past year, the Model Context Protocol (MCP) has gone from a niche idea to a go-to standard for integrating LLM agents with real-world tools and data. This setup lets agents deliver smarter, context-aware responses and handle complex workflows on their own. In this guide, you'll learn how to set up the Decodo MCP server with tools like Cursor, VS Code, and Claude Desktop and supercharge your web scraping operations.

Top 10 MCPs for AI Workflows in 2026

MCP has shifted from niche adoption to widespread use, with major platforms like OpenAI, Microsoft, and Google supporting it natively. Public directories now feature thousands of MCP servers from community developers and vendors, covering everything from developer tools to business solutions.

In this guide, you'll learn what MCP is and why it matters for real-world AI agents, which 10 MCP servers are currently most useful, and how to safely choose and combine MCPs for your setup.

Claude Skills vs. MCP: What's the Difference and When To Use Each

Claude Skills and MCP both extend what Claude can do, but they solve different problems. A Skill packages knowledge and procedure into a folder Claude reads when a task calls for it. MCP is a protocol that connects Claude to live tools and data. This guide gives you plain definitions, an honest comparison, and a working example of both on the same task.

© 2018-2026 decodo.com (formerly smartproxy.com). All Rights Reserved