An API is the interface a service exposes to other programs. MCP (Model Context Protocol) is an open protocol, built on JSON-RPC 2.0, that lets an AI application discover tools at runtime and call them, and most MCP servers are a thin layer over an API that already exists. So MCP vs API is rarely an either-or choice: what changes is who picks each call, your code or the model.

What is MCP vs an API?

MCP and APIs work at different layers. An API (application programming interface) defines the operations one service offers, such as listing pull requests with GET /repos/{owner}/{repo}/pulls on GitHub’s REST API. MCP defines how an AI application finds operations described as tools and invokes them, whatever service sits behind each one. In an API vs MCP comparison for a single service, the MCP server is usually the newer piece, wrapped around the API your code already calls.

What is an API?

The APIs most teams call are REST over HTTPS, with GraphQL and gRPC close behind. A developer reads the reference, writes the request, handles the errors and decides in code exactly when the call runs, so the same input produces the same request every time. Authentication is the vendor’s choice: an API key in a header, an OAuth access token, sometimes mutual TLS. Its scopes are the access control. They’re often coarser than the task needs.

What is MCP (Model Context Protocol)?

Anthropic released MCP on November 25, 2024, as “a new standard for connecting AI assistants to the systems where data lives,” with SDKs, local server support in Claude Desktop and a first set of servers for Google Drive, Slack, GitHub, Git, Postgres and Puppeteer. On December 9, 2025, Anthropic donated it to the Agentic AI Foundation, a directed fund under the Linux Foundation co-founded by Anthropic, Block and OpenAI. The current specification is revision 2026-07-28, and it’s the one this guide describes.

The spec names three roles. The host is the AI application, the client inside it holds the connection to one server, and the server offers capabilities: tools the model can execute, resources the user or model can read, and prompts a user can pick as templates. When people say MCP they mostly mean tools. Messages travel over stdio, for a server the client launches as a local subprocess, or over Streamable HTTP, for a remote server.

MCP vs API: key differences

Take one GitHub operation and reach it two ways: your code calling the REST API, and an agent calling the same operation through GitHub’s MCP server.

Calling the API from your codeCalling it through an MCP server
Who decides the callYour code, at the line where you wrote itThe model, which picks a tool and fills in the arguments
How the caller learns what existsA developer reads the docs or an OpenAPI file before writing codeThe client sends tools/list at runtime and gets a name, a description and a JSON Schema for each tool
Message formatThe vendor’s own: REST paths, GraphQL queries, gRPC methodsJSON-RPC 2.0 on every server, with tools/call for every invocation
TransportHTTPSstdio for a local server; Streamable HTTP for a remote one, where every message is a POST to one endpoint
State between requestsUsually noneNone at the protocol level since revision 2026-07-28
AuthenticationWhatever the vendor chose, usually an API key or an OAuth tokenOptional; OAuth 2.1 for HTTP servers, credentials from the environment for stdio servers
Who enforces accessThe API, through the scopes on the key or tokenThe server and the API behind it; the spec says MCP “cannot enforce these security principles at the protocol level”

The state row is the one most comparisons still get wrong by calling MCP stateful. Earlier revisions opened a session with an initialize handshake, and revision 2026-07-28 removed protocol-level sessions. A server that needs state across calls now mints a handle, such as a workflow ID, and gets it back as an ordinary tool argument on the next request. The MCP security best practices list hijacking those handles as an attack, which is one more reason a policy needs to see tool arguments as well as tool names.

Will MCP servers replace APIs?

No. An MCP server for a SaaS product calls that product’s API to do its work, so the API stays where access is checked and where actions are recorded. Comparing an MCP server vs API for the same service usually means comparing a wrapper with the thing it wraps. MCP replaces the per-service glue code that each AI application used to write, and leaves the service’s interface where it was.

Not every server wraps a web API, though. Two of the launch servers, Git and Puppeteer, drive a local program: a git checkout and a headless browser. So yes, you can use MCP without an API, because a tool can be anything the server is able to run. It’s also why the MCP security best practices warn that a local server runs with the same privileges as the client that launched it.

The opposite case is common too: services with an API and no MCP server. Our workflow orchestration provider is one. When we wanted cloud agents to read its run history, we registered it as a Formal resource and stored a read-only API key as a Native User, so agents call its plain HTTP API through the Connector without ever holding the key. The cloud agents post has the rest of that setup.

MCP vs REST API

If your own code is the client, call the REST API. You know which endpoint you need, you want the same request every time, and an MCP server in the middle adds a hop and one more thing to run. Expose an MCP server when the client is an agent that should find tools on its own, or when one integration has to work in Claude, ChatGPT, Cursor and whichever client ships next. The two can point at the same backend: a deploy script calls REST, an on-call agent calls the MCP server, and both land on the same endpoints with different credentials.

MCP vs function calling

Function calling, also called tool use, is the model’s half of the exchange. The application gives the model a list of functions with JSON Schema parameters; instead of answering in text, the model returns a structured request to call one, and the application runs it. MCP supplies that list and carries out the request. The host collects tool definitions from each server with tools/list, offers them to the model through function calling, and when the model asks for a tool, sends tools/call to the server that owns it. Without MCP, each application defines its own functions and writes custom code behind every one of them, which is the integration work MCP was built to remove.

How do you turn an API into an MCP server?

To turn an API into an MCP server, pick the operations an agent needs, describe each as a tool with a name, a description and a JSON Schema for its input, and make the server call the API when a tools/call arrives. Then choose a transport: stdio for a tool that runs next to one user, Streamable HTTP for a server several clients share. For HTTP, add authorization as the spec describes.

Generators that read an OpenAPI file and emit one tool per operation will get you a working server quickly, and the output is a draft. That’s the practical MCP vs OpenAPI difference: an OpenAPI file documents endpoints for developers and code generators, while MCP tool definitions are read by an AI model at runtime, and every tool you expose is one it can be talked into calling. An OpenAPI to MCP conversion of a large API can produce hundreds of tools. Cut the list to what the agent’s job requires, split read and write operations so they can be allowed separately, and leave destructive ones (delete, merge, transfer) out unless a person approves each call.

The model treats tool descriptions as instructions. OWASP calls instructions hidden in descriptions, parameter schemas or return values tool poisoning, and it applies whichever side you’re on: if you build and publish the server, its descriptions are yours to keep clean, and if you install someone else’s, they’re input to review before anyone’s agent can load them.

Does MCP use API keys? Authentication in MCP and APIs

MCP can use API keys, but the specification doesn’t require any authorization: it says “Authorization is OPTIONAL for MCP implementations.” A remote server that does protect itself acts as an OAuth 2.1 resource server. It publishes Protected Resource Metadata, and the client gets an access token from an authorization server and sends it as a bearer token on every HTTP request (the spec forbids putting it in the query string).

Local stdio servers are the exception. The authorization spec says implementations using stdio “SHOULD NOT follow this specification, and instead retrieve credentials from the environment.” In practice that’s an MCP API key or personal access token sitting in the client’s config file, next to the command that launches the server. OWASP’s advice on that file is short: “Never store OAuth tokens in plaintext in MCP config files or application settings.”

The spec also closes a shortcut. An MCP server must check that a token was issued for it, and servers “MUST NOT accept or transit any other tokens”, so a server can’t forward the user’s token to the API behind it. The security best practices call token passthrough explicitly forbidden. The server needs its own credential for the upstream API.

Formal’s own server shows both models side by side. Formal MCP at https://mcp.formal.ai/mcp signs in clients that support MCP authorization with OAuth. For clients that don’t, it accepts an API key in the x-api-key header, as the rest of the Formal API does, and with an API key it only reads.

Put a gateway between client and server and authentication splits into two hops: the client signs in to the gateway, and the gateway holds the credential for the server, either each user’s OAuth token or a shared key. The MCP authentication section of our gateway page walks through both sides.

What MCP changes for security

OWASP’s MCP Security Cheat Sheet puts the difference in one line: “Unlike traditional APIs where developers control every call, MCP lets LLMs decide which tools to invoke, when, and with what parameters.” An agent reads a ticket, a web page or a tool result that contains instructions, and its next tools/call can carry them out with whatever credential the server holds. Our CEO wrote that MCP “was never designed to be a security boundary”, and the spec agrees: it leaves enforcement to hosts and servers.

Four risks get sharper once the model is the one choosing:

  • Tool poisoning, ranked MCP03 in OWASP’s MCP Top 10 (still in beta). A rug pull is the delayed version: a server gets approved with clean descriptions and changes them later. Nobody feeds a REST reference to a model as instructions, so plain API clients don’t face this.
  • Over-scoped tokens. Our CISO tested an agent holding a GitHub token with read and write on contents and pull requests: it could open a pull request, and the same scopes let it merge one. GitHub’s permissions can’t separate the two, so the fix was a proxy policy that blocked the merge and allowed everything else (the permission pitfall).
  • Shadow MCP servers (MCP09): servers people add to their client without review. A local one runs with the user’s own privileges. Shadow AI detection covers finding the ones already installed.
  • Confused deputy. An MCP server that acts as a single OAuth client to a third-party API, with a static client ID, can be tricked into giving a malicious client an authorization code without the user’s consent. The security best practices require such servers to implement per-client consent.

What an API gateway sees in an MCP request

A remote MCP server receives every message as an HTTP POST to one endpoint, so a gateway that routes on path and method sees the same POST /mcp for a harmless tools/list and for a call that deletes a repository. Revision 2026-07-28 helps here. Clients must copy the JSON-RPC method into an Mcp-Method header and the tool name into Mcp-Name, so that, in the spec’s words, intermediaries “can route and inspect requests without parsing the body.” A plain API gateway can now allow or block by tool name.

The arguments are still in the body. A server can mark a parameter with x-mcp-header to mirror it into an Mcp-Param-{name} header, and the spec advises against doing that for sensitive values, so the repository a merge targets or the SQL a database tool runs stays in JSON. The body is also the source of truth: the headers are a copy that the server checks against it, and clients on earlier revisions send none of them. A rule like “this agent may merge in this repository and not that one” still needs something that parses the JSON-RPC body.

Securing API and MCP access together

The same agent often makes all three kinds of request in one task: an MCP call to Linear, a REST call to an internal service, a SQL query to Postgres. The controls are the same for each, and they belong outside the agent:

  • Tie each call to a person. The agent’s credential should say which session it is and who launched it; AI agent identity covers how the two identities fit together.
  • Allow individual tools as well as servers. Approving the GitHub server is a different decision from approving its merge tool.
  • Keep credentials on a proxy the agent can’t read, so neither a config file nor the agent’s environment holds the key.
  • Review tool definitions before a server is approved, pin the version, and review again when the descriptions change.
  • Put a person in front of destructive tools. The spec says there “SHOULD always be a human in the loop with the ability to deny tool invocations.”
  • Log every tool call with the user, server, tool and parameters; OWASP asks for “full parameters, user context, and timestamps.”
  • Decide in advance what happens when an agent reaches a server nobody approved.
  • Run local stdio servers in a sandbox with minimal default permissions, as the MCP security best practices recommend.

None of this is specific to MCP. It’s the agentic AI security approach applied to tool calls, where every choice the model makes is treated as untrusted input and the checks run on identity, credentials and the request itself.

MCP and API traffic through Formal

We built the Connector as a protocol-aware proxy for databases, servers and HTTP APIs, and MCP is one more technology on it. A server added to the Catalog is reachable at https://<server-name>.<connector-hostname>/<mcp-path>. Clients sign in to that address with Formal; the MCP gateway docs describe the rest: “The Connector adds the upstream credential, enforces policies, and logs every tool call.” The Connector validates the client’s Formal token and removes it before forwarding, then attaches the upstream credential, either the user’s linked OAuth account or a shared credential such as an API key, so the token passthrough problem from the spec doesn’t arise.

Access Controls on each server name who may use it and which tools; the Connector blocks tools/call for anything outside the list. Policies see the method, tool name and parameters of each tool call as input.mcp, which covers the argument-level rules a header-based gateway can’t. The example from the MCP resource docs blocks one GitHub tool:

package formal.v2

import future.keywords.if

default request := {"action": "allow"}

request := {"action": "block", "type": "block_with_formal_message"} if {
  input.mcp.tool_name == "list_pull_requests"
}

The Catalog has 72 server templates plus a custom option, and setup steps for over 40 clients. For servers that aren’t in it, Guardrails set the default: allow and monitor, or block, in which case the agent gets a list of approved servers to use instead and how to request the blocked one. Only the Formal Endpoint sees servers that have no Formal resource, because it intercepts traffic on the laptop or cloud agent VM. On macOS the Endpoint can also seal OAuth tokens with the Secure Enclave (the docs walk through Notion’s MCP server), so the client stores a formalsealed: value instead of a usable bearer token. Prompts and model responses are a separate stream, handled by the AI gateway.

There are limits. The gateway covers remote servers over HTTPS; local servers that a client starts over stdio don’t go through it. Our authorization server doesn’t support Dynamic Client Registration, so clients without Client ID Metadata Document support, such as Cursor and Devin, are pre-registered once. And our MCP policy fields describe tool calls. They don’t read tool descriptions, so we limit tool poisoning by reviewing a server before it enters the Catalog and allowing only the tools a job needs.

“With Formal, we can finally manage least privilege access across our entire infrastructure through a single control plane.”

— Tom Daniels, CISO @ Cursor

The MCP gateway page shows how tool calls and API requests end up under the same policies and the same logs. If you’d rather see it on your own servers, request a walkthrough.