The short answer

MCP is not competing with your API. It sits on top of it. If you already have a REST or GraphQL API, MCP is a standardized way to make that API’s capabilities discoverable and callable by an LLM at runtime, without you hand-writing a bespoke integration for every model or client that wants to use it.

The confusion is understandable because MCP is, in the strict technical sense, an API — it’s JSON-RPC 2.0 over a transport, with request/response semantics like any other. But asking “MCP or API?” is the wrong dichotomy. The real question is “who’s the consumer, and do they need to discover what’s callable at runtime, or did a human developer already read the docs and write the glue code.” We build both sides of this for a living (our own product is an MCP server wrapping an ordinary internal API), so here’s the concrete version of the answer.

What an API gives you (and what it assumes)

A REST API is a fixed, versioned contract. You publish endpoints, a human developer reads your docs or an OpenAPI spec, writes client code that calls those endpoints with the right shape of request, and handles auth, pagination, and errors the way you documented them. This works well because the human in the loop can read prose, infer intent, and write code once that keeps working until you ship a breaking change.

The assumption baked into that model is a human developer doing the integration work up front, one time, for one specific use case. Nothing about a REST API tells a caller at runtime what’s available. There’s no built-in “list what you can do” call. If you want that, you bolt on an OpenAPI/Swagger spec or an SDK, and now you’re maintaining a second artifact alongside the API itself.

That’s fine when the caller is a developer. It breaks down when the caller is a model deciding, mid-conversation, which of your 40 endpoints is relevant to what the user just asked — nothing about the endpoint list is something the model can query and reason over without a human first wiring an adapter.

What MCP adds

MCP standardizes three server-offered primitives — Tools (functions the model can execute), Resources (context/data for the model or user), and Prompts (templated workflows) — over a stateful, capability-negotiated JSON-RPC 2.0 connection. Concretely, that gives you four things a bare API doesn’t:

  • Runtime discovery. A client calls tools/list at connection time and gets back machine-readable schemas for every callable function — no hand-maintained OpenAPI doc required. The model learns your surface the moment it connects, not because a developer told it in advance.
  • A uniform calling convention. Every tool call looks the same to the model regardless of what it wraps: name in, JSON args in, structured content out. A REST API’s bespoke auth-per-endpoint, pagination-per-endpoint, error-shape-per-endpoint burden is exactly what an LLM (or the adapter code in front of it) would otherwise have to relearn per API.
  • Session continuity. The initialize handshake negotiates protocol version and capabilities once; a session ID (over Streamable HTTP, the current standard transport alongside stdio — the older HTTP+SSE transport was deprecated as of the 2025-03-26 spec revision) carries that context across every subsequent call. A REST API is stateless per request by design; anything resembling “session” is orchestration code you write yourself. Same spec-churn caveat as the auth bullet below, and here it bites harder: the 2026-07-28 release candidate removes the initialize handshake and protocol-level session ID entirely, making MCP itself stateless — if that RC lands as-is, this bullet describes the 2025-11-25 spec generation, not the next one.
  • A built-for-agents auth model. OAuth 2.1 with mandatory PKCE, RFC 8707 resource indicators binding tokens to a specific server audience, and RFC 9728 protected-resource metadata are all part of the MCP authorization spec (current revision as of July 2026: 2025-11-25; a release-candidate spec dated 2026-07-28 exists and may become current shortly after this article is published, so re-check the numbers if you’re reading this later). None of that is REST-specific — you can bolt OAuth onto a plain API too — but MCP requiring a specific, audience-bound flow means clients don’t each invent their own.

What MCP explicitly does not do: replace the underlying data layer. An MCP server is a thin translation layer in front of something that already exists — a database, a REST API, an internal RPC service. It doesn’t give you data modeling, versioning discipline, or business logic. You still need those; MCP just makes the result callable by a model without per-client glue code.

A concrete example: our own analytics MCP server

We didn’t design mcp-analytics as “an MCP-native product” from a whiteboard. We had an internal query layer (ClickHouse queries wrapped in application code) before we had an MCP server. The API came first. MCP is the layer we added on top so Claude, ChatGPT, and Cursor could call it directly instead of us building a dashboard nobody in an agent workflow would ever click into.

Concretely: our 23 MCP tools (catalog at /mcp/tools) are each a thin wrapper around a query that already existed internally — get_overview, top_pages, top_referrers, compare_periods, traffic_class_breakdown. The tool schema is what changed. When you connect Claude to our server, it calls tools/list, gets back a description and JSON schema for each of those 23 functions, and decides at runtime — based on what you actually asked — which one to call and with what arguments. Nobody hand-wired “if the user asks about referrers, call the referrers endpoint.” The model reads the tool description and figures that out itself.

Building this taught us where MCP earns its keep and where it’s just overhead. Discovery and uniform calling convention: real wins, no argument. Auth was the hardest part in practice, not because the OAuth 2.1 spec is unclear but because getting real MCP clients to actually complete the flow took a long list of client-specific workarounds — we wrote up all eleven of them in the OAuth deep dive if you’re implementing your own remote server and want to skip our week of debugging.

MCP vs API: the table

  Traditional REST/GraphQL API MCP server
Primary consumer Developer reading docs, writing integration code once LLM/agent deciding at runtime what to call
Discovery None built-in; needs OpenAPI/Swagger as a separate artifact tools/list at connect time, always in sync with the server
Calling convention Per-endpoint: bespoke auth, pagination, error shapes Uniform: name + JSON args in, structured content out
State Stateless per request Stateful session, negotiated once at initialize
Auth Whatever you choose to implement OAuth 2.1 + PKCE — the spec-defined path when a remote server requires auth (auth itself is optional)
Transport HTTP, any framing you like stdio or Streamable HTTP (spec-defined)
Replaces the data layer? Is the data layer No — wraps an existing API/DB, doesn’t replace it

When you need which

Building a fixed integration for one known consumer — a payment provider’s typed SDK, a webhook pipeline, a service-to-service call you control both ends of? A plain API (or a direct SDK call) is usually the more efficient path; MCP’s discovery and uniform-calling overhead buys you nothing when there’s no model deciding at runtime which capability to invoke.

Building something an LLM should be able to use conversationally, across multiple clients you don’t control (Claude Desktop, ChatGPT, Cursor, whatever ships next year) — that’s the case MCP was built for. Our Claude MCP setup guide covers the user-facing side if you’re evaluating whether to connect one rather than build one.

Most real systems end up with both: an API for the fixed integrations, an MCP server on top of the same backend for the agentic ones. That’s exactly our own architecture.

Where it gets fuzzy

The pedantic objection is correct and worth addressing directly: MCP is an API, in the same sense that REST and GraphQL are both APIs — a contract for a program to call another program. Nothing about MCP is anti-API. What MCP is not is a replacement category for REST/GraphQL; it’s a specific, opinionated API design pattern — one that trades an OpenAPI doc and hand-rolled auth for standardized discovery, a uniform tool-calling shape, and a mandated auth flow, all aimed at making an existing backend usable by a model instead of only by a developer with a text editor.

If you’re also getting the MCP-vs-RAG question from your team, that’s a different axis entirely — retrieval of static knowledge versus live tool access and action — and we cover that distinction separately in MCP vs RAG.