ZerkerGateway

Zerker MCP-Native Transport

Gateway reads the MCP envelope.

MCP traffic already passes through the proxy as JSON-RPC. Declaring protocol=mcp keeps that path and adds three things: which method was called, which tool, and whether the call is safe to retry.

shipped Apache-2.0 streamable http

Registering

Two fields, recorded on every call.

An MCP server is a catalog agent like any other. upstream_url is the Streamable HTTP endpoint. credential_ref works the same way for servers that authenticate with a bearer token or API key.

Register an MCP server

$ curl localhost:8080/v1/agents \
    -H "Authorization: Bearer $TOKEN" \
    -d '{
      "name": "docs-search",
      "upstream_url": "https://mcp.example.com",
      "protocol": "mcp",
      "mcp_transport": "streamable_http"
    }'

Find them again

$ curl localhost:8080/v1/agents?protocol=mcp

← filters the catalog down to your MCP
  servers, which matters once you are
  running a mix of REST and MCP

No new surface

No separate MCP endpoint.

An MCP agent uses the same /v1/proxy/{id} and /v1/proxy/{id}/stream calls as everything else. The body is one MCP JSON-RPC request. Use the streaming endpoint when the server answers with an event stream.

$ curl localhost:8080/v1/proxy/{id}/stream \
    -H "Authorization: Bearer $TOKEN" \
    -H 'Mcp-Session-Id: 9d3f2b1a-…' \
    -d '{"jsonrpc":"2.0","id":1,"method":"tools/call",
         "params":{"name":"search_docs","arguments":{"query":"SSRF"}}}'

The caller owns the session. You run initialize, manage Mcp-Session-Id, and sequence the calls. Gateway holds no session state and passes the session header through in both directions, so it can be restarted at any time.

The mechanic

tools/call is never retried.

The proxy retries transient upstream failures. For MCP, the retry decision depends on the method. A tool call can have side effects, and repeating one could execute it twice, so Gateway never retries it.

Method Why Retried
initialize Known-idempotent — reading the same thing twice costs nothing but a round trip Yes
tools/list Yes
resources/list Yes
prompts/list Yes
tools/call Side effects are the whole point of a tool. A silent double-execution is worse than a visible failure Never

A generic proxy sees an opaque body and has to pick one retry policy for everything. Reading the envelope is what makes a per-method rule possible.

What it can see

Total calls, or invocations of one named tool.

Gateway parses the JSON-RPC envelope for the method and, for a tool call, the tool name, and stores both on the invocation record as mcp_method and mcp_tool. That is what turns a call count into usage you can bill.

$ curl localhost:8080/v1/invocations/inv_019842…
{
  "id": "inv_019842…",
  "agent_id": "agt_019842…",
  "mcp_method": "tools/call",
  "mcp_tool": "search_docs",
  "status": "succeeded",
  "ttft_ms": 84
}

Both fields show up on GET /v1/invocations and on the per-agent invocation fetch, so they filter and aggregate like anything else. See observability.

Scope, on purpose

One transport.

Streamable HTTP is the only transport Gateway accepts.

Transport What happens Why
streamable_http Accepted The transport the MCP spec moved to; an ordinary HTTPS endpoint we can guard like any other
stdio Rejected at write time with 400 A local-process transport with no URL and no TLS. If it ships, it belongs in a client SDK
HTTP+SSE Reserved, not accepted Deprecated by the MCP spec in favour of Streamable HTTP

No new SSRF exposure. An MCP upstream URL is validated at write time and again at dial time, like every other upstream.

Where the line falls

MCP support is open source.

Envelope parsing, method and tool capture, and method-gated retries are all in the open-source binary.

Capability Tier
MCP-native transport over Streamable HTTP OSS
Method and tool capture, and method-gated retry safety OSS
Usage and revenue dashboards built on that capture Commercial

The full boundary →

Next

Read the calls back.

Method and tool are two fields on a record the proxy already writes. Observability is how you read them.