You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
feat(mcp): Server Card + Catalog discovery documents (#107)
Adds connection-less discovery metadata to the Streamable HTTP transport
(SEP-2127 / SEP-1649) so registries and crawlers can learn about the server
before connecting:
- Server Card at GET {path}/server-card (media type
application/mcp-server-card+json) — reverse-DNS name, version, description,
and the streamable-http remote endpoint with supportedProtocolVersions.
- MCP Catalog at GET /.well-known/mcp/catalog.json — site-wide index whose
entry points at the Server Card (urn:air:{publisher}:{name}).
Both endpoints are public (unauthenticated, CORS-enabled, cacheable) and served
before the auth check, like robots.txt / OAuth metadata. URLs derive from
QVERIS_MCP_PUBLIC_URL or the request (honoring X-Forwarded-Proto) so they're
correct behind a TLS proxy. Card metadata comes from package.json (mcpName,
version, description, homepage, repository) + the SDK's SUPPORTED_PROTOCOL_VERSIONS.
Pure builders in server-card.ts are unit-tested; the endpoints are covered
end-to-end (media type, CORS, preflight, public access under an auth token,
PUBLIC_URL override, 404 when no card info). 98 MCP tests pass. Full OAuth 2.1
remains the last #107 follow-up.
Copy file name to clipboardExpand all lines: packages/mcp/README.md
+6-1Lines changed: 6 additions & 1 deletion
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -296,7 +296,11 @@ npx -y @qverisai/mcp
296
296
-**Inbound auth:** set `QVERIS_MCP_HTTP_AUTH_TOKEN` to require `Authorization: Bearer <token>` on the MCP endpoint. The server **refuses to start** when binding a non-loopback host without a token, unless you set `QVERIS_MCP_HTTP_ALLOW_UNAUTHENTICATED=true` to delegate auth to an external proxy/gateway. Your `QVERIS_API_KEY` is the server's *outbound* credential to QVeris — it is **not** an inbound check, so anyone reaching an unauthenticated endpoint would spend your credits.
297
297
- DNS-rebinding protection is **on by default** (localhost + the bound host/port are allow-listed). When exposing the server publicly, add your public host via `QVERIS_MCP_ALLOWED_HOSTS`.
298
298
- Requests are capped at 4 MiB by default (`QVERIS_MCP_MAX_BODY_BYTES`), and idle sessions are evicted after 5 minutes (`QVERIS_MCP_SESSION_TIMEOUT_MS`).
299
-
- Full OAuth 2.1 and a `.well-known` MCP Server Card are tracked as follow-ups in [#107](https://github.com/QVerisAI/qveris-agent-toolkit/issues/107).
299
+
-**Discovery:** registries and crawlers can learn about the server without connecting:
300
+
-**Server Card** at `GET {path}/server-card` (default `/mcp/server-card`), media type `application/mcp-server-card+json` — server identity, version, and the remote endpoint.
301
+
-**MCP Catalog** at `GET /.well-known/mcp/catalog.json` — a site-wide index pointing at the Server Card.
302
+
- Both are public (unauthenticated, CORS-enabled), even when an auth token is set. Behind a TLS proxy, set `QVERIS_MCP_PUBLIC_URL` (or send `X-Forwarded-Proto`) so the advertised URLs use your public origin.
303
+
- Full OAuth 2.1 authorization is tracked as a follow-up in [#107](https://github.com/QVerisAI/qveris-agent-toolkit/issues/107).
300
304
301
305
## Environment Variables
302
306
@@ -317,6 +321,7 @@ npx -y @qverisai/mcp
317
321
|`QVERIS_MCP_HTTP_ALLOW_UNAUTHENTICATED`||`true` to allow a non-loopback bind without a token (auth delegated externally) |
318
322
|`QVERIS_MCP_MAX_BODY_BYTES`|| Max request body size in bytes (default `4194304`) |
319
323
|`QVERIS_MCP_SESSION_TIMEOUT_MS`|| Idle session TTL in ms (default `300000`) |
324
+
|`QVERIS_MCP_PUBLIC_URL`|| Public origin advertised in discovery documents (e.g. `https://mcp.example.com`) |
0 commit comments