Writing ·
How to enforce MCP tool policy at an HTTP gateway in 2026
Use the MCP 2026-07-28 Streamable HTTP headers to make a narrow edge classification decision — version, RPC method, and named tool or resource — then let the MCP server reject header/body mismatches, validate the request and token, authorize the operation, and enforce argument- and side-effect-specific controls.
By Youssef Hemimy · MCP · gateway · AgentOps
Use the MCP 2026-07-28 Streamable HTTP headers to make a narrow edge classification decision — version, RPC method, and named tool or resource — then let the MCP server reject header/body mismatches, validate the request and token, authorize the requested operation, and enforce argument- and side-effect-specific controls. The gateway can remove request classes that have no business reaching a server and can rate-limit and observe the rest by operation. It cannot make a tool call safe by itself.
- 3
- header fields available for edge classification: version, method, name
- 400
- status a header/body protocol-version mismatch must return
- 3
- named operations that require Mcp-Name: tools/call, resources/read, prompts/get
- 1
- layer that can validate a token and authorize an operation: the MCP server
Why the gateway can now see an MCP operation
The stable MCP 2026-07-28 revision requires Streamable HTTP clients to send every JSON-RPC request as its own HTTP POST to a single MCP endpoint. The protocol mirrors selected JSON-RPC body fields into HTTP headers so intermediaries can route and inspect a request without parsing its body — the body remains the source of truth, and bindings define how a mismatch is rejected.
For a compliant Streamable HTTP request, MCP-Protocol-Version is required on every POST, Mcp-Method is required on every request, and Mcp-Name is required for tools/call, resources/read, and prompts/get. The version header must agree with the request body's _meta protocol version; if it does not, the server must reject the request with 400 Bad Request and a HeaderMismatch error.
Cloudflare's implementation analysis describes the operational use plainly: a gateway, rate limiter, or WAF can build rules from these headers without parsing arbitrary JSON, and can record tool-level metrics using ordinary HTTP controls. That is one vendor's implementation experience, not a claim that every gateway ships the same feature set.
01
Request arrives
Streamable HTTP POST mirrors selected body fields into headers.
02
Gateway decision
Admit, deny, or rate-limit on version, method, and name alone.
03
Server authority
Reject mismatches, validate the token, authorize, validate arguments.
The gateway sees a narrow decision surface
Three header fields, not the JSON-RPC body — enough to admit or reject a request class, not enough to authorize an action.
The server stays the source of truth
Header/body agreement, token validity, authorization, and argument checks all happen after the gateway proxies the request.
Make the edge decision deliberately narrow
Start with an allowlist whose entries are recognizable to both the gateway and the server. A useful policy record has four fields — the first three built directly from the protocol's mirrored metadata, the fourth a deliberate operating choice layered on top:
| Policy field | Edge decision | Backend decision |
|---|---|---|
| Protocol version | Accept only versions the endpoint is prepared to serve; route legacy traffic separately or reject it. | Reject unsupported or body/header-inconsistent versions as the protocol requires. |
| RPC method | Allow only expected methods, such as tools/list, tools/call, or a documented discovery operation. | Confirm the JSON-RPC method and apply the method's protocol rules. |
| Operation name | For a named request, allow only the tool, prompt, or resource names intended for that endpoint. | Resolve the operation in the authenticated caller's permitted set. |
| Operation class | Rate-limit, log, and alert differently for discovery, read, and consequential invocation classes. | Validate arguments, authorization, preconditions, idempotency, and any approval requirement. |
A gateway might allow tools/list at a modest discovery rate, allow a read-only tools/call name under a per-user limit, and deny an unrecognized name before it consumes server capacity. It should still send the accepted request to a server that repeats the authoritative checks. A gateway allowlist is a traffic-admission control; it is not permission to perform the underlying action.
Keep header classification consistent with the JSON-RPC request
Do not let the edge rule become a second, loosely synchronized protocol implementation. The protocol says request metadata lives in the message body and that the HTTP binding mirrors selected fields into headers; the body is the source of truth. The Streamable HTTP specification requires the server to reject a mismatch between the protocol-version header and the body.
Treat the same principle as a design rule for gateway policy:
- Classify at the edge. Read the required protocol, method, and name headers; reject malformed, unknown, or unallowed combinations before proxying.
- Preserve the request. Do not rewrite a method or tool name to make a request fit an allowlist. Pass it to an MCP-aware backend that can compare the wire representation with the JSON-RPC payload.
- Reject disagreement. Make mismatched or incomplete metadata a visible failure class, not a fallback to a more permissive rule.
- Test the negative cases. Exercise a valid request, an unknown method, an unknown tool name, a header/body mismatch, an unsupported version, and a valid name with invalid arguments. Record which layer rejected each case.
That test plan is Bonfire guidance: a policy is only as credible as the behavior at the boundary where its classification and the server's semantics disagree.
Put authorization and argument controls behind the gateway
MCP's authorization specification defines authorization capabilities for HTTP-based transports but makes authorization optional for MCP implementations. When it is supported, a protected MCP server is an OAuth 2.1 resource server; it must validate access tokens and accept only tokens valid for its own resources, and clients must send authorization on every HTTP request.
That is server-side work. A gateway can enforce a coarse identity or route traffic to a tenant boundary, but a method/name header cannot establish that a token is valid for the target MCP server, that the caller has the right scope for one specific tool, or that the arguments satisfy the tool's business rules. The tools specification makes the gap concrete: a tools/call request includes a tool name and an argumentsobject, tool definitions provide JSON Schema for expected input, and a server's available tool set may vary by the authorization presented on a request.
Use two complementary checks:
- Gateway: Is this version/method/name combination allowed to enter this endpoint, at this rate, from this coarse traffic class?
- Server: Is the caller authenticated and authorized for this operation now? Do the arguments satisfy the schema and business preconditions? Is an approval, idempotency key, state check, or compensating path required before an effect occurs?
The second list is broader than MCP's headers on purpose — production safety depends on an operation's inputs and effects, not only its name. For the deeper tool-contract boundary, see production agent tool contracts.
Use custom parameter headers carefully
MCP 2026-07-28 also permits a server to designate selected primitive tool parameters with x-mcp-header. A conforming Streamable HTTP client must mirror those values into Mcp-Param-* headers, subject to defined schema and encoding constraints. The tools specification warns server developers not to mark sensitive parameters — passwords, API keys, tokens, or PII — with x-mcp-header, because headers are visible to network intermediaries.
Keep a parameter mirrored only when all of these are true:
- it is non-sensitive and has a documented edge-policy use;
- the client and server both enforce the protocol's header-encoding and schema constraints;
- the server still validates the original argument; and
- gateway telemetry and retention for the header are acceptable for that field.
Those four gates are Bonfire guidance. They turn the specification's transport feature into a reviewable operating decision rather than an accidental data export.
Design rate limits and telemetry around operation classes
A single endpoint budget hides the behavior you need to manage. tools/list can be a discovery path; a named tools/call may be a read, an expensive computation, or a consequential write. The method and name headers give an HTTP intermediary stable labels for separate rate limits and operation-level telemetry on Streamable HTTP.
| Signal | Why retain it | Do not infer from it |
|---|---|---|
| Protocol version, method, and operation name | Which policy branch handled the request | Whether the arguments were valid or the caller was authorized |
| Policy outcome and reason code | Whether the edge allowed, denied, or rate-limited traffic | Whether a server-side action completed |
| Server outcome class and latency | Whether a permitted request reached a successful backend result | The contents of the tool arguments or result |
| Approval or idempotency reference, when applicable | Which downstream control governed a consequential action | That the gateway itself provided that control |
This is a Bonfire observability pattern, not a prescribed MCP log schema. It pairs well with an agent observability dashboard: the edge and the server should make their decisions joinable without turning operation logs into an unrestricted argument archive.
Roll out the policy without breaking legitimate clients
The 2026-07-28 revision changed Streamable HTTP behavior, including removal of protocol-level sessions and the GET stream endpoint. The specification's changelog says this revision requires standard Mcp-Method and Mcp-Name request headers on Streamable HTTP POST requests.
Before enforcing a strict edge allowlist, inventory the protocol versions and methods your deployed clients actually use, then stage the policy:
- Observe first. Record the method/name/version combinations the endpoint receives and identify legacy or unknown traffic without silently treating it as safe.
- Define explicit compatibility behavior. Route a supported earlier protocol version to a separate endpoint or policy, or reject it with a documented migration path. Do not let a 2026-07-28 policy accidentally authorize legacy traffic that lacks the same metadata.
- Enforce known combinations. Deny unknown version/method/name combinations and give operators a durable reason code.
- Exercise failure paths. Verify rate-limit responses, mismatch rejection, unsupported-version handling, expired-token handling, invalid arguments, and the downstream approval path for consequential operations.
- Review after change. Compare edge denials, server rejections, and completed outcomes. A large mismatch between those views is a policy or integration problem to investigate.
What this boundary does — and does not — solve
An HTTP gateway is valuable because it can remove clearly unallowed request classes before they hit an MCP server and can make the remaining operation traffic easier to rate-limit and observe. It does not substitute for the server controls that make a tool call legitimate. Keep the boundaries separate:
- Securely exposing a private MCP server is about controlled network reachability; it is not tool authorization.
- Building a custom MCP server for production covers the backend's tool, authorization, logging, and versioning responsibilities.
- Human approval gates for AI agents cover the reviewer decision before a risky action; allowing a tool name through a gateway is not that decision.
That separation is less glamorous than “the gateway secures MCP.” It is also substantially more honest.
Caveats
This draft is scoped to the stable MCP 2026-07-28 specification and Streamable HTTP; other transports do not necessarily expose the same HTTP metadata. The specification treats the body as the source of truth for mirrored metadata, so this draft does not claim a header-only gateway can validate all JSON-RPC semantics or arguments. MCP authorization is optional, and its authorization specification does not prove that any particular gateway, OAuth deployment, tool schema, or policy configuration is secure. Cloudflare is cited as one implementation example for HTTP-native operation classification; its product claims are not generalized to other gateways or deployments.
Sources
FAQ
Can an HTTP gateway authorize an MCP tool call on its own?
No. MCP's Streamable HTTP headers give a gateway three classification fields — protocol version, RPC method, and (for named operations) the tool, resource, or prompt name. That is enough to admit or reject a request class. It is not a token check, a scope check, or an argument check. The MCP server remains the OAuth 2.1 resource server when authorization is used, and it is the only layer that can validate a token for itself, decide whether a specific caller may use a specific tool, and validate that tool's arguments.
What happens if the MCP-Protocol-Version header disagrees with the request body?
The Streamable HTTP specification requires the server to reject the request with 400 Bad Request and a HeaderMismatch error. A gateway that rewrites a header without the corresponding body field — or vice versa — will start breaking requests that were previously passing through untouched.
Is Mcp-Name required on every MCP request?
No. It is required specifically for tools/call, resources/read, and prompts/get — the named operations. Mcp-Method is required on every Streamable HTTP POST request, and MCP-Protocol-Version is required on every POST.
Should sensitive tool arguments be mirrored into Mcp-Param-* headers?
No. The MCP tools specification warns server developers not to mark sensitive parameters — passwords, API keys, tokens, or PII — with x-mcp-header, because HTTP headers are visible to network intermediaries. Reserve that mechanism for non-sensitive, edge-routing-relevant values, and keep the server validating the original argument regardless.
Does a gateway allowlist replace a human approval gate for consequential tool calls?
No. Allowing a version/method/name combination through the edge is a traffic-admission decision, not a review decision. A consequential action — one with a side effect that is hard to reverse — still needs the reviewer or approval-gate logic described separately from gateway policy.
Building something that has to hold up?
We do this work for teams — agent reliability hardening, custom MCP servers, and full-stack AI systems built to survive production.