MCP Server Registry
The MCP Gateway serves a read-only MCP Server Registry API (v0.1 freeze) that advertises every MCP route on the gateway. MCP clients and mirroring subregistries can discover the gateway's registered servers without authentication, per the registry specification.
The registry is enabled by default and mounted at /__zuplo/mcp-registry (with
the default basePath). Configure or disable it through the
registry option on
McpGatewayPlugin.
What gets listed
The registry projects one entry per MCP-serving route — both:
- Gateway virtual servers — routes that carry MCP gateway policies
(
mcp-oauth-inbound/mcp-token-exchange-inbound) and useMcpProxyHandlerto proxy to an upstream MCP server. - Standalone MCP servers — routes that use the built-in
mcpServerHandleror a bareMcpProxyHandlerwithout gateway policies.
Each entry includes:
| Field | Source |
|---|---|
name | <reverse-DNS of request host>/<operationId> (for example, com.example.gateway/linear-mcp-server) |
remotes | The gateway route URL, using the streamable-http transport. |
description | OpenAPI operation description or summary, clamped to 100 code points per the spec. |
title | mcpServerHandler options or the OpenAPI operation summary. |
version | mcpServerHandler options or upstream connection config. Range-like values (^1.2.3, 1.x) project as 0.0.0. |
websiteUrl | Upstream connection config, canonicalized to a valid URI. |
icons | Upstream connection config. Only HTTPS URLs are included; src is clamped to 255 characters. |
Server names are derived from the request host — the same posture as the OAuth
issuer and Protected Resource Metadata. Names vary with the hostname the gateway
is reached on, so a custom domain produces different names than the default
*.zuplo.dev origin.
The projection never throws. Every spec constraint is enforced by sanitizing or
omitting: names longer than 200 characters are truncated with a hash suffix,
descriptions are clamped, non-HTTPS icons are dropped, and version ranges are
replaced with 0.0.0. One malformed route can't take down discovery for the
rest.
Parameterized routes (for example, /mcp/{server}) are omitted from the
directory — a templated path is not a concretely connectable remote.
Endpoints
All paths are relative to the registry mount (default: /__zuplo/mcp-registry).
Discovery GETs are unauthenticated and CORS-permissive
(Access-Control-Allow-Origin: *).
GET {path}/v0.1/servers
Lists all MCP servers registered on the gateway.
| Query parameter | Type | Default | Description |
|---|---|---|---|
cursor | string | — | Opaque pagination cursor from a previous response. |
limit | integer | 30 | Maximum servers to return. Clamped to 100. |
search | string | — | Case-insensitive substring filter on server name. |
version | string | — | Filter to servers matching a specific version. |
updated_since | string | — | RFC 3339 date-time. Validated but never filters — every entry conservatively counts as changed. |
Response:
Code
When cursor is present in the response, more results are available. Pass it
back as the cursor query parameter on the next request.
GET {path}/v0.1/servers/{serverName}/versions
Lists versions for a single server. With the current read-only projection, every server has exactly one version entry.
{serverName} is URL-encoded — the slash in the reverse-DNS name is sent as
%2F (for example, com.example.gateway%2Flinear-mcp-server).
GET {path}/v0.1/servers/{serverName}/versions/{version}
Returns a single server version. The special alias latest resolves to the
server's current version.
Write endpoints (refused)
The registry is read-only. Write endpoints are refused per the spec:
| Endpoint | Method | Status | Reason |
|---|---|---|---|
{path}/v0.1/publish | POST | 501 | Spec marks publishing optional and documents 501. |
{path}/v0.1/servers/{serverName}/versions/{version} | PUT | 501 | Spec marks version update optional. |
{path}/v0.1/servers/{serverName}/versions/{version} | DELETE | 501 | Spec marks version deletion optional. |
{path}/v0.1/servers/{serverName}/versions/{version}/status | PATCH | 403 | Spec defines no 501 for status operations; returns 403 with a permissions error message. |
{path}/v0.1/servers/{serverName}/status | PATCH | 403 | Same as above. |
All error bodies use the spec's {"error": "..."} shape — not RFC 7807 problem
details — so off-the-shelf registry clients can parse them. A catch-all under
the mount ensures even unrecognized requests get the spec-shaped 404.
Configuration
The registry is configured through the McpGatewayPlugin constructor. See the
registry option for the
full options reference.
Code
Limitations
- No publishing. The registry is a read-only projection of the gateway's
live route configuration. There is no write path, no namespace-ownership
model, and no stored
publishedAtorupdatedAttimestamps. updated_sincevalidates but never filters. Because the registry is derived from route config with no stored timestamps, every entry conservatively counts as changed. This keeps mirroring subregistries correct at the cost of re-reading everything.- No deployment-pinned server identity. Server names are derived from the request host, not from a stable deployment-level identifier.
Related
McpGatewayPluginreference — the full plugin options, includingbasePathandregistry.- Gateway reference — the full URL catalog, including the registry mount.
- How the MCP Gateway works — architecture and request lifecycle.
- MCP Server Registry specification — the v0.1 freeze the gateway implements.