McpProxyHandler reference
McpProxyHandler is the route handler that backs every MCP Gateway route. It
accepts stateless Streamable HTTP requests over POST, forwards them to the
configured upstream MCP server using Zuplo's standard URL rewrite, and emits a
pair of analytics events per request so the gateway dashboard knows what each
capability call did.
When to use it
Use McpProxyHandler on any route that proxies to an upstream MCP server. Pair
it with at least one MCP OAuth policy on the inbound chain; add an
mcp-token-exchange-inbound policy when the upstream itself requires OAuth, and
optionally mcp-capability-filter-inbound to curate what the upstream
advertises.
If the upstream uses a static API key or static header instead of OAuth, keep
the MCP OAuth policy on the route, drop the token exchange policy, and add
set-upstream-api-key-inbound
or set-headers-inbound to attach the
credential before the handler forwards.
Configuration
The handler is referenced from the route's x-zuplo-route.handler block in
routes.oas.json:
Code
Set corsPolicy to "none". MCP clients aren't browser-based and shouldn't be
sending ambient credentials.
Options
rewritePattern (required)
The upstream MCP server URL. The handler forwards each authenticated POST to
this URL, with the resolved upstream Authorization: Bearer header applied by
the token exchange policy.
Two value forms are supported:
- A literal HTTPS or HTTP URL. Used verbatim as the upstream target.
- An environment-variable reference of the form
${env.X}. The variable must resolve to a fully-qualified HTTP(S) URL.
Code
Dynamic request-based patterns are explicitly rejected — MCP routes need a stable upstream URL.
The URL Rewrite handler's broader template syntax — ${params.x},
${headers.get("x")}, and so on — is not supported on rewritePattern for
MCP routes. Use a literal URL or an ${env.X} reference.
forwardSearch (optional)
Type: boolean. Default: true.
When true, the inbound request's query string is appended to the upstream URL
before forwarding. Set to false to drop client query parameters.
followRedirects (optional)
Type: boolean. Default: false.
When false, redirects from the upstream return as-is to the client (status
code and Location header passed through). Set to true to have the runtime
follow them transparently.
mtlsCertificate (optional)
Type: string. The id of an mTLS certificate registered with the Zuplo project.
When set, the upstream fetch uses mutual TLS with the specified client
certificate. Most MCP upstreams don't require mTLS; leave this unset unless you
specifically need it.
Behavior
GET returns 405
The gateway only speaks stateless Streamable HTTP, and the MCP authorization
spec uses POST for every JSON-RPC call. A GET to an MCP route returns:
Code
If you've seen an MCP server that exposes a GET endpoint for SSE event streams, that's a different transport. The Zuplo MCP Gateway is Streamable HTTP, POST-only.
POST forwards to the upstream
A POST request runs through the inbound policy chain, then the handler emits
capability analytics events, forwards to the upstream URL, and emits a
completion event with outcome, mcpStatus, latencyMs, and any JSON-RPC
error details.
Inbound auth headers don't leak to the upstream — the gateway-issued bearer
token is stripped, and the token exchange policy sets the upstream's own
Authorization: Bearer <upstream-token> header.
Route requirements
Every route that uses McpProxyHandler must:
- Set
operationId. It's used to identify the MCP route. - Include an MCP OAuth policy in the inbound chain — either
mcp-oauth-inboundormcp-auth0-oauth-inbound. - Include at most one
mcp-token-exchange-inboundpolicy.
Across the project:
- No two MCP routes can share an
operationId. - No two MCP routes can share a path.
- No two
mcp-token-exchange-*policies can share an upstreamid.
Analytics
Every POST emits two analytics events when the request body parses as a JSON-RPC call:
- A
capability_invocation_startedevent fired before the upstream fetch, carrying the parsedmcpMethodandcapabilityName. - A
capability_invocation_completedevent fired after the response, carryingoutcome,mcpStatus,latencyMs, and any JSON-RPC error details.
Each event also includes the route's operationId (as virtualServerName), the
upstream id (as upstreamServerName), the authenticated subjectId, the
authProfileId, and the upstreamAuthMode. See
Analytics for the dashboard view and
Logging for the structured-log counterpart.
Related
mcp-token-exchange-inbound— resolves the upstream credential and handles upstream 401 refresh and retry.mcp-capability-filter-inbound— curates the upstream surface area on a per-route basis.- Multi-upstream pattern — pair one
McpProxyHandlerroute with each upstream MCP server in one project.