In this article
March 13, 2026
March 13, 2026

MCP vs. REST: What's the right way to connect AI agents to your API?

MCP went stateless in the 2026-07-28 spec. Here's what still separates MCP from a REST API, and what no longer does.

Explore with AI
Open in ChatGPT
Open in Claude
Open in Perplexity

!!Updated September 2026 for MCP spec 2026-07-28: The July 2026 spec revision removed sessions and the initialize handshake, made list results cacheable, moved routing into HTTP headers, and deprecated Dynamic Client Registration. This article has been updated to include these changes. The revision itself is covered in MCP went stateless.!!

For the past fifteen years, REST APIs have been the backbone of software integration. They're well-understood, battle-tested, and supported by a rich ecosystem of tooling. When a developer needs to integrate with your service, the workflow is familiar: read the docs, grab an API key, write some code that calls GET /users/123, parse the response, and handle errors.

But a new class of API consumer has arrived: LLM-powered agents. And these consumers need to discover what your API can do, reason about which operation to use, and maintain context across multi-step workflows, all at runtime, without a human writing integration code in advance.

This is the gap that the Model Context Protocol (MCP) fills. Originally published by Anthropic in November 2024, MCP is an open JSON-RPC-based standard that lets AI applications discover tools, resources, and prompts from external servers and invoke them over plain HTTP requests. It doesn't replace your REST API; it wraps it in a layer that AI agents can actually work with.

That last part changed recently. Until July 2026, MCP was a stateful, bidirectional protocol built around a session handshake, and "stateful vs. stateless" was the headline difference between MCP and REST. The 2026-07-28 revision retired sessions entirely. MCP is now a request/response protocol where every call is self-describing and any request can land on any server instance behind a round-robin load balancer. Which makes the comparison in this article more interesting, not less: with the architectural differences gone, what's actually left?

The question for developers building APIs today isn't "MCP or REST?" It's "Do I need both, and if so, how do they fit together?"

REST APIs: What they do well

REST APIs are general-purpose interfaces. They were designed to let any software system access another system's functionality or data, regardless of whether the client is a web browser, a mobile app, a CLI tool, or a microservice. They're stateless by design, which makes them easy to cache, load-balance, and scale horizontally. Decades of tooling exist around them: OpenAPI specifications, Swagger UI, Postman, API gateways, rate limiters, CDNs.

For traditional software clients (where a developer writes deterministic code that knows exactly which endpoint to call and what parameters to pass) REST is excellent. The API acts as an abstraction layer: the requesting application doesn't need to know the internal details of the service, just how to format requests and understand responses. A frontend engineer building a dashboard doesn't need their API to explain itself at runtime. They read the docs, write the fetch call, and move on.

REST also benefits from proven infrastructure. HTTP caching, content delivery networks, load balancers, and API gateways all speak REST natively. These techniques have demonstrated scalability over decades. When you need high-throughput, low-latency data fetching for a web application, REST remains the right tool.

Where REST falls short for AI agents

The problems emerge when the client is an LLM making autonomous decisions rather than a developer following a spec. REST wasn't created with AI or LLMs in mind, and that's fine, it wasn't supposed to be. But it means there are structural gaps when the consumer is an agent:

  • No self-description at runtime. A REST API doesn't tell you what it can do, you have to know that going in. OpenAPI specs exist, but they're static documents designed for developer tooling, not for an LLM to interrogate at runtime. If the API changes or new endpoints are added, a developer has to manually update the client code. The API itself can't say "hey, I have a new capability you might want to use."
  • No way to pause and ask. If an agent hits a REST endpoint and a required parameter is missing, or the operation needs human confirmation before it commits, the call just fails. There's no protocol-level way for the server to say "I need one more thing from the user" and have the client come back with an answer. The agent has to guess, or the developer has to build that handshake by hand in application code.
  • Every API is a snowflake. Each REST API has its own unique endpoints, parameter formats, authentication schemes, and error conventions. If an AI agent wants to use five different REST APIs, it needs five different adapters; five sets of custom integration code, each with its own assumptions and edge cases.
  • Designed for developers, not for models. REST API design principles emphasize composability, discoverability for humans, and atomicity (small endpoints that developers combine). These principles work well for human developers who read docs once, write code, and iterate cheaply. They actively hurt AI agents, who pay a token cost for every tool definition they have to reason over.

MCP: Purpose-built for AI agents

Where REST is a general-purpose interface that any software client can use, MCP was explicitly designed to integrate LLM applications with external data and tools. It standardizes patterns like providing context and invoking tools in ways that align with how AI agents actually operate.

A useful analogy: MCP is to AI applications what USB-C is to laptops. A laptop with USB-C ports can connect to any peripheral (monitors, drives, power supplies) regardless of manufacturer, because they all share a common standard. Similarly, an AI agent using MCP can connect to any MCP server (databases, code repositories, email systems, calendars) because they all speak the same protocol. It doesn't matter who built the server; the interface is identical.

MCP doesn't replace your REST API. It's a protocol layer that sits on top of your existing APIs and makes them consumable by AI agents. Your REST API handles the actual business logic. MCP provides the AI-native discovery and tool semantics on top.

The three primitives

MCP servers advertise their capabilities through three types of primitives:

  • Resources are endpoints for information retrieval. A resource handler might return data from a database, fetch a document, or list items in cloud storage. Importantly, resources are read-only or passive; they provide data but typically do not cause side effects. For example, an MCP server for a knowledge base might have a resource called searchArticles that takes a query and returns relevant articles from a corpus. The server's request handler for searchArticles would handle the JSON-RPC request by executing the search and formatting the results to send back. Resources often need to handle large data (files, big query results), so their handlers might support streaming chunks or pagination for efficiency.
  • Tools are active operations that can perform side effects or computations. A tool handler might create a new record in a database, send an email, invoke an external API, or even control something like a web browser. Tools enable the AI to act on the world (within limits). For instance, an MCP server could expose a tool sendSlackMessage that, given a channel and message, posts to Slack. The request handler for this tool would contain the logic to call Slack's API and return a success/failure result. Because tools can have effects, their handlers usually include validations and safety checks. They also define input parameters and output schema clearly, so the AI (and client) know how to use them.
  • Prompts are a bit unique: they are reusable prompt templates or workflows that the server can provide to guide interactions with the AI model. Essentially, a prompt capability might supply a pre-defined prompt or chain-of-thought that the AI can use. For example, a server could have a prompt called sqlQueryTemplate that helps an AI format a database query request consistently. The request handler for a prompt might not hit an external system at all, but instead return a carefully crafted template or even initiate a multi-step workflow involving the model.

Not every MCP server uses all three. In practice, many focus primarily on tools. But the important thing is that an AI agent can query an MCP server at runtime to discover which primitives are available and then invoke those capabilities through a uniform interface.

How discovery actually works

This is MCP's most significant departure from REST. When an MCP client wants to know what a server can do, it sends a tools/list request using JSON-RPC 2.0:

  
{
  "jsonrpc": "2.0",
  "id": 1,
  "method": "tools/list"
}
  

The server responds with a machine-readable catalog of every available tool, including its name, a natural-language description, and a full JSON Schema defining its inputs:

  
{
  "tools": [
    {
      "name": "get_weather",
      "description": "Get current weather information for a location",
      "inputSchema": {
        "type": "object",
        "properties": {
          "location": {
            "type": "string",
            "description": "City name or coordinates"
          }
        },
        "required": ["location"]
      }
    }
  ]
}
  

The same pattern works for resources/list and prompts/list. The agent retrieves the capabilities list from the server rather than from a spec a developer committed months ago. As of 2026-07-28, list responses carry ttlMs and cacheScope hints and a deterministic ordering, so clients can cache a tool catalog instead of re-fetching it on every connection. But the catalog still originates from the running server, and the server decides how long it stays valid.

The 2026-07-28 revision also added an optional server/discover RPC for clients that want protocol version and capability information up front. It's optional, a client can go straight to tools/call without any preamble.

This is still different from OpenAPI, though the gap is narrower than it was. An OpenAPI spec is a build-time artifact: it exists so a human or a code generator can produce client code ahead of time. tools/list is a runtime interaction whose output is shaped for a model to choose from: natural-language descriptions alongside JSON Schema, served by the process that will actually execute the call. Now that those responses are cacheable, a tool catalog looks more like a document with a TTL than a live conversation. The difference that remains is who the catalog is written for.

One protocol, every server

Here's what makes the standardization matter practically: every MCP server, regardless of what service or data it connects to, speaks the same protocol and follows the same patterns. The tool invocation flow is always tools/call. The discovery flow is always tools/list.

If an AI agent wants to interact with five different services (GitHub, Slack, a database, Google Calendar, and a file system) and each has an MCP server, the agent uses the exact same protocol for all five. No custom adapters, no service-specific integration code. Build once, integrate many.

With state and transport differences gone, this is now the clearest structural argument for MCP over a pile of bespoke REST integrations: not that the protocol does more, but that it's the same everywhere.

Statelessness, and what replaced sessions

Until mid-2026, MCP maintained a persistent session: the client and server exchanged an initialize handshake, advertised capabilities to each other, and carried an Mcp-Session-Id header for the life of the connection. That's gone. The initialize/initialized exchange and the session header were both retired in 2026-07-28. Every request now carries its own protocol version, client identity, and client capabilities in _meta.

  
POST /mcp HTTP/1.1
MCP-Protocol-Version: 2026-07-28
Mcp-Method: tools/call
Mcp-Name: search

{"jsonrpc":"2.0","id":1,"method":"tools/call",
 "params":{"name":"search","arguments":{"q":"otters"},
 "_meta":{"io.modelcontextprotocol/clientInfo":{"name":"my-app","version":"1.0"}}}}
  

Dropping the protocol-level session doesn't mean your application can't hold state: it means state becomes explicit. The pattern the maintainers recommend is to mint a handle from a tool and have the model pass it back as an argument on subsequent calls. The upside over transport-level sessions is that the model can see the handle, so it can reason about which context it's operating in rather than depending on invisible server-side state.

Multi Round-Trip Requests

Sessions did buy one thing that plain request/response doesn't: a server could interrupt mid-call to ask the user something. 2026-07-28 preserves that without the held-open stream, via Multi Round-Trip Requests (MRTR). A server returns resultType: "input_required" along with what it needs answered, and the client retries the original call with the answers attached in inputResponses.

MRTR replaces the server-initiated elicitation/create, sampling/createMessage, and roots/list requests that previously required a stream held open in both directions.

This is the one MCP primitive with no REST analogue at all. A REST endpoint that's missing a required field returns a 400 and the caller starts over. An MCP tool can say "I need you to confirm this destructive operation first" and resume. For agentic workflows (where the expensive failure mode is an agent confidently doing the wrong thing) that's a more meaningful difference than session state ever was.

REST vs. MCP at a glance

Dimension REST APIGeneral-purpose MCP serverAI-native
Primary consumer Developer-written code (web apps, mobile clients, microservices) LLM-powered agents that reason about which tool to call
Discovery Static docs (OpenAPI specs, Swagger UI, READMEs a developer reads once) Runtime protocol: agent sends tools/list and gets a model-facing catalog
Communication HTTP verbs on resource URLs — GET /books/123 JSON-RPC 2.0 method calls — tools/call → get_book
State Stateless, each call is isolated Stateless as of 2026-07-28; sessions and initialize removed. State passed as explicit tool-minted handles
Human-in-the-loop None at the protocol level; a missing or unconfirmed parameter is a failed request Multi Round-Trip Requests: server returns input_required, client retries with answers
Interface consistency Every API is unique: endpoints, params, errors, auth all vary Every server speaks the same protocol: build once, integrate many
Auth Anything goes: API keys, Basic, Bearer, OAuth 2.0, custom headers OAuth 2.1 + PKCE, spec-mandated. Scoped tokens, RFC 9207 issuer validation, credentials bound to their issuer. Client registration moving from DCR to CIMD
Schema OpenAPI / Swagger: developer-facing, static documentation JSON Schema per tool: model-facing, machine-readable at runtime
Error handling HTTP status codes (400, 404, 500, etc.) Two layers: protocol errors (JSON-RPC) + tool execution errors
Scaling Battle-tested (load balancers, CDNs, HTTP caching, decades of tooling) Same primitives as REST since 2026-07-28 — any instance serves any request, list results cacheable, Mcp-Method/Mcp-Name headers let gateways route and rate-limit without parsing bodies
Integration cost Custom glue code per service: N APIs × M agents = N×M adapters Standardized: one protocol for all servers, eliminates N×M problem

MCP vs. REST: Layers, not adversaries

Here's the part that gets lost in the "MCP vs. REST" framing: in most real-world implementations, MCP servers use REST APIs internally to do their actual work.

Take the GitHub MCP server as a concrete example. It exposes high-level MCP tools like repository/list as primitives that any AI agent can discover and call through the standard MCP protocol. But internally, it translates each tool invocation into the corresponding GitHub REST API request. The MCP layer handles discovery, tool semantics, and provides a uniform interface. The REST API handles the actual GitHub operations.

This pattern repeats across the ecosystem. Whether it's Stripe, Slack, Notion, or Salesforce, the MCP server is typically a thin, intelligent layer that translates between the MCP protocol and the underlying service's native REST API.

MCP and REST are not competing technologies. They're complementary layers in the AI stack. MCP provides an AI-friendly interface on top of the underlying functionality that REST APIs already deliver. Today you can find MCP servers for file systems, Google Maps, Docker, Spotify, Stripe, and a growing list of enterprise services, and nearly all of them are, under the hood, calling REST APIs.

Design for outcomes, not endpoints

The most common mistake developers make when adopting MCP is auto-converting every REST endpoint into an MCP tool. Tools like FastMCP's OpenAPI converter make this tempting: one line of code and your entire API is exposed to LLMs.

The problem is that a good REST API and a good MCP server have fundamentally different design goals.

A REST API is generous. It offers hundreds of small, composable endpoints because programmatic iteration is cheap. A human developer reads the docs once and their code can chain together get_user(), then get_orders(user_id), then get_order_details(order_id) with fast network hops. For developers, more choice is good.

When you hand this same interface to an LLM agent, you're not empowering it, you're drowning it. The agent must load every tool description into its context window, pay a token tax on each one, and reason about which of your fifty user-related tools to call. Agentic iteration is expensive in ways that programmatic iteration isn't: every extra tool costs tokens and latency on every single interaction.

A better approach: design MCP tools around what the user is trying to accomplish, not around your internal API structure.

  • Bad: Three separate tools: get_user, get_orders, get_shipments; requiring the agent to make three round trips and hold intermediate results in conversation history.
  • Good: One tool (track_order(email)) that calls all three endpoints internally and returns a complete, contextualized answer like "Order #12345 shipped via FedEx, arriving Thursday."

Same outcome, one call, designed for the actual consumer. Think of your MCP server as a user interface: same product thinking you'd apply to a UI, just for a non-human user.

With the architectural differences between MCP and REST largely gone, this is where most of the remaining quality difference between a good MCP server and a bad one now lives. It's also measurable: tool count and schema verbosity translate directly into tokens charged on every single agent turn, and past roughly thirty tools they start costing you tool selection accuracy rather than just money. We put numbers on that in what an MCP server costs you in tokens.

Authentication: MCP's opinionated approach

REST APIs support a wide range of authentication mechanisms: API keys, Basic auth, Bearer tokens, OAuth 2.0, custom headers, mutual TLS. This flexibility is both a strength (it fits any use case) and a weakness (every integration needs its own auth implementation).

MCP takes an opinionated stance: OAuth 2.1 with PKCE is the standard. The MCP authorization specification has gone through several revisions (March 2025, June 2025, November 2025, July 2026), and 2026-07-28 was largely a hardening pass; the maintainers noted that authorization is where implementers spend most of their integration time.

What changed in the latest revision:

  • Authorization servers should return the iss parameter per RFC 9207, and clients must validate it before redeeming a code. This closes an authorization-server mix-up vulnerability.
  • Client credentials are now bound to the issuer that minted them; no reuse across authorization servers.
  • Dynamic Client Registration is formally deprecated in favor of Client ID Metadata Documents (CIMD). DCR still works for backward compatibility and will be removed in a future revision. If you're deciding between them today, see CIMD vs. DCR.
  • Clients set application_type during DCR so authorization servers stop rejecting localhost redirects (the cause of most redirect_uri errors in desktop and CLI clients).

This matters for enterprise deployments. When an AI agent acts on behalf of a user, you need to know which user authorized which actions. OAuth 2.1's scoped tokens solve this: every access token declares exactly which resources and actions it authorizes, and the MCP server can verify at tool invocation time that the token has the specific scope needed for that operation.

WorkOS AuthKit supports OAuth 2.1 as a compatible authorization server for MCP applications. It handles the complexity of the evolving protocol (Client ID Metadata Documents, token validation, Protected Resource Metadata, and legacy Dynamic Client Registration) so developers can focus on building the actual tools rather than implementing an OAuth provider from scratch.

When to use what

Keep your REST API for:

  • Web and mobile applications where the client is developer-written code that knows exactly what to call.
  • High-throughput data fetching where HTTP caching, CDNs, and load balancers provide performance benefits.
  • Third-party developer integrations where human developers consume your API using SDKs and documentation.
  • Microservice communication where services call each other in well-defined patterns.

Add MCP when:

  • AI agents are your consumer. If you're building for Claude, ChatGPT, Cursor, or any LLM-powered tool, MCP gives them a way to discover and use your API without bespoke integration code.
  • You need mid-workflow confirmation. Multi Round-Trip Requests let a tool stop and ask the user before it commits (worth having any time an agent can take a destructive or expensive action on someone's behalf).
  • You want write-once integrations. Build one MCP server and it works with any MCP-compatible client; no custom adapter per model.
  • You need fine-grained authorization for agentic access. OAuth 2.1 with scoped tokens gives you the controls enterprises require.

The practical architecture

In most production setups, MCP doesn't replace your REST API, it wraps it:

  
┌──────────────────────────────────┐
│  LLM Agent (Claude, Cursor...)   │
│  ↕ JSON-RPC / MCP Protocol       │
├──────────────────────────────────┤
│  MCP Server                      │
│  - Tool discovery & catalog      │
│  - Stateless request handling    │
│  - Auth (OAuth 2.1)              │
│  - Outcome-oriented tool logic   │
│  ↕ HTTP                          │
├──────────────────────────────────┤
│  Your REST API                   │
│  - Business logic                │
│  - Data access                   │
│  - Existing infrastructure       │
└──────────────────────────────────┘
  

Your REST API continues to serve your web app, mobile app, and third-party integrations unchanged. Your MCP server sits alongside it as a new interface layer, providing the AI-native protocol that agents need.

Getting started

If you already have a REST API with an OpenAPI spec, the fastest path is to use that spec as a starting point, but resist the urge to auto-convert everything. Instead:

  1. Identify the top five workflows your users would ask an AI agent to perform with your API.
  2. Design outcome-oriented tools for those workflows. Each tool should return a complete, useful result in one call.
  3. Use your REST API as the backend. Your MCP server calls your existing endpoints internally, no need to rewrite business logic.
  4. Implement OAuth 2.1 for auth. Use an identity provider like WorkOS AuthKit that already supports MCP's authorization requirements out of the box.
  5. Test with real MCP clients. Connect to Claude Desktop, Cursor, or the MCP Inspector to verify the agent experience feels right, and confirm which spec revision your client speaks; the Tier 1 SDKs kept backward compatibility, but 2026-07-28 is a breaking change for anything that depended on session identifiers.

The SDKs are mature enough for production use. TypeScript, Python, Go, and C# are the Tier 1 SDKs and all speak 2026-07-28, with Rust in beta. FastMCP 4.0 shipped support for stateless interactivity, background tasks, and enterprise auth, and platforms like Stainless can accelerate server creation. But the most important work isn't in the tooling, it's in the design. The difference between a mediocre MCP server and a great one is the same difference between a bad UI and a good one: understanding your user. The user just happens to be an LLM.

The bottom line

REST APIs aren't going anywhere. They remain the right choice for traditional software clients, high-performance data access, and developer-facing integrations. But when the client is an LLM agent, one that needs to discover capabilities at runtime, reason about which tool to use, and sometimes stop and ask before it acts, REST alone isn't enough.

MCP provides the missing layer: runtime discovery, uniform tool semantics across every server, opinionated auth, and a way for a tool to stop and ask before it acts. What it no longer provides is a different architecture. The July 2026 revision moved MCP onto the same stateless, cacheable, header-routable footing REST has always had. That's the right outcome. The interesting differences between MCP and REST were never about transport; they were about who the interface is written for.

  • Your REST API handles the business logic.
  • MCP makes that logic accessible to the AI agents that are increasingly becoming the primary way users interact with software.

The enterprises building for this future are the ones that will be ready when agents become the default interface.

Frequently asked questions

Is MCP stateful or stateless?Stateless, as of the 2026-07-28 spec revision. Sessions and the initialize handshake were removed; each request carries its own protocol version, client identity, and capabilities.

Does MCP replace REST APIs?No. Most MCP servers call REST APIs internally. MCP is an interface layer for agent consumers, not a replacement for your business logic.

Can you cache MCP responses?Yes. tools/list, prompts/list, resources/list, and resources/read carry ttlMs and cacheScope hints.

Is Dynamic Client Registration still supported in MCP?It works, but it's formally deprecated in favor of Client ID Metadata Documents and will be removed in a future revision.

What replaced MCP sessions for multi-step workflows?Explicit handles minted by tools and passed back as arguments, plus Multi Round-Trip Requests for anything needing user input mid-call.