OBLETHdocs

MCP gateway

Put MCP tool servers behind gateway keys, so clients reach them at /mcp/<server> and models can use their tools.

4 min readUpdated for v2.4.0Edit on GitHub

Route/mcp/<server> on the data plane, any method
You needA streamable-HTTP or SSE MCP server
Turn it onMCP servers › Add server
Who can callAny key whose tenant is active and in its schedule

A registered MCP server is used two ways. Clients with a gateway key call it directly at /mcp/<server>, and the gateway adds the server's own credential on the way. Models you grant it to get its tools, and the gateway runs their tool calls for them.

Add a server

  1. 1

    Register it

    On the MCP servers page, choose Add server and fill in Name, Upstream URL and, if the server needs one, Authorization (the full header value, such as Bearer ghp_…). Add and check saves it and checks it at once.

    The name is the path clients use (/mcp/github) and the name models are granted it by: letters, digits, ., _ and -, up to 64 characters, starting with a letter or digit.

  2. 2

    Confirm it answers

    The page checks every server when it opens: whether it answers, its tools, the models that use it, its calls in the last 7 days and its response time. A server that does not answer shows under Not answering. See Verifying a server.

  3. 3

    Grant it to models

    Open the server and turn on the models under Models that can use it. Only models with Function calling on are listed. Then turn on the tool loop so the gateway runs the calls (see below).

Or use the Management API:

curl -X POST http://localhost:9180/api/v1/mcp-servers \
  -H "Authorization: Bearer $OBLETH_ADMIN_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"name": "github", "upstream_url": "https://mcp.githubcopilot.com/mcp", "auth_header": "Bearer ghp_…"}'

GET, PUT and DELETE /api/v1/mcp-servers/{id} read, change and remove a server. auth_header is write-only: responses carry auth_header_set instead, a PUT without it keeps the stored value, and clear_auth: true removes it. A PUT may also rename the server with name, and every model's grant follows the new name. Removing a server removes it from every model that had it. The upstream URL must pass the gateway's outbound address policy.

Call a server through the gateway

Point the MCP client at the gateway with a gateway key:

POST https://gateway.example.com/mcp/github
Authorization: Bearer sk_...
Content-Type: application/json

{"jsonrpc": "2.0", "method": "tools/list", "id": 1}

The key may also go in x-api-key, and an identity token works as it does for model traffic. For each call the gateway:

  1. Authenticates the credential and applies the tenant gate: a disabled key, a tenant that is not active, or one outside its activation window, expiry or weekly schedule gets 403.
  2. Looks up the server; a disabled one gets 403 mcp server is disabled.
  3. Removes the client's credential and sends the server's Authorization header instead. Clients never see it.
  4. Forwards the request and streams the answer back, JSON or SSE, with the server's headers, including mcp-session-id.

The gateway does not parse JSON-RPC, so any MCP-over-HTTP server works. /mcp/<server>/<rest> forwards the rest of the path and the query string. Bodies are limited to 16 MiB. MCP calls are not admitted through fairshare and not billed.

Verifying a server

A wrong URL or a rejected credential only shows when something calls the server, so the dashboard runs the real MCP handshake (initialize, then tools/list) through /mcp/<name>. It does this when the MCP servers page opens, when you add a server, and when you choose Check again. It reports whether the server answered, its name and version, its tools and the round-trip time. These checks are not counted in the server's call statistics.

The Playground assistant can check every registered server, or a named few, in one step and returns a pass or fail row per server.

The gateway tool loop

When a model is granted MCP servers and the tool loop is on, a plain chat client gets a grounded answer without ever seeing a tool. The gateway adds the servers' tools to the request, runs the model's tool calls against the servers, feeds back the results and asks again until the model answers. A client that sends its own tools keeps them: the granted tools are merged in, and only the gateway's own calls are run by the gateway.

To use it: register the server, grant it to a model with Function calling on, and turn on Tool loop in Settings › Boons, then save. Turn limits, per-tool timeout, the per-request deadline and billing are covered in Boons.

Errors

StatusMessageMeaning
400invalid request pathThe sub-path tries to leave the server's path
401missing bearer token, invalid api key, invalid tokenNo credential, or one that does not verify
403api key disabled, tenant is not active, or a schedule reasonThe key or tenant may not call now
403mcp server is disabledThe server is turned off
404mcp server '<name>' is not registeredNo server by that name
413request body too largeOver 16 MiB
502mcp upstream request failedThe server could not be reached
503authentication backend unavailable, identity provisioning unavailableThe credential lookup failed

An error status from the server itself, 5xx included, is passed back unchanged.

Metrics, traces and alerts

obleth_mcp_requests_total{server, status} counts calls per server by status class (2xx, 4xx, 5xx). With tracing on, each call is an mcp_request span tagged with the server name. Per-server daily counts (direct calls, tool-loop calls, failures, time) feed the MCP servers page and GET /api/v1/stats/daily?kind=mcp.

These alerts go to your alert channels (see Alerting): mcp_upstream_request_failed (the server could not be reached), mcp_upstream_5xx_response, redis_mcp_lookup_failed and mcp_tool_discovery_failed (the tool loop could not list a granted server's tools).

Changes to servers are recorded in the audit log and reach every gateway replica through Redis.