MCP gateways become enterprise security boundaries

Companies now run MCP in production beyond its original local-tool use case. The 2026 MCP roadmap identifies four enterprise concerns: audit trails, SSO authentication, gateway behavior, and configuration portability. The Enterprise-Managed Authorization extension lets organizations control MCP server access through their identity provider.

Organizations use MCP gateways to implement these controls. MintMCP centralizes server hosting, OAuth and SSO, role-based access, and audit logs. Open-source Agentgateway routes MCP requests, validates JWT and OIDC identities, calls external authorization services, and enforces CEL policies for tool access. Teams can buy this layer, deploy an open-source gateway, or build one into an internal platform.

Agentgateway architecture topology showing CLI and IDE clients, AI agents, and applications connecting through the gateway to MCP and A2A servers, APIs, agents, and model providers Agentgateway sits between clients and the agents, model providers, MCP/A2A servers, and APIs they call. Official architecture diagram.

The gateway authenticates the caller, selects an upstream backend, and evaluates tool access. What happens when the backend selection and authorization decision point to different connections?

Agentgateway versions before v1.4.0 had a cross-route session reuse vulnerability. A cached MCP session restored the upstream connection from one route. The current route supplied the authorization context. I found this split in the session handling code and built a two-route reproduction to confirm the impact.

One session, two security contexts

To reproduce the mismatch, I configured two routes and two MCP backends on a vulnerable pre-v1.4.0 Agentgateway build:

Agentgateway Raw Configuration showing the sensitive route with deny true and the permissive route pointing to a public backend The intended boundary: /sensitive denies every MCP tool call; /permissive reaches a separate public backend.

/sensitive pointed to a local MCP server with an export_customer_records tool. Its authorization policy denied every tool call. /permissive allowed tool calls and pointed to a separate public server. Only the sensitive backend exposed export_customer_records.

I initialized a session on /sensitive. Agentgateway returned Mcp-Session-Id: SID-A. Agentgateway applies mcpAuthorization to MCP resources and tool calls. Session initialization falls outside that policy.

The control call returned the expected result. export_customer_records on /sensitive returned HTTP 400 with Unknown tool. The policy blocked access to the tool on that route.

I sent the same JSON-RPC request with the same session ID and changed only the URL:

session created on: POST /sensitive  + Mcp-Session-Id: SID-A
session reused on:  POST /permissive + Mcp-Session-Id: SID-A

The second request returned HTTP 200:

CONFIDENTIAL: customer_id=1842, plan=enterprise

The sensitive backend returned a synthetic customer record through the reused session.

Live terminal reproduction with a red line connecting the reused session ID: the sensitive route returns HTTP 400, while the permissive route returns HTTP 200 and confidential data Live reproduction. The public backend’s tools/call counter stayed at zero; the response came from the sensitive backend.

The public server’s tools/call counter stayed at zero. Agentgateway matched /permissive and applied its policy, then resumed the upstream connection created through /sensitive.

Where Agentgateway split the request

Agentgateway used one session manager for all MCP routes. The cache recovered the original upstream connection from the supplied ID. The session entry omitted the backend identity. The gateway accepted the cached connection without checking it against the backend selected by the new route.

For the reused request, Agentgateway supplied authorization inputs from /permissive. The relay kept the upstream connection already stored in SID-A.

That produced this pairing:

authorization policy = /permissive
upstream backend      = /sensitive

The read operation disclosed data. A state-changing tool could use the same authorization mismatch to modify data.

I reported the issue to Agentgateway. The maintainers published GHSA-mvgg-jvj2-4frq and rated the issue High with a CVSS score of 8.1.

The fix: bind each session to its backend

The fix adds the missing backend identity to the cached session. Agentgateway v1.4.0 stores a backend_id in each session entry. When another route presents that session, the gateway compares the selected backend with the stored identity and rejects a mismatch.

Agentgateway added backend_id to the session entry and rejects mismatches in the backend check. The maintainers added a cross-backend regression test, published the advisory, and documented the issue in the v1.4.0 release. The commit title states the change: mcp: pin session to backend.

MCP 2026-07-28 moves to a stateless core

The Agentgateway bug illustrates the “gateway behavior” concern in the 2026 MCP roadmap. In MCP 2025-11-25, initialize set the protocol version, client capabilities, and client information. The server returned an Mcp-Session-Id, and later tool calls carried that identifier:

initialize -> Mcp-Session-Id -> tools/call

The later request required the gateway to recover the session state and preserve each security binding. Agentgateway restored the upstream connection and chose authorization inputs from the new route. One identifier represented two security contexts.

MCP 2026-07-28 replaces that flow with a self-contained request. MCP-Protocol-Version, Mcp-Method, and Mcp-Name give gateways the protocol version and operation. The client sends its information and capabilities in _meta. The server/discover method returns server capabilities on demand:

tools/call + MCP-Protocol-Version + Mcp-Method + Mcp-Name + _meta

Any compatible server instance can process the call. Deployments can use round-robin load balancing without sticky sessions or a shared protocol session store. An MCP gateway can route on the new headers. Servers reject requests when those headers disagree with the JSON-RPC body. Clients that use MCP 2026-07-28 send no protocol session.

Applications carry state through explicit handles. A browser tool can return a browser_id. Tasks can return a taskId. Later calls pass the handle in a tool argument. SEP-2567 defines sessionless MCP through explicit state handles. The server receives the handle and authorization context in the same call.

MCP 2026-07-28 also strengthens core authorization. Clients validate the authorization server issuer and bind registered credentials to that issuer. The Enterprise-Managed Authorization extension lets corporate identity providers control server access.

The AAIF migration guide maps the old sessionful flow to self-contained requests and explicit application state.

Agentgateway v1.4.0 checks backend_id when it resumes a sessionful client. MCP 2026-07-28 removes the protocol session for new clients. Explicit application handles carry the remaining state.

The Agentgateway fix closes the vulnerability I reported in current deployments. The MCP protocol update removes the same hidden state from future clients. Thanks to Howard John and the Agentgateway maintainers for investigating the report, publishing the advisory, and shipping the fix.