DEV Community

Cover image for MCP vs. API Explained: Do We Still Need APIs After MCP?
Sneha M K
Sneha M K

Posted on

MCP vs. API Explained: Do We Still Need APIs After MCP?

No. MCP doesn't replace APIs — it sits on top of them. An MCP server is, almost always, a thin adapter that calls a REST/GraphQL/gRPC API underneath and translates it into a shape an LLM can safely discover and call. If you're building anything that isn't talking to an AI model, you still reach for a normal API. If an AI agent is the caller, MCP is very likely the better interface. Here's the actual technical breakdown.

The confusion is understandable

Since Anthropic open-sourced the Model Context Protocol in November 2024, it's been described as "USB-C for AI applications," gotten adopted by OpenAI, Google DeepMind, and Microsoft, and spawned a whole ecosystem — over 5,500 servers listed on the PulseMCP registry, with remote MCP server deployments up roughly 4x since May 2025 alone. When something grows that fast, "does this replace X" questions are inevitable. So let's actually answer it, at the protocol level.

What an API is, quickly

An API (usually REST these days) is a contract: a set of HTTP endpoints, request/response shapes, and auth mechanisms that let one piece of software call another. You read the docs, you write code that constructs a request to a known URL with known parameters, you parse a known response shape. The caller is a human developer writing deterministic code that will run the same way every time.

GET https://api.weatherapi.com/v1/current.json?key=API_KEY&q=Delhi

That's it. You know exactly what you're calling, you handle the API key yourself, and the code either runs correctly or throws an error you wrote a handler for.

What MCP actually is

MCP is a client-server protocol, communicating over JSON-RPC 2.0, built specifically for the case where the caller is an LLM, not a human writing deterministic code. It defines three roles:

Host — the AI application itself (Claude, an IDE, your own agent runtime)
Client — created by the host, one per server it talks to
Server — exposes tools (callable functions), resources (readable content), and prompts (reusable templates), each described in natural language plus a JSON Schema so a model can understand what it does without a human reading documentation first

Wrap that same weather lookup as an MCP tool and it looks like this:

python
from mcp. server.fastmcp import FastMCP

mcp = FastMCP("weather")

@mcp.tool()
def get_weather(city: str) -> str:
"""Get the current weather for a city."""
# The API key and request-building logic live here,
# never in the model's context
return fetch_weather_api(city)

Nothing magic happened to the weather API. It's the same HTTP call. What changed is who's allowed to see what: the model gets a name, a description, and a schema — not a raw API key it could leak, misuse, or hallucinate the wrong parameters for.

Show Image

The actual differences, side by side
Traditional API MCP
Built for A developer who reads docs and writes fixed code An LLM that discovers capabilities at runtime
Discovery Manual — you read documentation Automatic — tools/list returns every tool's name, description, and schema
Interface shape Varies wildly per API (REST, GraphQL, SOAP, gRPC…) Uniform — every MCP server speaks the same JSON-RPC patterns
Credentials Caller handles auth directly (API keys, OAuth tokens) Server holds credentials; the model never sees them
Execution model Caller writes exact, deterministic logic Model chooses which tool to call and with what arguments, non-deterministically
Governance Rate limiting, auth at the endpoint Per-tool validation, input restriction, logging built into the pattern
Transport Usually plain HTTP, cacheable, CDN-friendly JSON-RPC over stdio or HTTP; historically stateful (changing — more below)

The short version: REST optimizes for a human who already knows what they want to call. MCP optimizes for a model that has to figure out what's available and how to call it correctly, safely, on the fly.

MCP is evolving fast — worth knowing where it stands right now

The spec isn't frozen. The 2026-07-28 release made the protocol stateless at the wire level — the old initialize/initialized handshake and sticky Mcp-Session-Id are gone, so requests can now route to any server instance behind a normal load balancer instead of pinning to one. That release also added an extensions framework with formal governance, MCP Apps (servers shipping actual interactive HTML UI rendered in a sandboxed iframe, not just text), and six authorization updates aligning MCP with OAuth 2.0 and OIDC. This is a protocol still actively fixing its own early design mistakes, not a finished spec everyone's just implementing.

The part worth being honest about

MCP has real, documented problems in production right now, and a dev audience deserves the unvarnished version, not just the pitch:

Context bloat. Some implementations dump entire tool schemas into the model's context on every turn regardless of relevance — one reported GitHub MCP server burns roughly 50,000 tokens just initializing, and a database server with over a hundred tools has been measured wasting up to 81% of the available context window before a single user query runs.

Security. Independent testing found command injection flaws in 43% of tested MCP implementations, and scans have turned up close to 2,000 internet-exposed MCP servers with zero authentication. There have been real CVEs — a 9.6-severity flaw in mcp-remote, an RCE in Anthropic's own Inspector tool. The protocol's original design conflating the resource server and authorization server didn't help.

Reliability. Because early MCP was stateful, a crashed server could take down an entire session rather than failing one request — production implementations from major vendors have reportedly hung or cascaded on basic scenarios.

None of that means "don't use MCP." It means treat it like the young, fast-moving protocol it is: pin versions, scope tool permissions tightly, don't expose an MCP server to the internet without real auth, and don't reach for it reflexively where a normal API call would do the job with far less surface area.

So: when do you actually reach for which?

Keep building a regular API when:

The caller is deterministic code you or another team controls
You need HTTP-layer performance — caching, CDNs, load balancers, the whole stack that REST gets for free
You're shipping a public developer platform with SDKs and docs (developers still want to read docs and write typed clients, not rely on a model choosing tools for them)
Service-to-service communication inside your own backend

Add an MCP server on top when:

The caller is an LLM or an agent that needs to decide, at runtime, which capability to use
You want that capability discoverable without a human reading your docs first
You need the model to have restricted, auditable access to a system rather than a raw credential
You're building for Claude, Claude Code, an IDE assistant, or any other MCP host, and want your service to show up as a first-class tool there

In practice, almost every real MCP server you'll build or use is a wrapper: REST (or a DB driver, or a filesystem call) underneath, MCP as the adapter layer that makes it legible to a model. You're not choosing one or the other. You're deciding whether you need the adapter — and the answer is yes exactly when an AI agent, not a human developer, is the one making the call.

Top comments (1)

Collapse
 
mthburnsbarberweb profile image
mthburnsbarber-web

"The caller is an LLM that needs to figure out what's available and how to call it correctly, safely, on the fly" — that sentence is worth bookmarking as the one-line answer to the MCP-vs-API question.

The 50K token initialization cost for the GitHub MCP server is the number to put in front of anyone building an MCP server without measuring context load first. That's a quarter of some context windows gone before the first user query. The stat about 81% context waste for a 100+ tool database server is even wilder.

The framing of APIs as "I know what I want" vs MCP as "figure it out dynamically" is exactly right, and it's why they coexist rather than one replacing the other. Different callers, different contracts.