Skip to content
AI Solutions14 min read

MCP in Production: The Four Boundaries a Deployment Has to Get Right

Most MCP failures are not bugs in a server. They are decisions made at the wrong boundary — authorization enforced where it cannot see identity, integrity checked where it cannot detect change, state assumed where the protocol guarantees none.

  • MCP
  • Model Context Protocol
  • AI Architecture
  • AI Security
  • Production AI
Structural columns supporting a building at distinct load points, representing the four independent boundaries in an MCP deployment

Key takeaways

  • MCP is a stateless protocol in the current specification. A server must not infer context from earlier requests even on the same connection, and an open connection — STDIO included — is explicitly not a session. Anything spanning requests travels as an ordinary argument the client supplies each time.
  • That statelessness makes possession of a state handle worthless as authentication. The specification is direct: servers must verify every inbound request and must not treat holding a handle as proof of identity. Bind handles server-side to the authenticated subject.
  • Authorization is per hop and never transitive. A server must reject tokens not issued for it, and must not forward the client's token upstream. Each boundary requires its own credential, obtained separately.
  • Tool definitions are a supply chain. They are instructions placed in the model's context, they can change after approval, and a one-time review plus mutable artefacts is not a security model in any ecosystem that has tried it.
  • The four boundaries fail independently. A gateway enforcing policy cannot detect a poisoned tool description, pinned definitions say nothing about what an approved tool may reach, and a correct token says nothing about whether the caller owns the handle they passed.
Level
advanced
Time to implement
Half a day to map an existing deployment onto the four boundaries
Written for
Engineers

An MCP deployment that fails in production usually does not fail because a server was written badly. It fails because a decision landed at the wrong boundary — authorization enforced somewhere that cannot see who is calling, integrity checked somewhere that cannot detect a change, continuity assumed where the protocol explicitly guarantees none.

There are four such boundaries, they fail independently, and no single component covers more than two. This is the map, with the decisions that belong at each.

Boundary one: the protocol carries less than you think

Start here, because two of the other three boundaries inherit their shape from it. The current specification states that MCP is a stateless protocol: all the information needed to process a request is contained in the request itself, and a server processes each one independently.

“Servers MUST NOT rely on prior requests over the same connection to establish context (e.g., capabilities, protocol version, client identity). Every request supplies this metadata in its _meta field.”

— MCP specification — Statelessness

The consequences are larger than they first appear. An open connection is not a conversation — the specification says so directly, and includes STDIO processes in that. Clients may interleave unrelated requests on one transport, and a server must not treat connection identity as a proxy for conversation continuity. Protocol version and client capabilities travel in per-request metadata rather than being negotiated once.

Anything that must span requests therefore travels as an explicit identifier the client passes each time — an ordinary tool argument, indistinguishable in transit from any other string. That single fact determines the shape of boundary four and creates the vulnerability at the heart of it.

Boundary two: authorization is per hop and never transitive

The rule is short. A server must reject tokens that were not issued for it, and must not forward the token it received to anything upstream. Both halves are normative, and the second is the one teams break because forwarding is the path of least resistance.

The reasoning is worth carrying rather than memorising. A token is bound to an audience; passing it on either fails validation at a correctly built upstream or succeeds at one that is not checking — and the second outcome means a token minted for one service works at another. The security best practices document enumerates what that costs: security controls bypassed because they keyed on audience, audit trails that cannot distinguish which client acted, and trust boundaries broken such that compromising one service reaches the others.

What the specification does not define is how the server obtains its upstream credential on a user's behalf, which is the actual engineering problem and the subject of the second-hop article. The short version: three strategies exist, they differ in whose identity survives the hop, and per-user OAuth is the default for anything touching user data.

A server that fronts a third-party API is a proxy, and proxies have a documented confused-deputy exposure with precise preconditions: a static client ID at the third party, dynamic client registration for MCP clients, a consent cookie set after first authorization, and no per-client consent before forwarding. All four together let an attacker register a client with their own redirect URI and ride the existing consent cookie to receive an authorization code without the user ever seeing a screen.

The mitigation is per-client consent stored server-side and checked before the third-party flow begins — not after, and not implied by the third party's own cookie. The specification is unusually specific about the details: exact redirect URI matching rather than pattern matching, single-use state values, and the state cookie set only after consent is approved, because setting it earlier renders the consent screen decorative.

Boundary three: tool definitions are a supply chain

A tool description is not documentation. It is text placed into the model's context so the model knows when to call the tool, which makes it an instruction channel — and one the user typically sees only in summarised form while the model reads it whole.

Approval is a one-time human decision and definitions are fetched repeatedly afterwards, which is the shape of every package supply-chain attack. Pinning by hash is the accepted defence and the tool poisoning article covers what to hash — the answer is not just the description, because nested input-schema property descriptions reach the model identically and can carry the whole payload while the top-level hash stays constant.

What matters at the reference-model level is that this boundary is orthogonal to the others. A gateway enforcing perfect authorization policy has no view of whether a tool description changed. A correctly scoped token does not constrain what instructions arrive with the tool it authorises. These are different controls answering different questions, and neither substitutes for the other.

Boundary four: routing, and the handle that is not a credential

Because the protocol carries no session, a server needing continuity mints a handle and receives it back as a tool argument. That produces the most easily missed vulnerability in the whole model, and the specification names it: state handle hijacking.

“MCP servers MUST verify all inbound requests. MCP servers MUST NOT treat possession of a state handle as authentication.”

— MCP security best practices — State handle hijacking

The failure is quiet because the code looks correct. A handle arrives, it resolves to state, the operation proceeds. Nothing checks whether the caller is the principal the handle was minted for, and an attacker who guesses or obtains one operates on somebody else's data with a perfectly valid token of their own.

The mitigation is a storage-key decision rather than a new component: key state as subject plus handle, where the subject is derived from the verified token rather than supplied by the client, and reject a handle presented by any other principal. Combined with non-deterministic handle generation and expiry, guessing a handle then buys nothing.

python
def load_state(claims: dict, handle: str) -> dict:
    """Fetch state named by a client-supplied handle.

    Keyed on the authenticated subject, never on the handle alone.
    Possession of a handle is not authentication — the specification is
    explicit — and a handle arriving as an ordinary tool argument is
    exactly as forgeable as any other argument.
    """
    subject = f"{claims['iss']}|{claims['sub']}"   # from the verified token

    state = store.get(f"{subject}:{handle}")
    if state is None:
        # Do not distinguish "no such handle" from "not yours". The
        # difference is an oracle for enumerating other users' handles.
        raise NotFound("unknown handle")

    return state

Where a request lands still matters

Statelessness is a protocol property, not an implementation one. A server holding state in memory still needs the follow-up request to reach the replica holding it, so affinity remains an operational question — it simply keys on an application-level handle now rather than on a session the protocol used to supply. That, and when a gateway is justified at all, is the subject of the gateway article, whose short answer is that the threshold is a deployment property rather than a fleet size.

Reading the boundaries together

BoundaryQuestion it answersCannot tell you
ProtocolWhat travels with each request, and what does not persist between them.Whether the caller is who they claim, or whether the payload is safe.
AuthorizationWhich credential is presented at this hop, and whether it was issued for this recipient.What an approved tool will instruct the model to do.
Tool definitionWhether the instructions reaching the model are the ones somebody approved.What the tool may reach once it executes.
Routing and stateWhere a request lands, and whether the state it names belongs to the caller.Whether the tool that produced the handle was trustworthy.
The four boundaries and the question each answers. No component covers more than two, which is why deployments fail at the seams.

The right-hand column is the useful one. Almost every incident in this space comes from assuming a control at one boundary covers a question belonging to another — a gateway bought for security that cannot see tool descriptions, pinned definitions treated as proof a tool is safe to run, a validated token taken as evidence the handle beside it was the caller's.

The case that sits outside the model

Locally-run servers deserve separate treatment because their threat model is different in kind rather than degree. They are binaries executing on the user's machine with the client's privileges, and the documented attacks are correspondingly blunt: a malicious startup command in a client configuration, a payload inside the server itself, or an insecure local server reachable via DNS rebinding.

The guidance for clients offering one-click configuration is explicit — show the exact command without truncation, identify it as executing code on the user's system, and require explicit approval. For server authors, STDIO limits access to the client alone; anything on HTTP locally needs an authorization token or a restricted IPC mechanism rather than an open localhost port.

Common mistakes

  1. Designing around protocol sessions. They belong to 2025-11-25 and earlier; the current specification carries no session and says an open connection is not a conversation.
  2. Treating a state handle as proof of identity. It arrives as an ordinary tool argument and is exactly as forgeable as any other. Key state on the verified subject.
  3. Forwarding the client's token upstream. Prohibited, and it converts your server into a deputy for anyone who can reach it.
  4. Distinguishing 'no such handle' from 'not your handle' in errors. That difference enumerates other users' state.
  5. Buying a gateway for security and assuming it covers tool integrity. It sees requests, not the instructions inside tool definitions.
  6. Approving tools once. Definitions can change afterwards and nothing re-verifies them by default.
  7. Requesting broad scopes up front to avoid future prompts. It converts a per-operation permission into a standing grant and widens every subsequent compromise.
  8. Following OAuth metadata URLs supplied by a server without SSRF protection. A malicious server can point discovery at cloud metadata endpoints.

Security and performance considerations

One threat in the catalogue is worth calling out because it inverts the usual direction of suspicion: SSRF during OAuth metadata discovery. A malicious server can populate discovery URLs with internal addresses — cloud metadata endpoints, localhost services, private ranges — and a client that fetches them without validation becomes the attacker's proxy through the network perimeter. Here the client is the vulnerable party and the server is hostile, which is the reverse of how most MCP threat modelling starts.

Scope design is the other cross-cutting decision. Requesting everything up front to avoid repeated consent prompts is the natural instinct and it maximises the blast radius of any token compromise while making revocation all-or-nothing. Start with a minimal set and use the step-up flow the specification defines, accepting a little friction in exchange for a token that is worth less when stolen.

On performance, statelessness has a real cost that is easy to overlook: every request carries its own metadata and cannot rely on prior negotiation, which makes prompt and context assembly repetitive across a session-shaped workload. That interacts directly with caching — tools sit first in the cache prefix, so a tool list assembled in non-deterministic order invalidates everything after it, as covered in prompt caching done wrong.

Mapping an existing deployment

  1. Which protocol version does each server implement, and does anything in the deployment assume a session the current version does not provide?
  2. Does every server validate that inbound tokens name it as the audience, and does anything forward a received token onward?
  3. Where does the upstream credential for each third-party API come from, and whose identity does it carry?
  4. Are tool definitions pinned, and does the fingerprint cover nested input-schema descriptions rather than the top-level description alone?
  5. Is state keyed on the verified subject, and do error responses avoid distinguishing missing handles from foreign ones?
  6. If a proxy fronts a third-party authorization server, is per-client consent stored and checked before forwarding?
  7. Do clients validate OAuth discovery URLs against private ranges and non-HTTPS schemes before fetching them?
  8. For local servers, is the exact startup command shown to the user before execution?

The model is worth keeping because the specification will keep moving — it has already reversed on statelessness once — and boundaries outlive mechanisms. The questions each boundary answers stay the same even when the machinery answering them is replaced.

If you are putting MCP servers in front of real systems and want the trust boundaries settled before they become incidents, that is what our agent and MCP engineering work covers. For something already running, an architecture review maps the deployment onto these four boundaries and reports what each one is currently enforcing.

Frequently asked questions

Does MCP have sessions?
Not in the current specification, which defines MCP as a stateless protocol where all information needed to process a request is contained in that request. A server must not infer context from earlier requests even on the same connection, and an open connection — including a STDIO process — is explicitly not a conversation. Protocol-level session identifiers belong to version 2025-11-25 and earlier, which is what much existing tooling and writing still assumes.
How should state that spans multiple MCP requests be handled?
The server mints an explicit identifier that the client passes back on each request as an ordinary tool argument. Critically, that handle is not a credential — the specification states servers must verify all inbound requests and must not treat possession of a handle as authentication. Key stored state on the authenticated subject derived from the verified token, so a handle presented by a different principal resolves to nothing.
Can an MCP server forward the client's token to a third-party API?
No. A server must reject tokens not issued for it and must not pass through the token it received. Doing so bypasses controls that key on audience, destroys the audit trail by making requests appear to come from a different identity, and creates a confused deputy that anyone able to reach the server can exploit. Each hop requires its own credential obtained separately.
Does an MCP gateway make a deployment secure?
It addresses one boundary and cannot see the others. A gateway enforces policy on requests, which is genuinely useful, but it has no visibility into whether a tool description changed since approval and cannot determine whether a state handle belongs to the caller presenting it. Treating it as comprehensive security is the most common category error in MCP deployments.
What is the most overlooked MCP vulnerability?
State handle hijacking, because the code that permits it looks correct. A handle arrives as a tool argument, resolves to state, and the operation proceeds — with nothing checking that the caller is the principal the handle was minted for. It follows directly from statelessness: because the protocol carries no session, continuity travels as an ordinary argument that is exactly as forgeable as any other.

References

  1. [1]Overview and statelessness — MCP specification — Model Context Protocol, accessed 8 August 2026
  2. [2]Security Best Practices — Model Context Protocol, accessed 8 August 2026
  3. [3]Authorization Security Considerations — Model Context Protocol, accessed 8 August 2026

Revision history

  1. First published. Statelessness, per-request metadata, and the threat catalogue verified against the draft specification and its security best practices document on the same date.

Share this article

Auravon AI

Auravon AI Editorial

Newsletter

Get Practical Engineering Insights

Articles like this one, delivered to your inbox. No filler, no news roundups — just engineering practice.