What is an MCP server, and what it quietly asks you to own
An MCP server exposes tools, resources and prompts to AI clients over the Model Context Protocol. What it is, how it works, and why it is not the thing that authorizes the agent.
In short
An MCP server is a program that exposes tools, resources and prompts to an AI client over the Model Context Protocol, as JSON-RPC over stdio or HTTPS. It does not authorize the agent: under the 2026-07-28 spec a remote one is an OAuth 2.1 resource server, and a separate authorization server issues its tokens.
An MCP server is a program that exposes tools, resources and prompts to an AI client over the Model Context Protocol. The client asks what is on offer, the server answers with a typed list, and the model calls entries by name. Locally that server is a subprocess on your laptop. Remotely it is an HTTPS endpoint you host.
Every explainer on the first page of results says roughly that, and they are right. Then they stop, usually one paragraph before the question that actually blocks people: who is allowed to call it?
The moment your server stops being a subprocess on your own machine and becomes a URL, the protocol hands you a second job it does not do for you. Under the current revision an MCP server is an OAuth 2.1 resource server. It validates tokens. It does not issue them. Something else has to, and that something else is an authorization server with discovery, a consent screen and revocation.
Key takeaway
The protocol is the easy half. An MCP server validates tokens it did not issue, which means a remote one is only as shippable as the authorization server sitting behind it.
Why the protocol exists
MCP exists to stop every assistant and every data source needing a bespoke integration.
Anthropic published it on 25 November 2024 with a specification, SDKs and a set of prebuilt servers for things like Git, Postgres and Slack. In December 2025 it was donated to the Agentic AI Foundation, a directed fund under the Linux Foundation set up with Block and OpenAI, whose platinum members now include AWS, Bloomberg, Cloudflare, Google and Microsoft. The announcement of that handover cited more than 10,000 active public MCP servers.
The problem it solves is the old N times M one. Five assistants and twenty data sources is a hundred integrations if each pair is its own adapter, and twenty five if both sides agree on a protocol. Nothing about that is novel. It is the same argument that produced ODBC and the Language Server Protocol, and it works for the same reason.
How it actually works
Three roles, one wire format. A host is the application a person is using, a client is the connector inside it that holds one connection, and a server is the thing you write. Every message is JSON-RPC 2.0.
What a server exposes comes in three kinds, and the useful distinction between them is not what they contain but who decides to use them:
- Tools are model-controlled. The model picks one and calls it, which is why a tool that writes anything deserves more thought than its code suggests.
- Resources are application-controlled. The host decides what to pull in as context: file contents, a query result, git history.
- Prompts are user-controlled. They surface as slash commands or menu items that a person deliberately invokes.
Two transports are standard. With stdio the client launches your server as a subprocess and speaks newline-delimited JSON-RPC over the pipe. With Streamable HTTP every message is an HTTP POST to a single endpoint, and the reply is either a JSON object or a stream scoped to that request.
If you are reading older material, one change in the 2026-07-28 revision will
trip you up. The protocol is now stateless: there is no initialize
handshake and no protocol-level session. Each request carries the version it
expects in its metadata, the server accepts or rejects it per request, and a
mandatory server/discover RPC returns supported versions, capabilities and
identity in one go. Servers no longer initiate requests of their own.
The half that is not MCP
Almost none of a remote connection is protocol-specific. Here is the shape of one.
An agent calls your server with no token. You answer 401 with a
WWW-Authenticate header naming where your protected resource metadata lives.
That document, defined by RFC 9728, is required: the spec says an MCP server
MUST implement it. The client reads it, finds which authorization server you
trust, fetches that server's metadata, and runs an ordinary OAuth 2.1
authorization code flow with PKCE. S256 is required, and a client MUST refuse
to continue if the authorization server does not advertise support for it.
One parameter in that flow does most of the security work. RFC 8707 resource
indicators mean the client MUST send a resource parameter, set to your
server's canonical URI, in both the authorization request and the token
request, whether or not the authorization server appears to support it. That is
what binds the resulting token to you specifically.
Which sets up the two hard rules. A server MUST validate that a token's audience is itself, and MUST NOT accept or pass through a token issued for someone else. The spec calls token passthrough explicitly forbidden, and the reasoning is worth reading even if you never implement MCP. A server that forwards a caller's token upstream breaks the audit trail and bypasses whatever controls the upstream thought it had. The trust boundary becomes a hallway.
Notice what none of this is. There is no MCP-specific auth mechanism to learn. It is OAuth 2.1 with three RFCs bolted on, which is good news if you already run an authorization server and a surprise bill if you do not.
What the spec deprecated, and why your tutorial is probably wrong
This is the part that goes stale fastest, so here are the rows that change how you build, as of 28 October 2026, read from the specification's own deprecations page.
| Feature | Deprecated in | Use instead |
|---|---|---|
| HTTP+SSE transport | 2025-03-26 | Streamable HTTP |
| Dynamic Client Registration (RFC 7591) | 2026-07-28 | Client ID Metadata Documents |
| Sampling | 2026-07-28 | Call the model provider directly |
| Roots | 2026-07-28 | Tool parameters, resource URIs |
| Logging | 2026-07-28 | stderr, OpenTelemetry |
None of it has been removed yet. The four deprecated in 2026-07-28 cannot go
before the first revision dated on or after 28 July 2027. HTTP+SSE runs on a
different clock: three months after SEP-2596 reaches Final.
The registration row is the one that matters, and it is the reason to distrust most of what you will read about MCP auth. Dynamic client registration was how an agent you had never met got a client ID without a human filling in a form. Several of the pages ranking for it today still present it that way. It is now deprecated in favour of Client ID Metadata Documents, where the client is identified by a URL that resolves to its own metadata. A page that describes DCR as the way agents register is describing the 2025 spec.
When an MCP server is the wrong choice
An explainer that only lists reasons to say yes is a brochure. Four cases where the answer is no:
You control both ends. If one application calls one model and you own both, an MCP server is indirection with a spec attached. Call your own functions. MCP earns its keep when the client is something you did not write.
You need it to be local, and it is. A stdio server reading your filesystem is genuinely simple and needs no authorization at all, because the credentials come from the environment the client launched it in. The spec says so. Going remote is what adds the OAuth bill, so do not go remote by default.
Your tool descriptions come from somewhere you do not trust. The specification states plainly that descriptions of tool behaviour should be treated as untrusted unless they come from a trusted server. A description is text the model reads and acts on. A server you did not write is a channel straight into your model's instructions.
You cannot own the consent screen. The spec's security guidance walks through the confused deputy case: a static client ID at a third-party authorization server, plus a consent cookie from an earlier approval, lets an attacker register their own redirect URI and skip the screen entirely. The mitigations are per-client consent recorded per user, exact-string redirect URI matching with no wildcards, and a single-use state cookie set only after approval. All three are authorization server responsibilities. If you are not running one, you are trusting someone who is.
What this costs when you actually run it
We build the authorization half, and the SDK ships the server half as an
/mcp entry point. The numbers below are ours rather than the
specification's, and they are what that second job looks like once it is
running.
Every app built on BuildBase is an OAuth 2.0 authorization server for agents
and third-party apps. That is a separate thing from the
sign-in methods a person uses, and it never counts
as one of them. Each organization gets its own RFC 8414 metadata document,
cached for 300 seconds and invalidated whenever a client changes. It advertises
S256 as the only code challenge method and none as the first supported
token endpoint auth method, because the public clients doing the calling
authenticate with PKCE alone.
Access tokens last 1 hour. Refresh tokens for agent clients last 30 days and
rotate on every use, against 180 days for first-party app clients.
Authorization codes expire after 10 minutes. Registration accepts at most 20
redirect URIs, rejects anything that is not HTTPS or loopback, and rejects a
URI carrying a fragment or userinfo, since https://[email protected]/cb
reads as your domain to a human and as evil.test to a browser. Replaying a
consumed refresh token does not just fail, it revokes the whole token family.
One thing we got wrong first, which is usually the useful part. We originally
published scopes on the authorization server, and that was the wrong place:
scopes belong to the application that owns the data, not to a shared
authorization server that sits across all of an organization's clients and
resources. Our metadata now deliberately omits scopes_supported and the app
publishes its own RFC 9728 document instead. The admin API rejects the old
fields rather than quietly ignoring them.
And the honest one. Our implementation targets the 2025-06-18 revision, not
2026-07-28. Dynamic client registration is still our registration path where
an organization turns it on, which is the mechanism the current spec
deprecated. It works. It has until that 2027 revision, and moving to Client ID
Metadata Documents is on us.
A post that told you to check the spec revision and then skipped its own would
not be worth much.
Where this sits
An MCP server is a protocol adapter in front of things you already have. The interesting questions are almost never about the adapter.
Weighing what belongs in your own service against what belongs in a dependency? The reasoning is the same here as anywhere else in a SaaS backend. If what you actually want is for another system to be told when something changes, rather than to come and ask, you want a webhook and not this. And the part that decides what an agent may touch once it is acting as a specific person is your roles and permissions model. MCP has no opinion about that at all.