MCP vs API: what changes when the caller is a model
An MCP server and a REST API are not competing technologies, because in practice one wraps the other: the API does the work, and the MCP server translates it into a form a language model can discover and call. The genuine difference is the consumer. A REST API is designed for a developer who reads documentation at build time and writes code against it. MCP is designed for a model that reads tool schemas at run time and decides, call by call, what to invoke. Almost everything that separates the two follows from that one change of audience.
We say this as a team that operates a scoring engine which connects to every server in the official MCP registry, reads its tool schemas, and probes its transport and authentication. The comparison below comes from the current specification (revision 2026-07-28) and from what we observe probing servers daily, not from paraphrasing other comparison posts.
| REST/HTTP API | MCP server | |
|---|---|---|
| Consumer | A developer, at build time | A language model, at run time |
| Contract | Documentation and OpenAPI specs, read by humans | tools/list schemas and descriptions, read by the model |
| Discovery | Manual: read the docs, hardcode endpoints | Runtime: server/discover and tools/list, negotiated per request |
| Wire format | HTTP verbs and paths; body shape is yours | JSON-RPC 2.0 over stdio or Streamable HTTP |
| State | Stateless; state carried in resource IDs | Stateless since 2026-07-28; state carried in explicit handles |
| Auth | Whatever you build: API keys, OAuth, mTLS | Optional in the spec; a protected remote server uses OAuth 2.1 with protected resource metadata (RFC 9728) |
| Errors | Status codes, interpreted by client code | Tool execution errors written so the model can retry and self-correct |
| Cost of a bad description | A developer wastes an afternoon | The model calls the wrong tool, or obeys injected instructions |
Does MCP replace a REST API?
No. An MCP server is almost always an adapter over an API that already exists. A GitHub MCP server calls the GitHub REST API. A Stripe MCP server calls Stripe’s API. A Postgres MCP server speaks the Postgres wire protocol. If the underlying API goes down, the MCP server goes down with it. MCP standardises the last hop between an LLM application and a capability; it does not provide the capability.
The shape of the official registry reflects this. As of 29 July 2026, the registry held 20,692 components: 10,895 are packages (npm, PyPI, OCI and similar) that run on your machine and reach out to some service, and 9,797 are remote endpoints, which are typically a hosted adapter in front of the same vendor API you could call directly.
This is also why “MCP vs REST” is subtly the wrong frame. REST is an architectural style for APIs. MCP is a protocol: a fixed set of JSON-RPC 2.0 methods (tools/list, tools/call, resources/read and so on) that most often sits in front of a REST API. The specification describes servers plainly as services “that provide context and capabilities” to LLM applications. Nothing in it replaces your API; it assumes you have one.
What is actually different, then?
Three things change when the consumer of your interface is a model rather than a developer.
Discovery moves to run time. A REST integration is fixed when the code ships: someone read your docs, chose endpoints, and wrote calls against them. An MCP client instead sends tools/list and receives every tool’s name, description and JSON Schema on the spot. Since the 2026-07-28 revision, a client can also call server/discover to learn a server’s supported protocol versions and capabilities before any other request. Add a tool to your server tonight and every connected agent can see it tomorrow, with no SDK regeneration and no client release.
The schema’s prose becomes as important as its correctness. With a REST API, a vague docs page wastes a developer’s time, but the code they eventually write is still precise. With MCP there is no developer at call time. The model chooses a tool by reading its description and fills in arguments from its inputSchema, so woolly wording does not merely confuse, it misroutes calls. The tools specification even asks servers to return tools in a deterministic order so clients can cache the list into model context and hit prompt caches. Your API documentation has, quite literally, become part of a prompt.
Errors are written for self-correction. A REST API returns a 400 and expects client code (written by a human who read the docs) to handle it. MCP distinguishes protocol errors from tool execution errors, and asks servers to make the latter actionable (“invalid departure date: must be in the future”) precisely so the model can adjust its arguments and retry without a human in the loop.
Is MCP stateful? Not any more
Most of the comparison articles ranking for this query list statefulness as the difference: APIs are stateless, MCP maintains a session with context carried across requests. That described earlier protocol revisions, and it is now wrong. The 2026-07-28 revision removed protocol-level sessions and the Mcp-Session-Id header entirely, and removed the initialize handshake with them. Every request now carries its own protocol version and client capabilities in _meta, and a server that needs state across calls mints an explicit handle and accepts it back as an ordinary tool argument, a basket ID, a transaction token.
Which is exactly how a well-designed REST API has always handled state: identifiers in the request, not memory in the connection. The axis that supposedly separated the two protocols now reads the same on both sides, and it changed less than a month before this post was written, so treat any comparison that leads with “MCP is stateful” as describing 2025. We covered what the revision changed, and how it moved our own scores, in our 2026-07-28 support post.
Is MCP slower than an API?
On the wire, not meaningfully. MCP is JSON-RPC 2.0 carried over stdio or HTTP, so a tools/call to a remote server costs about what an HTTP request to the API behind it costs, plus that server’s own hop to the upstream API. If you are comparing raw request latency, you are comparing the same network.
The latency that actually shows up is the model’s, not the network’s. Calling an API from code runs at code speed: a loop can fire a hundred requests without anything thinking in between. An agent calling MCP tools pays an inference pass to decide each call and another to read each result, so a five-step task is five model round trips rather than one function that runs five times. Anthropic’s own engineering write-up on code execution with MCP makes the related point about context: clients that load every tool definition upfront can spend enormous numbers of tokens before the first request, and their worked two-step example dropped from 150,000 tokens to 2,000 when the agent called the tools from code instead. Tokens are not milliseconds, but on an agent workload they are the bill that grows.
Two things in the current revision cut the fixed costs. Removing the initialize handshake means a cold call no longer pays a round trip before it can do anything, which is most of why a stateless server runs happily on serverless infrastructure. And tools/list results now carry ttlMs and cacheScope, so a client can stop re-fetching definitions it already holds. Neither changes the per-call inference cost, which is inherent to letting a model decide what to call.
What does wrapping an API in MCP do to your security?
This is the part generic comparisons skip, and it is most of what our scoring engine exists to measure. Putting an MCP server between an agent and an API changes who holds your credentials and what can influence the model.
Your credential moves. Call an API directly and the key lives in your code or secret manager, and requests flow from your process to the vendor. Put a local MCP server in between and that key now sits in the server’s configuration, with every call passing through code you did not write. Use a remote MCP server and your key, or an OAuth grant, is now held by whoever operates that server: a party that simply did not exist in the direct integration. Each hop enlarges the blast radius of a compromise.
Tool descriptions are an injection surface. The model reads every description a server returns from tools/list. The specification itself warns that clients “MUST consider tool annotations to be untrusted unless they come from trusted servers”, and its security principles say the same of tool behaviour descriptions generally. Instructions hidden in a description or a tool result are a form of indirect prompt injection, the top entry in the OWASP GenAI risk list (LLM01). API documentation has never been able to execute anything. A tool description effectively can, because the model treats what it reads as context for its next action.
You have added a dependency. A wrapper fetched from npm or PyPI carries ordinary supply-chain risk on top of everything above: typosquats, hijacked maintainer accounts, install scripts. The 10,895 package components in the registry are, from a supply-chain point of view, 10,895 more dependencies the ecosystem is being asked to trust.
Authentication on remote servers is far from universal. As of 29 July 2026, 1,201 of 9,797 remote components in the official registry required authentication when we probed them. Many of the rest serve public, read-only data and are fine as they are, but the number is worth knowing before you assume the remote server you are about to connect gates access to anything.
Should you build an MCP server or just an API?
Build the API first, almost always. An MCP server over a well-designed API is a thin adapter, often days of work, and the API keeps serving every consumer that is not an agent: dashboards, mobile apps, partners, your own test suite. An MCP-only capability serves none of them. The exception is a capability that is local by nature, such as filesystem access, a desktop application, or a developer tool, where a stdio MCP server is the natural interface and there is no HTTP API worth building underneath it.
When you do write the wrapper, resist mirroring your API one endpoint per tool. A model choosing between forty near-identical tool schemas selects worse than one choosing between eight task-shaped ones, and every schema you expose is context you are asking the client to spend. Write each description as an instruction to the model, not as marketing. And if the server is remote, require authentication from the first deploy; our guide to adding OAuth to a remote server covers the RFC 9728 metadata that modern clients expect.
The other side of that decision is choosing which of the other 19,048 servers in the registry you are willing to connect, and that is the question VerifyMCP exists for. We probe each one, read its schemas, check its transport, TLS and auth, inspect its package supply-chain signals, and publish a 0–100 score with the evidence attached. Scores are independent estimates rather than guarantees, we mark unverifiable signals as inconclusive instead of guessing, and we take no payment to raise a score. Start with the server you were about to install: look it up in the registry, and read how the scoring works so you know exactly what the number does and does not claim.