SevyDevy

Guide · AI and GenAI

MCP vs API vs Agent-to-Agent: Which Integration to Use

25 min readUpdated By Saket Bhatnagar

Researched and drafted with AI, reviewed and edited by Saket Bhatnagar.

In short

  • MCP vs API comes down to who decides each call. With a plain API (fixed requests one program sends another), your code decides. With MCP, the AI model picks from tools you list.
  • MCP, the Model Context Protocol, is an open standard for listing tools so any AI app can use them. A tool is an action the model may ask your code to run.
  • A2A, the Agent2Agent protocol, is an open standard for handing a whole job to another team's agent, a program where a model chooses its own next steps.
  • Pick a plain API when your code already knows the call. Pick MCP when the model must choose and several AI apps share the tools. Pick A2A when another owner's agent does the job.
  • Beginners can stop after the flowchart that picks between the three. Engineers get what the MCP 2026-07-28 revision changed, where MCP and A2A now overlap, and an A2A 1.0 Agent Card checked with the official SDK.

What goes wrong when an AI app uses one kind of integration for everything?

One of the connections ends up unsafe, clumsy or impossible, because the things an AI app connects to are different. MCP vs API is the wrong framing if you pick one for the whole app. Some calls are fixed, some need the model to choose, and some are whole jobs run by someone else.

Picture a common setup. A travel booking app in Goa adds an AI trip planner, where a customer types a request in plain words. The setup is made up, but the problem is real.

A customer asks for 3 nights near Candolim beach from 14 to 17 December, a flight from Delhi, and a total budget of Rs 25,000. The planner must reach three outside things to finish the trip.

  • The payment gateway, which charges the customer once the trip is final.
  • The hotel inventory API, which lists free rooms at 40 partner hotels.
  • The partner airline, which has replaced its booking desk with its own AI booking agent and will not share raw seat data.

Now try one integration style for all three. Give the model a charge_card tool, and a confused model can move money. Hard-code every hotel search, and the planner cannot handle "somewhere quiet, near the beach". Wait for an airline API, and you wait forever, because the airline offers only its agent.

So the team builds three different connections. Payment gets a plain API call, hotel search gets MCP tools, and the airline gets A2A. The rest of this guide shows why each fits, so you can make the same call on your own projects.

Which words do you need before comparing MCP, APIs and A2A?

You need six ideas: API, tool, MCP, host and client, agent, and A2A. Each gets a plain meaning here, tied to the travel app.

API. An API (application programming interface) is a fixed set of requests one program accepts from another. Many web APIs follow a style called REST: you send an HTTP request to a web address and get JSON (structured text data) back. The app's checkout code calls the gateway's charge address this way.

Tool. A tool is an action your program lets the model ask for. The model only asks; your code does the work. Example: search_rooms, which finds free rooms for given dates.

MCP. The Model Context Protocol is an open standard for offering tools and data to AI apps. A program called an MCP server lists its tools in a standard format, and any AI app that speaks MCP can use them.

Host and client. In MCP, the host is the AI app that holds the model, such as the trip planner. Inside it, a small connector called an MCP client talks to one MCP server.

Agent. An agent is a program where the model picks its own next step in a loop until a goal is done. The airline's booking agent takes "book one seat", checks fares, asks a question if needed, and finishes the booking.

A2A. The Agent2Agent protocol is an open standard for one agent to hand a job to another. Each A2A agent publishes an Agent Card: a small JSON file saying what it can do, where to reach it and how to log in.

One everyday picture covers all three, using a kirana shop (a neighbourhood grocery). A plain API is a written order slip: the shop packs exactly what the slip says. MCP is walking in and picking from labelled shelves yourself.

A2A is phoning the shopkeeper: "snacks for 20 guests, under Rs 2,000." He decides what to pack, may call back with a question, and delivers. Where the picture breaks: with MCP, the model picks, but your code still fetches every item for it.

MCP vs API vs A2A: what is the real difference?

The real difference is who decides what happens. With a plain API, your code decides every call. With MCP, the model decides which tool to call. With A2A, another agent decides how to do the whole job.

Each step down that list gives away control. In return, you handle vaguer requests and you can work with partners whose insides you never see. The table compares the three on the travel app's connections.

Plain API, MCP and A2A compared on the travel app's three connections
Plain API callMCP toolsAgent-to-agent (A2A)
Who decides the callYour codeThe model, from tools you listThe other agent decides how to do the job
What the other side isA service with fixed requestsActions your model can ask forAn independent agent with its own tools
How you learn what it offersRead the docs or OpenAPI file while codingAsk the server for its tool list at run timeFetch its Agent Card from a fixed web path
Control and predictabilityHighest: same input, same callMedium: the model may pick oddlyLowest: you see task states and the result
Where the risk sitsYour code and its secretsWhat the model can triggerWhat you send to, and accept from, another agent
Long jobs and questionsUsually one request, one reply; long jobs need your own polling (checking back) or webhooks (a call back when done)Short calls; a call can pause for input; long jobs can use the optional Tasks extensionIn the spec: task states and input required; streaming and push updates are optional
In the travel appCharging Rs 19,400Hotel search and room holdsBooking the flight with the airline's agent

On the wire, the three look more alike than you might think. MCP messages use JSON-RPC 2.0, a simple format for calling a named method with JSON inputs, sent over stdio (a program's standard input and output) or HTTP. A2A 1.0 defines three standard bindings, or ways to carry its messages: JSON-RPC, gRPC (a binary format for remote calls) and HTTP with JSON.

So the difference is not the pipe. It is who decides, and what sits on the other side.

When is a plain API call the right choice?

A plain API call is right when your own code already knows which call to make, when, and with what values. If no choice is left for the model, there is no reason to involve it.

Payment is the clearest case. Once the customer taps "Pay Rs 19,400", nothing is left to decide. You do not want a model deciding whether to charge someone, or how much.

Here is the charge call in Python with the requests library (tested on requests 2.34.2). GATEWAY_URL and PAYMENT_KEY come from your server's settings, never from a prompt. If you do not code, the paragraphs under it explain each line.

python
import requests

def charge_customer(booking, payment_token):
    response = requests.post(
        f"{GATEWAY_URL}/v1/charges",
        headers={"Authorization": f"Bearer {PAYMENT_KEY}",
                 "Idempotency-Key": booking.id},           # same key on every retry
        json={"amount_paise": booking.total_rupees * 100,  # Rs 19,400 -> 1940000
              "token": payment_token,
              "reference": booking.id},
        timeout=10,                                        # seconds; see below
    )
    response.raise_for_status()                            # 4xx or 5xx -> exception
    return response.json()["charge_id"]

The function sends one HTTP POST request. The Authorization header proves the request comes from the app. The amount goes in paise (1 rupee is 100 paise), so Rs 19,400 becomes 1,940,000.

The Idempotency-Key header is a convention many payment APIs accept. If a network error makes you retry with the same key, a gateway that supports it returns the first result instead of charging twice. As of 19 October 2026, its IETF draft has expired without becoming a standard, so check your gateway's docs.

raise_for_status turns any 4xx or 5xx reply (an HTTP error code) into a Python exception, so a declined card never looks like a success. The model appears nowhere in this function, and that is the point.

The timeout is easy to misread. timeout=10 limits connecting, and each wait between bytes of the reply, to 10 seconds. It is not a cap on the whole call.

You can check both against a local fake gateway (tests run in October 2026). A gateway that never answers raised ReadTimeout after 10.0 s. One that sent its reply in four pieces, 4 s apart, returned normally after 16.0 s.

How did the team know which address and fields to use? An engineer read the gateway's documentation while coding. Many APIs publish an OpenAPI description, a standard file listing every request and field. With a plain API, discovery happens once, at build time, in a person's head.

When should you expose tools to a model through MCP?

Expose tools through MCP when two things are true. The model must choose the action and its inputs, and more than one AI app should share the same tools. If either is missing, a simpler option may do.

Hotel search needs a choice. The customer might say "somewhere quiet, walking distance to the beach", then change the budget. Someone must turn those words into a search, read the results, and maybe search again. That is a job for the model.

Now the sharing part. Suppose the company has three AI apps: the trip planner, a support assistant and an internal tool for the bookings team. All three need hotel search. Without a shared standard, the team wires the same tools into each app three ways.

With MCP, the team builds one hotel MCP server, and each app's MCP client talks to it. The diagram follows one request. Notice that the model picks the tool, but the plain hotel API still does the real work at the bottom.

Hotel search through MCP

The model only chooses the tool; the MCP server and the plain API underneath do the work.

Diagram connections:

  • Customer's request to Trip planner app
  • Trip planner app to MCP client (Model picks a tool)
  • MCP client to Hotel MCP server (tools/list, tools/call)
  • Hotel MCP server to Hotel inventory API (Plain API call)
  • Customer's request: Quiet, near Candolim, under Rs 5,000 a night
  • Trip planner app: The MCP host; holds the model
  • MCP client: Fetches the tool list, then calls tools
  • Hotel MCP server: Offers search_rooms and hold_room
  • Hotel inventory API: Plain REST API over 40 partner hotels

Discovery works differently from a plain API. The MCP client sends a tools/list request, and the server replies with each tool's name, description and input format. If the server supports it, a client can also subscribe to a notice that the list changed, then fetch it again.

Here is a real hotel MCP server, written with the official MCP Python SDK (mcp 2.3.0). A short list stands in for the hotel API so you can run it as is. If you do not code, skip to the callout after the output.

python
from mcp.server.mcpserver import MCPServer

server = MCPServer("hotel-search")

ROOMS = [  # stand-in for the hotel inventory API
    {"room_id": "CAN-12", "area": "Candolim", "price_per_night": 4200},
    {"room_id": "CAN-31", "area": "Candolim", "price_per_night": 9500},
    {"room_id": "BAG-07", "area": "Baga", "price_per_night": 3800},
]

@server.tool(description="Find free rooms in a Goa area. Prices are rupees per night.")
def search_rooms(area: str, check_in: str, nights: int) -> list[dict]:
    rooms = [r for r in ROOMS if r["area"] == area]
    return [r for r in rooms if r["price_per_night"] <= 8000]  # hard cap

@server.tool(description="Hold a room for 15 minutes. Does not charge money.")
def hold_room(room_id: str) -> str:
    return f"Held {room_id} for 15 minutes"

if __name__ == "__main__":
    server.run()  # stdio: the AI app starts this file as a child process

MCPServer creates the server and names it. Each @server.tool line turns the function below into a tool. The description is what the model reads to decide when to use it, so keep it short and honest.

The SDK turns the type hints (labels such as nights: int) into each tool's input format, so a client knows nights must be a whole number. search_rooms drops anything over Rs 8,000 a night, a limit the model cannot change. There is no book_and_pay tool, because payment stays in checkout code.

You can check it worked. Save the server as hotel_server.py, install mcp 2.3.0 in a virtual environment, and run this small client next to it inside that environment. It starts the server as a child process and talks MCP over stdin and stdout.

python
import asyncio
from mcp import Client, StdioServerParameters

async def main():
    params = StdioServerParameters(command="python", args=["hotel_server.py"])
    async with Client(params) as client:
        listed = await client.list_tools()
        for tool in listed.tools:
            props = tool.input_schema["properties"]
            print(tool.name, {k: v["type"] for k, v in props.items()})
        result = await client.call_tool(
            "search_rooms", {"area": "Candolim", "check_in": "2026-12-14", "nights": 3})
        print(result.structured_content)

asyncio.run(main())

You should see this output. The integer type for nights came from the type hint, and the Rs 9,500 room never reaches the model.

text
search_rooms {'area': 'string', 'check_in': 'string', 'nights': 'integer'}
hold_room {'room_id': 'string'}
{'result': [{'room_id': 'CAN-12', 'area': 'Candolim', 'price_per_night': 4200}]}

When we sent nights as the word "three", the call came back as a tool error saying it should be a valid integer. The model gets that message and can retry with a number.

When do you need an agent-to-agent protocol like A2A?

You need an agent-to-agent protocol when the other side is an independent agent that takes a whole job, works through the steps on its own, and returns a result. This is common when another company owns that agent and keeps its insides private.

That is the airline's position. It will not expose its fare rules, seat maps or pricing tools. It offers one thing: its booking agent, reachable through A2A. The A2A specification describes agents working together without access to each other's internal state, memory or tools.

Here is one A2A run for the customer's flight. The web address and all details are made up.

  1. Read the Agent Card

    The planner fetches the airline's card from the fixed path /.well-known/agent-card.json. It lists a skill (a job the agent can do) called book_domestic_flight, the agent's address and its login method.

  2. Send a message

    The planner sends: book 1 economy seat, Delhi to Goa, 14 December, at most Rs 7,500. The airline replies with a task, A2A's record of one job, which has an ID and a state.

  3. Working

    The task state becomes working. The airline's agent checks fares and seats with its own private tools, which the travel app never sees.

  4. Input required

    The state changes to input required: a window seat costs Rs 350 more, does the customer want it? The planner asks the customer, who says no, and sends that answer back.

  5. Completed

    The state becomes completed. The result arrives as an artifact, the task's output, holding the booking reference and the final fare of Rs 6,800.

The diagram shows the task states a job moves through. Follow the path from the Agent Card to completed, and notice the loop through input required.

An A2A task with the airline's booking agent

Main task states only; A2A has others, such as canceled. The travel app sees states and the result, never the airline's tools.

Diagram connections:

  • Read the Agent Card to Planner sends a message
  • Planner sends a message to Working
  • Working to Input required (Needs an answer)
  • Input required to Planner answers (Question to planner)
  • Planner answers to Working (Answer sent)
  • Working to Completed (Success)
  • Working to Failed or rejected (Cannot finish)
  • Read the Agent Card: Skills, address and login method
  • Planner sends a message: 1 seat, Delhi to Goa, max Rs 7,500
  • Working: Airline agent uses its own private tools
  • Input required: Window seat for Rs 350 more?
  • Planner answers: Customer said no; the agent carries on
  • Completed: Artifact: booking reference, Rs 6,800
  • Failed or rejected: No seat, or the agent refuses the job

Back to the kirana shop. The travel app did not pick seats off the airline's shelves. It phoned the shopkeeper with a goal and a budget, answered one question, and got the booking delivered.

Here are four signs that point toward an agent-to-agent protocol.

  • Another team or company owns the other side and will not expose its raw APIs or tools.
  • The job takes several steps, and you only care about the final result.
  • The work can take minutes or hours, so you need progress updates instead of one quick reply.
  • You want to find and talk to partner agents in a standard way, through their Agent Cards.

How do APIs, MCP and A2A fit together in one system?

They fit together as layers. A2A connects agents to agents, MCP connects an agent to its tools, and plain APIs sit underneath both. The A2A project's own page calls the two protocols complementary.

Here is the travel app's full setup. Trace each arrow and read its label, because the label tells you which kind of connection it is.

The travel app's three connections in one system

One planner uses all three; notice the payment path never passes through the planner.

Diagram connections:

  • Customer in the app to Trip planner
  • Trip planner to Hotel MCP server (MCP)
  • Hotel MCP server to Hotel inventory API (Plain API)
  • Trip planner to Airline booking agent (A2A task)
  • Customer in the app to Checkout code (Taps Pay)
  • Checkout code to Payment gateway (Plain API)
  • Customer in the app: Asks for a 3-night Goa trip
  • Trip planner: The app's own agent
  • Hotel MCP server: search_rooms, hold_room
  • Hotel inventory API: 40 partner hotels
  • Airline booking agent: Owned by the airline; uses its own tools
  • Checkout code: Runs only after the customer taps Pay
  • Payment gateway: Charges Rs 19,400

Look at the payment arrows. They start from the customer tapping Pay, not from the planner. The planner can build the trip and show the total, but it has no path to the gateway at all.

The layers stack on the airline's side too. Its booking agent may use its own tools inside, perhaps through MCP, and those tools call the airline's own APIs. The travel app never needs to know.

How do you choose between an API, MCP and A2A?

Choose by asking three questions in order. Does your code already know the call? Is the other side one action or a whole job? Who owns it? Your answers lead to the simplest option that works.

Start at the top of the flowchart and follow the answer that fits your connection. Notice that A2A only appears after you have ruled out the simpler options.

Which integration should you use?

Start simple; give away control only when the job really needs it.

Diagram connections:

  • Code knows the exact call? to Plain API call (Yes)
  • Code knows the exact call? to One action or a whole job? (No, the model picks)
  • One action or a whole job? to Many AI apps need it? (One action)
  • Many AI apps need it? to MCP server (Yes)
  • Many AI apps need it? to Tools in your own code (No)
  • One action or a whole job? to Owned by another team? (Whole job)
  • Owned by another team? to Agent-to-agent (A2A) (Yes)
  • Owned by another team? to Keep it inside your agent (No)
  • Code knows the exact call?: Same call, same values, every time
  • Plain API call: Like the payment
  • One action or a whole job?: Search a room, or book a flight end to end
  • Many AI apps need it?: Or apps you do not own
  • MCP server: Like the hotel tools
  • Tools in your own code: Function calling, no protocol yet
  • Owned by another team?: And they keep its insides private
  • Agent-to-agent (A2A): Like the airline booking
  • Keep it inside your agent: A step your own team writes and runs

Run the three connections through it. Payment: the code knows the call, so it is a plain API. Hotel search: the model picks, one action at a time, and three apps need it, so it is MCP. The flight: a whole job owned by the airline, so it is A2A.

The last box, keep it inside your agent, matters too. If the company had its own flight helper, written and run by its own team, A2A would be extra ceremony. Agent-to-agent protocols earn their cost at the boundary between owners.

Beginners can stop here: you can now pick the right integration for each connection. The rest is for engineers who will build one.

Where do MCP and A2A overlap as of the MCP 2026-07-28 revision?

They overlap more than one-line summaries suggest. MCP servers can ask the user for input, through elicitation, since the 2025-06-18 revision. Long jobs got experimental tasks in 2025-11-25, now an optional extension.

So "A2A can ask questions and MCP cannot" is not a safe rule. Here is how it works today.

Pause and predict: can an MCP tool ask the customer a question halfway through a call? Yes, if the client says it supports elicitation. Under 2026-07-28, the server answers tools/call with an input-required result. The client asks the user, then retries the call with the answer. Earlier revisions used a separate request from server to client instead.

The MCP Tasks extension goes further. A server can reply with a task ID instead of a result, and the client polls it. Its statuses are working, input_required, completed, failed and cancelled, close to A2A's task states.

So where is the line? It is the shape of the other side, not the features. MCP models the far side as tools your agent calls, with your agent in charge. A2A models it as a peer agent that owns the job and publishes an Agent Card.

What changed in MCP 2026-07-28 that affects this choice

  • No more initialize handshake or sessions: every request carries its protocol version and client capabilities. A server that needs state across calls returns a handle, an ID for that state, which the model passes back as a tool argument.
  • Tool list changes now reach clients that open a subscriptions/listen stream and ask for tool list notices.
  • Long-running tasks moved out of the core protocol into the optional Tasks extension. Both client and server must opt in.
  • Server-to-user questions, such as asking for confirmation, now use the multi round-trip pattern: an input-required result, then a retry, instead of a separate server-initiated request.

Our check client above, running on mcp 2.3.0 over stdio, negotiated protocol version 2026-07-28. If you connect to an older server, the SDK's default mode first tries the new server/discover request, then falls back to the old handshake.

What an A2A 1.0 Agent Card looks like

Here is a minimal Agent Card for the airline's agent. We parsed it with the A2A Python SDK (a2a-sdk 1.2.3, protocol 1.0), and no required field was missing.

json
{
  "name": "Airline booking agent",
  "description": "Books domestic flights for partner travel apps.",
  "supportedInterfaces": [
    {"url": "https://agent.airline.example/a2a",
     "protocolBinding": "JSONRPC", "protocolVersion": "1.0"}
  ],
  "version": "2.4.0",
  "capabilities": {"streaming": true, "pushNotifications": true},
  "securitySchemes": {
    "partnerToken": {"httpAuthSecurityScheme": {"scheme": "Bearer"}}
  },
  "securityRequirements": [{"schemes": {"partnerToken": {}}}],
  "defaultInputModes": ["text/plain"],
  "defaultOutputModes": ["application/json"],
  "skills": [
    {"id": "book_domestic_flight", "name": "Book a domestic flight",
     "description": "Finds and books a seat within a fare limit.",
     "tags": ["flights", "booking"]}
  ]
}

supportedInterfaces says where to reach the agent and over which binding. securitySchemes names the login method, here a bearer token (a secret string sent with each request). securityRequirements says callers must use it. Each skill needs an id, name, description and tags.

Cards written for A2A 0.3 put the address in a top-level url field. The 1.0 types reject that field, though the Python SDK's card resolver maps it into supportedInterfaces for you. Behaviour checked on a2a-sdk 1.2.3; pin your version.

What security trade-offs come with each integration?

Each option moves the risk to a different place. With a plain API, the risk sits in your code. With MCP, it sits in what the model can trigger. With A2A, it sits in what you send to, and accept from, another company's agent.

This section names the risks so you can compare the options. Login and permissions get their own guide on authentication and authorization for AI agents.

  • Plain API: keep secrets like PAYMENT_KEY on your server, never in a prompt. The model never sees them, because it is not part of the call.
  • MCP: every tool is something the model can trigger, by mistake or because a user typed tricky text. OWASP calls giving an AI more power than its job needs excessive agency.
  • MCP: the specification's security principles say hosts must get explicit user consent before invoking any tool. The tools page adds that a human should always be able to deny a tool call. Your host app does that work, so decide which tools need a confirmation step: hold_room might, search_rooms might not. Clients must treat tool annotations, a server's hints about what a tool does, as untrusted unless the server is trusted.
  • MCP: tool results can carry tricks. A hotel review saying "ignore your rules" is data, not an instruction. Following it is called prompt injection.
  • A2A: send the other agent only what the job needs. The airline needs the traveller's name and dates, not a home address or card details.
  • A2A: treat the returned artifact as untrusted data, and check the fare is within the Rs 7,500 you asked for. An Agent Card can also carry a JWS signature (a signed JSON proof). It lets you check the card was not changed and was signed by a key you already trust.

Notice the pattern. The more control you give away, the more checks you need in your own code. The Rs 8,000 room cap and the Rs 7,500 fare check both live in the travel app, so they hold whatever the model or the airline does.

How do you explain MCP vs API vs A2A in a system design interview?

Pick per connection, not per system, and justify each choice by who decides the call and who owns the other side. Interviewers want trade-offs, not protocol names.

  1. List the connections

    Name each outside thing the AI system must reach, like the payment gateway, hotel inventory and partner airline.

  2. Ask who decides each call

    If your code can name the call in advance, use a plain API. If the model must choose, offer it as a tool.

  3. Decide when tools need a standard

    Use MCP when several AI apps, or apps you do not own, share the same tools. Otherwise, pass tools straight from your code.

  4. Use A2A at ownership boundaries

    When another team's agent does a whole job and keeps its insides private, hand it a task through A2A.

  5. Place your checks

    Keep money moves out of the model's reach, keep tools narrow, check returned results against your own limits, and log every call.

Questions people ask

Is MCP just an API?
In a loose sense, yes: MCP is a standard API for AI apps to find and call tools. The difference is who uses it. A plain API is called by code you wrote, while MCP tools are listed for a model to choose from. Most MCP servers call ordinary APIs underneath, as the hotel server does.
Does MCP replace REST APIs?
No. MCP sits on top of APIs, not instead of them. The hotel MCP server in this guide still calls the plain hotel inventory API. MCP only changes how a model finds the action and asks for it, through tools/list and tools/call.
What is the difference between MCP and A2A?
MCP connects an agent to tools, and A2A connects an agent to another agent. A tool does one action your agent asks for. An A2A agent takes a whole job, works on it its own way, may ask questions, and returns a result through task states.
Can MCP and A2A be used together?
Yes. The A2A project describes the two as complementary: A2A connects agents to each other, and MCP connects each agent to its own tools. An agent can take a job through A2A and use MCP tools to finish it.
Can an MCP tool ask the user a question in the middle of a call?
Yes, if the client supports elicitation. Elicitation, added in the 2025-06-18 revision, lets a server ask the user for input. Under 2026-07-28, the server answers tools/call with an input-required result, and the client retries with the answer. Long jobs can use the optional Tasks extension.
Do I need MCP if I have only one AI app?
Often not. You can pass tool definitions directly to your model provider with each request, which is simpler for one app. Move to MCP when several apps, or apps you do not control, need the same tools.
Which option is the cheapest to build and run?
A plain API call is usually the cheapest, because it needs no model requests and has the fewest parts. MCP adds a server and model choices. A2A adds another company's agent, partner testing and longer jobs, so use it only at a boundary between owners.

Sources and further reading

Practise this with Lala

Practise