zuplo 7.4.4 → 7.4.5
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/docs/articles/ci-cd-azure/local-testing.mdx +22 -4
- package/docs/articles/ci-cd-bitbucket/local-testing.mdx +22 -4
- package/docs/articles/ci-cd-circleci/local-testing.mdx +21 -4
- package/docs/articles/ci-cd-github/deploy-and-test.mdx +28 -13
- package/docs/articles/ci-cd-github/local-testing.mdx +36 -16
- package/docs/articles/ci-cd-gitlab/local-testing.mdx +22 -4
- package/docs/articles/github-deployment-testing.mdx +118 -31
- package/docs/articles/testing-getting-started.mdx +220 -0
- package/docs/articles/testing-preview-environments.mdx +148 -0
- package/docs/articles/testing-recipes.mdx +429 -0
- package/docs/articles/testing.mdx +138 -408
- package/docs/mcp-gateway/auth/configuring-auth0.mdx +6 -5
- package/docs/mcp-gateway/auth/configuring-clerk.mdx +4 -4
- package/docs/mcp-gateway/auth/configuring-cognito.mdx +5 -4
- package/docs/mcp-gateway/auth/configuring-entra.mdx +5 -4
- package/docs/mcp-gateway/auth/configuring-generic-oidc.mdx +8 -8
- package/docs/mcp-gateway/auth/configuring-google.mdx +4 -4
- package/docs/mcp-gateway/auth/configuring-keycloak.mdx +4 -3
- package/docs/mcp-gateway/auth/configuring-logto.mdx +4 -4
- package/docs/mcp-gateway/auth/configuring-okta.mdx +3 -3
- package/docs/mcp-gateway/auth/configuring-onelogin.mdx +3 -3
- package/docs/mcp-gateway/auth/configuring-ping.mdx +3 -3
- package/docs/mcp-gateway/auth/configuring-workos.mdx +5 -4
- package/docs/mcp-gateway/auth/manual-oauth-testing.mdx +8 -8
- package/docs/mcp-gateway/auth/overview.mdx +17 -17
- package/docs/mcp-gateway/auth/upstream-oauth.mdx +5 -5
- package/docs/mcp-gateway/code-config/local-development.mdx +14 -12
- package/docs/mcp-gateway/code-config/overview.mdx +9 -4
- package/docs/mcp-gateway/how-it-works.mdx +11 -9
- package/docs/mcp-gateway/introduction.mdx +3 -1
- package/docs/mcp-gateway/quickstart-local.mdx +7 -7
- package/docs/mcp-gateway/reference.mdx +58 -25
- package/docs/mcp-gateway/server-registry.mdx +179 -0
- package/docs/mcp-gateway/test-clients.mdx +2 -2
- package/docs/mcp-server/custom-tools.mdx +32 -0
- package/docs/programmable-api/mcp-gateway-plugin.mdx +137 -0
- package/docs/programmable-api/mcp-sdk.mdx +240 -0
- package/package.json +5 -5
|
@@ -32,7 +32,9 @@ prompt injection or token misuse. Each gateway feature maps to one of five
|
|
|
32
32
|
problems that show up the moment a team uses more than one or two MCP servers:
|
|
33
33
|
|
|
34
34
|
1. **Discovery.** A single catalog of approved MCP servers your developers and
|
|
35
|
-
agents can connect to.
|
|
35
|
+
agents can connect to. The gateway serves a read-only
|
|
36
|
+
[MCP Server Registry](./server-registry.mdx) API that advertises every
|
|
37
|
+
registered MCP route — no more sharing OAuth client IDs in Slack.
|
|
36
38
|
2. **Authentication.** Translation from your corporate SSO to whatever each
|
|
37
39
|
upstream MCP requires. MCP client config files no longer hold raw upstream
|
|
38
40
|
credentials.
|
|
@@ -91,7 +91,7 @@ older project and the build complains about the compatibility date, see
|
|
|
91
91
|
"jwksUrl": "http://127.0.0.1:9000/.well-known/jwks.json"
|
|
92
92
|
},
|
|
93
93
|
"browserLogin": {
|
|
94
|
-
"url": "http://127.0.0.1:9000/oauth/dev-login"
|
|
94
|
+
"url": "http://127.0.0.1:9000/__zuplo/oauth/dev-login"
|
|
95
95
|
}
|
|
96
96
|
}
|
|
97
97
|
}
|
|
@@ -100,12 +100,12 @@ older project and the build complains about the compatibility date, see
|
|
|
100
100
|
|
|
101
101
|
:::caution
|
|
102
102
|
|
|
103
|
-
`/oauth/dev-login` returns `403 Forbidden` for any request that
|
|
104
|
-
arrive over loopback, so it's safe to leave configured, but only
|
|
105
|
-
local dev. Production deployments should use a real OIDC provider
|
|
106
|
-
of the [IdP wrappers](./auth/overview.mdx#identity-providers). A
|
|
107
|
-
pattern is keeping two OAuth policies (one for production, one for
|
|
108
|
-
selecting between them in `routes.oas.json` by environment.
|
|
103
|
+
`/__zuplo/oauth/dev-login` returns `403 Forbidden` for any request that
|
|
104
|
+
doesn't arrive over loopback, so it's safe to leave configured, but only
|
|
105
|
+
useful in local dev. Production deployments should use a real OIDC provider
|
|
106
|
+
through one of the [IdP wrappers](./auth/overview.mdx#identity-providers). A
|
|
107
|
+
common pattern is keeping two OAuth policies (one for production, one for
|
|
108
|
+
dev) and selecting between them in `routes.oas.json` by environment.
|
|
109
109
|
|
|
110
110
|
:::
|
|
111
111
|
|
|
@@ -16,37 +16,70 @@ The URLs below are all relative to the gateway origin. For a project deployed to
|
|
|
16
16
|
`https://my-gateway.zuplo.dev` with an MCP route at `/mcp/linear-v1`, the public
|
|
17
17
|
route is `https://my-gateway.zuplo.dev/mcp/linear-v1`.
|
|
18
18
|
|
|
19
|
+
The gateway mounts its internal OAuth and upstream-connection action routes
|
|
20
|
+
under the `basePath` configured on the `McpGatewayPlugin`. The default base path
|
|
21
|
+
is `/__zuplo`, which keeps gateway endpoints out of your API's route namespace.
|
|
22
|
+
Discovery documents (`/.well-known/…`) always stay at the origin root because
|
|
23
|
+
RFC 8414 / RFC 9728 locate them relative to the issuer. Pass `basePath: "/"` in
|
|
24
|
+
the plugin options to mount action routes at the origin root instead.
|
|
25
|
+
|
|
19
26
|
### Well-known metadata
|
|
20
27
|
|
|
21
28
|
| Path | Methods | Purpose |
|
|
22
29
|
| ------------------------------------------------------ | ---------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
23
30
|
| `/.well-known/oauth-authorization-server` | `GET`, `OPTIONS` | RFC 8414 Authorization Server metadata for the gateway. Issuer is the gateway origin. |
|
|
24
|
-
| `/.well-known/oauth-authorization-server/{routePath*}` | `GET`, `OPTIONS` | Per-route AS metadata. The issuer is rebound to the route's canonical URI, and `authorization_endpoint` points at
|
|
31
|
+
| `/.well-known/oauth-authorization-server/{routePath*}` | `GET`, `OPTIONS` | Per-route AS metadata. The issuer is rebound to the route's canonical URI, and `authorization_endpoint` points at `{basePath}/oauth/authorize/{routePath}`. |
|
|
25
32
|
| `/.well-known/oauth-protected-resource/{routePath*}` | `GET`, `OPTIONS` | RFC 9728 Protected Resource Metadata for an MCP route. Lists `resource`, `resource_name`, `authorization_servers`, `bearer_methods_supported`, `scopes_supported`, and `mcp_protocol_version`. |
|
|
26
|
-
| `/.well-known/oauth-client/{connection}` | `GET` | OAuth Client ID Metadata Document the gateway hosts to identify itself to an upstream provider. Requires the `?authProfileId=` query parameter. |
|
|
27
33
|
|
|
28
34
|
These routes are CORS-permissive (`Access-Control-Allow-Origin: *`) because
|
|
29
35
|
spec-compliant browser-resident MCP clients fetch them cross-origin.
|
|
30
36
|
|
|
31
37
|
### OAuth endpoints
|
|
32
38
|
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
|
37
|
-
|
|
|
38
|
-
| `/oauth/
|
|
39
|
-
| `/oauth/
|
|
40
|
-
| `/oauth/
|
|
41
|
-
| `/oauth/
|
|
42
|
-
| `/oauth/
|
|
39
|
+
Paths below use the default `basePath` of `/__zuplo`. Substitute your configured
|
|
40
|
+
`basePath` if you changed it.
|
|
41
|
+
|
|
42
|
+
| Path | Methods | Purpose |
|
|
43
|
+
| ------------------------------------------------ | ------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
44
|
+
| `/__zuplo/oauth/register` | `POST` | RFC 7591 Dynamic Client Registration. Supports `none`, `client_secret_basic`, `client_secret_post`, and `private_key_jwt` token-endpoint auth methods. DCR clients expire after 90 days. |
|
|
45
|
+
| `/__zuplo/oauth/authorize` | `GET` | Gateway-wide authorization endpoint. Requires the `resource` parameter unless exactly one MCP route is configured. |
|
|
46
|
+
| `/__zuplo/oauth/authorize/{routePath*}` | `GET` | Per-route authorization endpoint. The `resource` is implicit from the path. |
|
|
47
|
+
| `/__zuplo/oauth/callback` | `GET` | Browser-login callback from the configured identity provider. Renders the consent page. |
|
|
48
|
+
| `/__zuplo/oauth/setup` | `GET`, `POST` | Consent screen. Lists the upstream the requested MCP route depends on. `POST` accepts `decision=continue` / `approve` / `cancel`. |
|
|
49
|
+
| `/__zuplo/oauth/token` | `POST` | RFC 6749 token endpoint. Supports `authorization_code` and `refresh_token` grants. |
|
|
50
|
+
| `/__zuplo/oauth/revoke` | `POST` | RFC 7009 revocation endpoint. Accepts public-client revocations without authentication. |
|
|
51
|
+
| `/__zuplo/oauth/dev-login` | `GET` | Loopback-only dev shortcut. Returns `403` over non-loopback addresses. |
|
|
52
|
+
| `/__zuplo/.well-known/oauth-client/{connection}` | `GET` | OAuth Client ID Metadata Document the gateway hosts to identify itself to an upstream provider. Requires the `?authProfileId=` query parameter. |
|
|
43
53
|
|
|
44
54
|
### Upstream connection endpoints
|
|
45
55
|
|
|
46
|
-
| Path
|
|
47
|
-
|
|
|
48
|
-
| `/auth/connections/{connection}/connect` | `GET` | Browser entry to the upstream OAuth flow. With `redirect=true`, returns a 302 to the upstream `/authorize`; otherwise returns `428` with the connect-required payload. |
|
|
49
|
-
| `/auth/connections/{connection}/callback` | `GET` | Upstream OAuth callback. Renders a success or failure page. |
|
|
56
|
+
| Path | Methods | Purpose |
|
|
57
|
+
| ------------------------------------------------- | ------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
58
|
+
| `/__zuplo/auth/connections/{connection}/connect` | `GET` | Browser entry to the upstream OAuth flow. With `redirect=true`, returns a 302 to the upstream `/authorize`; otherwise returns `428` with the connect-required payload. |
|
|
59
|
+
| `/__zuplo/auth/connections/{connection}/callback` | `GET` | Upstream OAuth callback. Renders a success or failure page. |
|
|
60
|
+
|
|
61
|
+
### MCP Server Registry
|
|
62
|
+
|
|
63
|
+
The gateway serves a read-only
|
|
64
|
+
[MCP Server Registry API](https://github.com/modelcontextprotocol/registry)
|
|
65
|
+
(v0.1) that advertises every MCP route. Enabled by default; see
|
|
66
|
+
[Server Registry](./server-registry.mdx) for the endpoint reference and
|
|
67
|
+
[`McpGatewayPlugin`](../programmable-api/mcp-gateway-plugin.mdx#registry) for
|
|
68
|
+
configuration.
|
|
69
|
+
|
|
70
|
+
| Path | Methods | Purpose |
|
|
71
|
+
| -------------------------------------------------------------------- | ---------------- | ---------------------------------------------------------------------- |
|
|
72
|
+
| `{registryPath}/v0.1/servers` | `GET`, `OPTIONS` | List servers with `cursor`/`limit`/`search`/`version`/`updated_since`. |
|
|
73
|
+
| `{registryPath}/v0.1/servers/{serverName}/versions` | `GET`, `OPTIONS` | List versions for a single server. |
|
|
74
|
+
| `{registryPath}/v0.1/servers/{serverName}/versions/{version}` | `GET`, `OPTIONS` | Get a specific version (supports `latest` alias). |
|
|
75
|
+
| `{registryPath}/v0.1/publish` | `POST` | `501` — publishing is optional in the spec and not supported. |
|
|
76
|
+
| `{registryPath}/v0.1/servers/{serverName}/versions/{version}` | `PUT`, `DELETE` | `501` — version update/delete is optional in the spec. |
|
|
77
|
+
| `{registryPath}/v0.1/servers/{serverName}/versions/{version}/status` | `PATCH` | `403` — no publish/edit permissions. |
|
|
78
|
+
| `{registryPath}/v0.1/servers/{serverName}/status` | `PATCH` | `403` — no publish/edit permissions. |
|
|
79
|
+
|
|
80
|
+
`{registryPath}` defaults to `/__zuplo/mcp-registry`. Discovery GETs are
|
|
81
|
+
unauthenticated and CORS-permissive. Error bodies use the spec's
|
|
82
|
+
`{"error": "..."}` shape.
|
|
50
83
|
|
|
51
84
|
### Customer-defined MCP routes
|
|
52
85
|
|
|
@@ -74,17 +107,17 @@ DCR requests that include any other scope value are rejected with
|
|
|
74
107
|
| Refresh token / grant | ~10 years | `gateway.refreshTokenTtlSeconds` on the OAuth policy. | Downstream refresh grants are gateway client sessions, not upstream OAuth token lifetimes. The default is intentionally long so the gateway doesn't impose a shorter session bound than the upstream provider's refresh-token policy already does. |
|
|
75
108
|
| DCR-registered client | 90 days | Not configurable. | Encourages clients to use CIMD where possible; stale DCR clients age out automatically. |
|
|
76
109
|
| Authorization code | 60 seconds | Not configurable. | OAuth 2.1 recommendation. |
|
|
77
|
-
| `oauth_authorize` state | 15 minutes | `browserLogin.stateTtlSeconds`. | Window between `/oauth/authorize` and `/oauth/callback`.
|
|
110
|
+
| `oauth_authorize` state | 15 minutes | `browserLogin.stateTtlSeconds`. | Window between `/__zuplo/oauth/authorize` and `/__zuplo/oauth/callback`. |
|
|
78
111
|
|
|
79
112
|
## Headers
|
|
80
113
|
|
|
81
114
|
### Required on requests to MCP routes
|
|
82
115
|
|
|
83
|
-
| Header | Required | Notes
|
|
84
|
-
| --------------------------------------------- | ----------------------- |
|
|
85
|
-
| `Authorization: Bearer <token>` | Yes (after initial 401) | Opaque access token issued by `/oauth/token`. Tokens in query strings are rejected. |
|
|
86
|
-
| `Accept: application/json, text/event-stream` | Yes | Per the Streamable HTTP transport spec. The gateway forwards the body as-is.
|
|
87
|
-
| `MCP-Protocol-Version: 2025-11-25` | Yes after `initialize` | Per the MCP spec. The gateway tracks the current MCP protocol revision.
|
|
116
|
+
| Header | Required | Notes |
|
|
117
|
+
| --------------------------------------------- | ----------------------- | ------------------------------------------------------------------------------------------- |
|
|
118
|
+
| `Authorization: Bearer <token>` | Yes (after initial 401) | Opaque access token issued by `/__zuplo/oauth/token`. Tokens in query strings are rejected. |
|
|
119
|
+
| `Accept: application/json, text/event-stream` | Yes | Per the Streamable HTTP transport spec. The gateway forwards the body as-is. |
|
|
120
|
+
| `MCP-Protocol-Version: 2025-11-25` | Yes after `initialize` | Per the MCP spec. The gateway tracks the current MCP protocol revision. |
|
|
88
121
|
|
|
89
122
|
### Honored when present
|
|
90
123
|
|
|
@@ -119,9 +152,9 @@ See [Compatibility dates](./code-config/compatibility-dates.mdx).
|
|
|
119
152
|
In addition to the standard RFC 8414 / OIDC discovery fields, the gateway
|
|
120
153
|
publishes a vendor extension:
|
|
121
154
|
|
|
122
|
-
| Field | Type | Values | Purpose
|
|
123
|
-
| ---------------------------- | ------ | --------------------------------- |
|
|
124
|
-
| `x-zuplo-browser-login-kind` | string | `"federated_oidc"`, `"local_dev"` | Lets client tooling special-case local development configurations (which use `/oauth/dev-login` and a loopback IdP). |
|
|
155
|
+
| Field | Type | Values | Purpose |
|
|
156
|
+
| ---------------------------- | ------ | --------------------------------- | ---------------------------------------------------------------------------------------------------------------------------- |
|
|
157
|
+
| `x-zuplo-browser-login-kind` | string | `"federated_oidc"`, `"local_dev"` | Lets client tooling special-case local development configurations (which use `/__zuplo/oauth/dev-login` and a loopback IdP). |
|
|
125
158
|
|
|
126
159
|
## Public route URL pattern
|
|
127
160
|
|
|
@@ -0,0 +1,179 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: "MCP Server Registry"
|
|
3
|
+
sidebar_label: "Server Registry"
|
|
4
|
+
description:
|
|
5
|
+
The read-only MCP Server Registry API (v0.1) the gateway serves by default —
|
|
6
|
+
advertises every MCP route so clients and subregistries can discover them
|
|
7
|
+
without authentication.
|
|
8
|
+
---
|
|
9
|
+
|
|
10
|
+
The MCP Gateway serves a read-only
|
|
11
|
+
[MCP Server Registry API](https://github.com/modelcontextprotocol/registry)
|
|
12
|
+
(v0.1 freeze) that advertises every MCP route on the gateway. MCP clients and
|
|
13
|
+
mirroring subregistries can discover the gateway's registered servers without
|
|
14
|
+
authentication, per the registry specification.
|
|
15
|
+
|
|
16
|
+
The registry is enabled by default and mounted at `/__zuplo/mcp-registry` (with
|
|
17
|
+
the default `basePath`). Configure or disable it through the
|
|
18
|
+
[`registry` option](../programmable-api/mcp-gateway-plugin.mdx#registry) on
|
|
19
|
+
`McpGatewayPlugin`.
|
|
20
|
+
|
|
21
|
+
## What gets listed
|
|
22
|
+
|
|
23
|
+
The registry projects one entry per MCP-serving route — both:
|
|
24
|
+
|
|
25
|
+
- **Gateway virtual servers** — routes that carry MCP gateway policies
|
|
26
|
+
(`mcp-oauth-inbound` / `mcp-token-exchange-inbound`) and use
|
|
27
|
+
[`McpProxyHandler`](./code-config/mcp-proxy-handler.mdx) to proxy to an
|
|
28
|
+
upstream MCP server.
|
|
29
|
+
- **Standalone MCP servers** — routes that use the built-in `mcpServerHandler`
|
|
30
|
+
or a bare `McpProxyHandler` without gateway policies.
|
|
31
|
+
|
|
32
|
+
Each entry includes:
|
|
33
|
+
|
|
34
|
+
| Field | Source |
|
|
35
|
+
| ------------- | ----------------------------------------------------------------------------------------------------------------- |
|
|
36
|
+
| `name` | `<reverse-DNS of request host>/<operationId>` (for example, `com.example.gateway/linear-mcp-server`) |
|
|
37
|
+
| `remotes` | The gateway route URL, using the `streamable-http` transport. |
|
|
38
|
+
| `description` | OpenAPI operation `description` or `summary`, clamped to 100 code points per the spec. |
|
|
39
|
+
| `title` | `mcpServerHandler` options or the OpenAPI operation `summary`. |
|
|
40
|
+
| `version` | `mcpServerHandler` options or upstream connection config. Range-like values (`^1.2.3`, `1.x`) project as `0.0.0`. |
|
|
41
|
+
| `websiteUrl` | Upstream connection config, canonicalized to a valid URI. |
|
|
42
|
+
| `icons` | Upstream connection config. Only HTTPS URLs are included; `src` is clamped to 255 characters. |
|
|
43
|
+
|
|
44
|
+
:::note
|
|
45
|
+
|
|
46
|
+
Server names are derived from the request host — the same posture as the OAuth
|
|
47
|
+
issuer and Protected Resource Metadata. Names vary with the hostname the gateway
|
|
48
|
+
is reached on, so a custom domain produces different names than the default
|
|
49
|
+
`*.zuplo.dev` origin.
|
|
50
|
+
|
|
51
|
+
:::
|
|
52
|
+
|
|
53
|
+
The projection never throws. Every spec constraint is enforced by sanitizing or
|
|
54
|
+
omitting: names longer than 200 characters are truncated with a hash suffix,
|
|
55
|
+
descriptions are clamped, non-HTTPS icons are dropped, and version ranges are
|
|
56
|
+
replaced with `0.0.0`. One malformed route can't take down discovery for the
|
|
57
|
+
rest.
|
|
58
|
+
|
|
59
|
+
Parameterized routes (for example, `/mcp/{server}`) are omitted from the
|
|
60
|
+
directory — a templated path is not a concretely connectable remote.
|
|
61
|
+
|
|
62
|
+
## Endpoints
|
|
63
|
+
|
|
64
|
+
All paths are relative to the registry mount (default: `/__zuplo/mcp-registry`).
|
|
65
|
+
Discovery GETs are unauthenticated and CORS-permissive
|
|
66
|
+
(`Access-Control-Allow-Origin: *`).
|
|
67
|
+
|
|
68
|
+
### `GET {path}/v0.1/servers`
|
|
69
|
+
|
|
70
|
+
Lists all MCP servers registered on the gateway.
|
|
71
|
+
|
|
72
|
+
| Query parameter | Type | Default | Description |
|
|
73
|
+
| --------------- | ------- | ------- | ----------------------------------------------------------------------------------------------- |
|
|
74
|
+
| `cursor` | string | — | Opaque pagination cursor from a previous response. |
|
|
75
|
+
| `limit` | integer | `30` | Maximum servers to return. Clamped to `100`. |
|
|
76
|
+
| `search` | string | — | Case-insensitive substring filter on server name. |
|
|
77
|
+
| `version` | string | — | Filter to servers matching a specific version. |
|
|
78
|
+
| `updated_since` | string | — | RFC 3339 date-time. Validated but never filters — every entry conservatively counts as changed. |
|
|
79
|
+
|
|
80
|
+
Response:
|
|
81
|
+
|
|
82
|
+
```json
|
|
83
|
+
{
|
|
84
|
+
"servers": [
|
|
85
|
+
{
|
|
86
|
+
"name": "com.example.gateway/linear-mcp-server",
|
|
87
|
+
"description": "Linear MCP proxy",
|
|
88
|
+
"title": "Linear",
|
|
89
|
+
"version": "1.0.0",
|
|
90
|
+
"remotes": [
|
|
91
|
+
{
|
|
92
|
+
"type": "streamable-http",
|
|
93
|
+
"url": "https://gateway.example.com/mcp/linear-v1"
|
|
94
|
+
}
|
|
95
|
+
],
|
|
96
|
+
"websiteUrl": "https://linear.app",
|
|
97
|
+
"icons": []
|
|
98
|
+
}
|
|
99
|
+
],
|
|
100
|
+
"metadata": { "count": 1 }
|
|
101
|
+
}
|
|
102
|
+
```
|
|
103
|
+
|
|
104
|
+
When `cursor` is present in the response, more results are available. Pass it
|
|
105
|
+
back as the `cursor` query parameter on the next request.
|
|
106
|
+
|
|
107
|
+
### `GET {path}/v0.1/servers/{serverName}/versions`
|
|
108
|
+
|
|
109
|
+
Lists versions for a single server. With the current read-only projection, every
|
|
110
|
+
server has exactly one version entry.
|
|
111
|
+
|
|
112
|
+
`{serverName}` is URL-encoded — the slash in the reverse-DNS name is sent as
|
|
113
|
+
`%2F` (for example, `com.example.gateway%2Flinear-mcp-server`).
|
|
114
|
+
|
|
115
|
+
### `GET {path}/v0.1/servers/{serverName}/versions/{version}`
|
|
116
|
+
|
|
117
|
+
Returns a single server version. The special alias `latest` resolves to the
|
|
118
|
+
server's current version.
|
|
119
|
+
|
|
120
|
+
### Write endpoints (refused)
|
|
121
|
+
|
|
122
|
+
The registry is read-only. Write endpoints are refused per the spec:
|
|
123
|
+
|
|
124
|
+
| Endpoint | Method | Status | Reason |
|
|
125
|
+
| ------------------------------------------------------------ | -------- | ------ | -------------------------------------------------------------------------------------------- |
|
|
126
|
+
| `{path}/v0.1/publish` | `POST` | `501` | Spec marks publishing optional and documents `501`. |
|
|
127
|
+
| `{path}/v0.1/servers/{serverName}/versions/{version}` | `PUT` | `501` | Spec marks version update optional. |
|
|
128
|
+
| `{path}/v0.1/servers/{serverName}/versions/{version}` | `DELETE` | `501` | Spec marks version deletion optional. |
|
|
129
|
+
| `{path}/v0.1/servers/{serverName}/versions/{version}/status` | `PATCH` | `403` | Spec defines no `501` for status operations; returns `403` with a permissions error message. |
|
|
130
|
+
| `{path}/v0.1/servers/{serverName}/status` | `PATCH` | `403` | Same as above. |
|
|
131
|
+
|
|
132
|
+
All error bodies use the spec's `{"error": "..."}` shape — not RFC 7807 problem
|
|
133
|
+
details — so off-the-shelf registry clients can parse them. A catch-all under
|
|
134
|
+
the mount ensures even unrecognized requests get the spec-shaped `404`.
|
|
135
|
+
|
|
136
|
+
## Configuration
|
|
137
|
+
|
|
138
|
+
The registry is configured through the `McpGatewayPlugin` constructor. See the
|
|
139
|
+
[`registry` option](../programmable-api/mcp-gateway-plugin.mdx#registry) for the
|
|
140
|
+
full options reference.
|
|
141
|
+
|
|
142
|
+
```ts title="modules/zuplo.runtime.ts"
|
|
143
|
+
import { RuntimeExtensions } from "@zuplo/runtime";
|
|
144
|
+
import { McpGatewayPlugin } from "@zuplo/runtime/mcp-gateway";
|
|
145
|
+
|
|
146
|
+
export function runtimeInit(runtime: RuntimeExtensions) {
|
|
147
|
+
runtime.addPlugin(
|
|
148
|
+
new McpGatewayPlugin({
|
|
149
|
+
registry: {
|
|
150
|
+
enabled: true, // default
|
|
151
|
+
path: "/mcp-registry", // default: "{basePath}/mcp-registry"
|
|
152
|
+
},
|
|
153
|
+
}),
|
|
154
|
+
);
|
|
155
|
+
}
|
|
156
|
+
```
|
|
157
|
+
|
|
158
|
+
## Limitations
|
|
159
|
+
|
|
160
|
+
- **No publishing.** The registry is a read-only projection of the gateway's
|
|
161
|
+
live route configuration. There is no write path, no namespace-ownership
|
|
162
|
+
model, and no stored `publishedAt` or `updatedAt` timestamps.
|
|
163
|
+
- **`updated_since` validates but never filters.** Because the registry is
|
|
164
|
+
derived from route config with no stored timestamps, every entry
|
|
165
|
+
conservatively counts as changed. This keeps mirroring subregistries correct
|
|
166
|
+
at the cost of re-reading everything.
|
|
167
|
+
- **No deployment-pinned server identity.** Server names are derived from the
|
|
168
|
+
request host, not from a stable deployment-level identifier.
|
|
169
|
+
|
|
170
|
+
## Related
|
|
171
|
+
|
|
172
|
+
- [`McpGatewayPlugin` reference](../programmable-api/mcp-gateway-plugin.mdx) —
|
|
173
|
+
the full plugin options, including `basePath` and `registry`.
|
|
174
|
+
- [Gateway reference](./reference.mdx) — the full URL catalog, including the
|
|
175
|
+
registry mount.
|
|
176
|
+
- [How the MCP Gateway works](./how-it-works.mdx) — architecture and request
|
|
177
|
+
lifecycle.
|
|
178
|
+
- [MCP Server Registry specification](https://github.com/modelcontextprotocol/registry)
|
|
179
|
+
— the v0.1 freeze the gateway implements.
|
|
@@ -102,8 +102,8 @@ Whichever tool you pick, exercise these gateway behaviors during a smoke test:
|
|
|
102
102
|
`WWW-Authenticate: Bearer resource_metadata=...`. If it doesn't, the route is
|
|
103
103
|
missing an MCP OAuth policy.
|
|
104
104
|
2. **OAuth handshake completes.** Confirm the inspector lands on the gateway's
|
|
105
|
-
`/oauth/setup` consent page (rendered HTML), that the upstream's
|
|
106
|
-
button works, and that the inspector receives an access token.
|
|
105
|
+
`/__zuplo/oauth/setup` consent page (rendered HTML), that the upstream's
|
|
106
|
+
**Connect** button works, and that the inspector receives an access token.
|
|
107
107
|
3. **`tools/list` returns the expected curated set.** If you've attached
|
|
108
108
|
`mcp-capability-filter-inbound`, verify the filter is working — only
|
|
109
109
|
allow-listed tools should appear.
|
|
@@ -432,6 +432,36 @@ export default async function (request: ZuploRequest, context: ZuploContext) {
|
|
|
432
432
|
}
|
|
433
433
|
```
|
|
434
434
|
|
|
435
|
+
## Controlling tool results with ZuploMcpSdk
|
|
436
|
+
|
|
437
|
+
By default, the MCP Server handler serializes the downstream response body into
|
|
438
|
+
a single text content block and, when `includeStructuredContent` is enabled,
|
|
439
|
+
derives `structuredContent` from it. The `ZuploMcpSdk` class gives you
|
|
440
|
+
programmatic control over what the AI client receives — without changing the
|
|
441
|
+
response your handler returns.
|
|
442
|
+
|
|
443
|
+
The most common use case is a **compact summary**: replace the full serialized
|
|
444
|
+
body in `content` with a short human-readable summary, while the gateway still
|
|
445
|
+
auto-derives `structuredContent` from the downstream response. This reduces the
|
|
446
|
+
token count in the model's context without losing data.
|
|
447
|
+
|
|
448
|
+
```typescript
|
|
449
|
+
import { ZuploContext, ZuploMcpSdk, ZuploRequest } from "@zuplo/runtime";
|
|
450
|
+
|
|
451
|
+
export default async function (request: ZuploRequest, context: ZuploContext) {
|
|
452
|
+
const response = await context.invokeRoute("/todos");
|
|
453
|
+
new ZuploMcpSdk(context).setRawCallToolResult({
|
|
454
|
+
content: [{ type: "text", text: "Fetched the todo list" }],
|
|
455
|
+
});
|
|
456
|
+
return response;
|
|
457
|
+
}
|
|
458
|
+
```
|
|
459
|
+
|
|
460
|
+
You can also read metadata from the incoming `tools/call` request using
|
|
461
|
+
`getRawCallToolRequest()`, and override `structuredContent` and `_meta`
|
|
462
|
+
independently. For the full API reference, see
|
|
463
|
+
[ZuploMcpSdk](../programmable-api/mcp-sdk.mdx).
|
|
464
|
+
|
|
435
465
|
## Best Practices
|
|
436
466
|
|
|
437
467
|
### Tool Design
|
|
@@ -479,6 +509,8 @@ export default async function (request: ZuploRequest, context: ZuploContext) {
|
|
|
479
509
|
|
|
480
510
|
## Learn More
|
|
481
511
|
|
|
512
|
+
- [ZuploMcpSdk](../programmable-api/mcp-sdk.mdx) - Programmatic control over MCP
|
|
513
|
+
tool results
|
|
482
514
|
- [MCP Server Handler](../handlers/mcp-server.mdx) - For simple route-to-tool
|
|
483
515
|
mapping
|
|
484
516
|
- [Model Context Protocol Overview](../mcp-server/introduction.mdx) -
|
|
@@ -0,0 +1,137 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: "MCP Gateway Plugin"
|
|
3
|
+
sidebar_label: "MCP Gateway Plugin"
|
|
4
|
+
description:
|
|
5
|
+
Reference for the McpGatewayPlugin runtime plugin — options for the base path,
|
|
6
|
+
OAuth endpoints, and the read-only MCP Server Registry.
|
|
7
|
+
---
|
|
8
|
+
|
|
9
|
+
`McpGatewayPlugin` activates the MCP Gateway internal routes on the runtime
|
|
10
|
+
router: the OAuth authorization server, upstream connection management,
|
|
11
|
+
well-known metadata endpoints, and the read-only
|
|
12
|
+
[MCP Server Registry](../mcp-gateway/server-registry.mdx). When no MCP-related
|
|
13
|
+
policy is present, the plugin registers no OAuth routes — it still records
|
|
14
|
+
`plugin.mcp-gateway` feature usage on construction so gateway adoption is
|
|
15
|
+
visible in telemetry.
|
|
16
|
+
|
|
17
|
+
Importing from `@zuplo/runtime/mcp-gateway` is the opt-in: the runtime core
|
|
18
|
+
doesn't depend on MCP Gateway code until the plugin is added.
|
|
19
|
+
|
|
20
|
+
## Registration
|
|
21
|
+
|
|
22
|
+
Register the plugin in `modules/zuplo.runtime.ts`:
|
|
23
|
+
|
|
24
|
+
```ts title="modules/zuplo.runtime.ts"
|
|
25
|
+
import { RuntimeExtensions } from "@zuplo/runtime";
|
|
26
|
+
import { McpGatewayPlugin } from "@zuplo/runtime/mcp-gateway";
|
|
27
|
+
|
|
28
|
+
export function runtimeInit(runtime: RuntimeExtensions) {
|
|
29
|
+
runtime.addPlugin(new McpGatewayPlugin());
|
|
30
|
+
}
|
|
31
|
+
```
|
|
32
|
+
|
|
33
|
+
The plugin accepts an optional configuration object. All options have defaults,
|
|
34
|
+
so the no-argument form works for most projects.
|
|
35
|
+
|
|
36
|
+
```ts title="modules/zuplo.runtime.ts"
|
|
37
|
+
import { RuntimeExtensions } from "@zuplo/runtime";
|
|
38
|
+
import { McpGatewayPlugin } from "@zuplo/runtime/mcp-gateway";
|
|
39
|
+
|
|
40
|
+
export function runtimeInit(runtime: RuntimeExtensions) {
|
|
41
|
+
runtime.addPlugin(
|
|
42
|
+
new McpGatewayPlugin({
|
|
43
|
+
basePath: "/__zuplo",
|
|
44
|
+
registry: {
|
|
45
|
+
enabled: true,
|
|
46
|
+
path: "/mcp-registry",
|
|
47
|
+
},
|
|
48
|
+
}),
|
|
49
|
+
);
|
|
50
|
+
}
|
|
51
|
+
```
|
|
52
|
+
|
|
53
|
+
## Options
|
|
54
|
+
|
|
55
|
+
| Option | Type | Default | Description |
|
|
56
|
+
| ---------- | --------------------------- | ------------ | -------------------------------------------------------------------------------------------------------------------------------- |
|
|
57
|
+
| `basePath` | `string` | `"/__zuplo"` | Base path for all gateway internal routes (OAuth, well-known, registry). Must start with `/` and can't collide with route paths. |
|
|
58
|
+
| `registry` | `McpGatewayRegistryOptions` | See below | Configures the read-only [MCP Server Registry](../mcp-gateway/server-registry.mdx). |
|
|
59
|
+
|
|
60
|
+
### `basePath`
|
|
61
|
+
|
|
62
|
+
The base path prefixes every internal URL the gateway serves — OAuth endpoints
|
|
63
|
+
(`/oauth/*`), well-known metadata (`/.well-known/*`), and the registry mount
|
|
64
|
+
(`/mcp-registry`). With the default `"/__zuplo"`, the token endpoint lives at
|
|
65
|
+
`/__zuplo/oauth/token` and the registry at `/__zuplo/mcp-registry`.
|
|
66
|
+
|
|
67
|
+
```ts
|
|
68
|
+
new McpGatewayPlugin({ basePath: "/internal" });
|
|
69
|
+
// OAuth token: /internal/oauth/token
|
|
70
|
+
// Registry: /internal/mcp-registry
|
|
71
|
+
```
|
|
72
|
+
|
|
73
|
+
### `registry`
|
|
74
|
+
|
|
75
|
+
Configures the read-only MCP Server Registry API (v0.1) that advertises the
|
|
76
|
+
gateway's registered MCP routes. See
|
|
77
|
+
[MCP Server Registry](../mcp-gateway/server-registry.mdx) for the endpoint
|
|
78
|
+
reference and behavior.
|
|
79
|
+
|
|
80
|
+
| Option | Type | Default | Description |
|
|
81
|
+
| --------- | --------- | --------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
82
|
+
| `enabled` | `boolean` | `true` | Set to `false` to disable the registry entirely. No registry routes are registered when disabled. |
|
|
83
|
+
| `path` | `string` | `"{basePath}/mcp-registry"` | Absolute URL path where the registry API mounts. Registry endpoints live below this path (for example, `{path}/v0.1/servers`). Validated like `basePath`. |
|
|
84
|
+
|
|
85
|
+
The registry path is validated with the same schema as `basePath`, plus a
|
|
86
|
+
boot-time guard that rejects mounts shadowing the OAuth discovery
|
|
87
|
+
(`/.well-known`) or authorize wildcard routes.
|
|
88
|
+
|
|
89
|
+
:::note
|
|
90
|
+
|
|
91
|
+
When `registry.enabled` is `false`, the path collision guard is skipped — a
|
|
92
|
+
disabled registry registers no routes and must not fail boot over a mount that
|
|
93
|
+
will never exist.
|
|
94
|
+
|
|
95
|
+
:::
|
|
96
|
+
|
|
97
|
+
#### Default mount
|
|
98
|
+
|
|
99
|
+
With the default `basePath` of `"/__zuplo"` and no `registry` override, the
|
|
100
|
+
registry is served at:
|
|
101
|
+
|
|
102
|
+
```text
|
|
103
|
+
/__zuplo/mcp-registry/v0.1/servers
|
|
104
|
+
```
|
|
105
|
+
|
|
106
|
+
#### Custom registry path
|
|
107
|
+
|
|
108
|
+
Set `registry.path` to mount the registry independently of `basePath`:
|
|
109
|
+
|
|
110
|
+
```ts
|
|
111
|
+
new McpGatewayPlugin({
|
|
112
|
+
registry: { path: "/registry" },
|
|
113
|
+
});
|
|
114
|
+
// Registry: /registry/v0.1/servers
|
|
115
|
+
// OAuth: /__zuplo/oauth/token (basePath unchanged)
|
|
116
|
+
```
|
|
117
|
+
|
|
118
|
+
#### Disabled registry
|
|
119
|
+
|
|
120
|
+
Turn off the registry if you don't need server discovery:
|
|
121
|
+
|
|
122
|
+
```ts
|
|
123
|
+
new McpGatewayPlugin({
|
|
124
|
+
registry: { enabled: false },
|
|
125
|
+
});
|
|
126
|
+
```
|
|
127
|
+
|
|
128
|
+
## Related
|
|
129
|
+
|
|
130
|
+
- [MCP Server Registry](../mcp-gateway/server-registry.mdx) — the read-only
|
|
131
|
+
registry API the plugin serves by default.
|
|
132
|
+
- [Set up an MCP Gateway](../mcp-gateway/code-config/overview.mdx) — the how-to
|
|
133
|
+
that puts this plugin into a project with policies and routes.
|
|
134
|
+
- [How the MCP Gateway works](../mcp-gateway/how-it-works.mdx) — the
|
|
135
|
+
architecture and request lifecycle.
|
|
136
|
+
- [Gateway reference](../mcp-gateway/reference.mdx) — the full URL catalog,
|
|
137
|
+
default TTLs, and configuration constants.
|