Omnafy

August 6, 2026

What is an MCP Gateway?

An MCP gateway is a server that sits between MCP clients and MCP servers, speaks MCP in both directions, and applies policy to every request that passes through it. Clients connect to one endpoint instead of a dozen, and the gateway decides what gets forwarded, to which upstream server, under which identity, and writes down what happened.

How it works

The mechanism is short. A client is configured with a single MCP endpoint, the gateway's. The gateway calls tools/list against each upstream server it knows about, merges the results, namespaces them so two servers can both have a search tool (github__search, jira__search), and returns one combined list. That prefixing is not just convention; the spec tells aggregating proxies they should implement a disambiguation strategy along exactly those lines. When the client sends tools/call for github__create_issue, the gateway strips the prefix, checks policy, attaches the credential for that upstream, forwards the call, and returns the result.

Everything a gateway is good for happens at that policy check. Allow or deny by tool name. Inspect arguments and reject the ones that violate a rule. Rate limit per identity. Redact patterns out of responses on the way back. Write one audit record per call, with the caller, the tool, the arguments, and the outcome.

The difference between an MCP server and an MCP gateway

The confusing part is that a gateway is an MCP server. From the client's side of the wire it is indistinguishable from one: it answers tools/list, handles tools/call, and advertises its capabilities through server/discover in protocol version 2026-07-28. From the upstream side, it is an MCP client. A gateway is both roles in one process, which is why the two words get used interchangeably and why doing so obscures the actual distinction.

The distinction is who owns the work. An MCP server implements tools. The GitHub MCP server contains the code that knows create_issue means POST /repos/{owner}/{repo}/issues, knows the shape of the response, and knows what to do when GitHub returns a 422. A gateway implements no tools of its own. It owns routing, identity, policy, and logs, and it is useless without servers behind it.

Three consequences follow from that, and they are what you should actually reason about when deciding which one you need.

Credentials work differently. The specification forbids token passthrough: an MCP server must not accept tokens that were not issued for it, and must not forward the token it received from the client to a downstream API. A correctly built gateway is an OAuth 2.1 resource server in its own right. It validates the client's token against its own audience, then presents a separate credential to each upstream server. Passing the client's token straight through is the anti-pattern the spec calls out by name, and it also destroys the thing you built the gateway for: the upstream's logs will show a request that appears to come from the client, not from the gateway, so nobody can tell afterward which agent did what.

Failure domains differ. If your GitHub MCP server goes down, GitHub tools stop working. If your gateway goes down, everything stops working. A gateway is a single point of failure by construction, and it is a high-value target for the same reason it is useful: it sees every tool call and every result that flows through it.

The unit of policy differs. A server enforces its own rules about its own tools. A gateway enforces rules across servers, which is the only place a rule like "this agent may read from anywhere but write nowhere" can be expressed, since no individual server knows what the others expose.

The API gateway analogy holds up well here. An MCP server is to an MCP gateway roughly what an HTTP service is to an API gateway. The main difference is that the unit of policy is a tool call and its arguments rather than a URL and a method.

What the protocol gives intermediaries

Several changes in the 2026-07-28 revision read as though intermediaries were on the authors' minds, and one of them says so outright. The Streamable HTTP transport mirrors selected JSON-RPC body fields into HTTP headers, in the spec's words, "so that intermediaries (load balancers, gateways, observability tooling) can route and inspect requests without parsing the body." Every POST carries Mcp-Method, and tools/call, resources/read, and prompts/get also carry Mcp-Name. A gateway can rate limit a specific tool at the HTTP layer without touching JSON.

Servers can go further and mirror chosen tool parameters into headers with an x-mcp-header annotation in the parameter's schema. The spec's own example annotates a region parameter on an execute_sql tool, which arrives as Mcp-Param-Region: us-west1. You can route on the region without reading the SQL.

There is a trap in that convenience. Servers that process the body must validate that the headers match the body and reject mismatches with a HeaderMismatch error (-32020), but that requirement only exists in the revisions that define it. The spec anticipates this and tells intermediaries enforcing policy on mirrored headers that they should check MCP-Protocol-Version first and reject the request when the version does not require header and body validation. It is advisory language, and you should treat it as mandatory anyway: a gateway that trusts Mcp-Name from a client speaking an older revision has a policy bypass, where the header says one tool and the body calls another.

Two other changes matter for anyone operating a gateway. Protocol-level sessions and the Mcp-Session-Id header were removed, and MCP is now stateless. That means no session affinity: any gateway instance can serve any request, and you scale horizontally without a shared session store. And cacheable results now carry ttlMs and cacheScope (the list methods, plus resources/read), where cacheScope is "public" or "private" and controls whether shared intermediaries are allowed to cache the response. That field exists specifically for things like gateways.

The spec also documents OpenTelemetry trace context conventions for _meta keys (traceparent, tracestate, baggage), so a gateway can attach tool calls to an existing distributed trace instead of inventing its own correlation scheme.

What a gateway does not fix

It adds a hop. Usually small next to the tool call itself, but real, and much larger if the gateway and the upstream server sit in different regions.

Its permissions are per tool call, not per record. If a tool takes a query string, allowing the tool allows every query that string can express. Row-level and field-level authorization has to live in the upstream server or in the system behind it, and no amount of gateway policy substitutes for it.

It makes tools/list worse before it makes it better. Aggregating thirty servers hands the model several hundred tools and a much harder selection problem, along with a large fixed cost in context. Per-client tool filtering stops being optional at that point, and deciding which client sees which tools is a judgment call somebody has to make and maintain.

And it does not solve prompt injection. A gateway sees the tool result flowing back and can redact patterns out of it, but it cannot tell that a paragraph inside a returned web page is an instruction the model is about to act on. Determining that a call was influenced by injected text requires knowing what the model was reasoning over, which is not information the gateway has.