Add experimental Server Card extension support (SEP-2127)#2527
Draft
claude[bot] wants to merge 10 commits into
Draft
Add experimental Server Card extension support (SEP-2127)#2527claude[bot] wants to merge 10 commits into
claude[bot] wants to merge 10 commits into
Conversation
…2127) New public subpath @modelcontextprotocol/core/experimental/server-card: Zod v4 schemas hand-ported from the extension repo's authoritative schema.ts, inferred types, and the five wire constants. Objects are open (looseObject), IconSchema is reused from the core spec schemas, and the schema stays strict (missing/wrong $schema rejected) so the vendored conformance fixtures round-trip verbatim. Version ranges are rejected at the validator level per spec prose.
New public subpath @modelcontextprotocol/server/experimental/server-card: buildServerCard/buildAICatalog validate at startup, getServerCardUrl computes the reserved <mcp-url>/server-card location, serverCardCatalogEntry derives the 4-segment urn:air identifier, and serverCardResponse/aiCatalogResponse answer GET/HEAD/OPTIONS with permissive CORS, Cache-Control, and a strong SHA-256 ETag with If-None-Match 304 handling. Matching is synchronous and unmatched paths fall through as undefined, mirroring oauthMetadataResponse.
…liation
New public subpath @modelcontextprotocol/client/experimental/server-card:
fetchServerCard/fetchAICatalog send credential-free GETs with the
canonical Accept type, enforce HTTPS-only and private-address guards on
every redirect hop, cap response size and redirect count, verify the
media type essence, apply lenient missing-$schema ingestion, and return
etag/cacheControl so the caller owns the cache. discoverServerCards
walks the well-known AI Catalog (404/410 is an empty cacheable miss)
with listing-chain provenance and per-entry error reporting.
requiredRemoteInputs/resolveRemote handle the {var} template grammar
with one aggregated missing-input error, and reconcileServerCard diffs
card claims against the live serverInfo without ever throwing.
…ders New subpath @modelcontextprotocol/express/experimental/server-card: a thin Router that converts the Express request to a fetch Request, delegates to serverCardResponse/aiCatalogResponse, and writes the Response back. No MCP functionality; unmatched paths call next().
…tegration test The topology pins gain the ./experimental/server-card entries on core, client, server, and express. The integration workspace composes the card and catalog responders in front of createMcpHandler on a real HTTP server, then discovers, resolves, connects, and reconciles with the public client helpers.
docs/advanced/server-cards.md covers serving (fetch-API and Express, including the exact-mount caveat), domain discovery with listing-chain consent guidance, input resolution and reconciliation, and caller-owned ETag caching. All fences sync from examples/guides/advanced/server-cards.examples.ts, which runs the whole flow in process and produces the quoted output.
HTTP-only self-verifying pair: the server exposes an MCP endpoint plus its card and AI Catalog; the client is told only the origin, probes the well-known catalog, validates and resolves the card, connects, calls a tool, and reconciles the card against the live serverInfo.
Minor for the fixed group (core, client, server), patch for express. Additive experimental subpaths only; no migration-guide entry needed.
- apply discoverServerCards' maxEntries after the card-type filter, so non-card entries in a mixed catalog can no longer silently starve discovery of every Server Card - always exempt the local-dev hosts (localhost, 127.0.0.1, [::1]) from the private-address guard, matching the allowHttp JSDoc and the extension's local-dev policy; drop the now-unneeded overrides from the integration test and example story - block NAT64 (64:ff9b::/96), 6to4 (2002::/16), and IPv4-compatible IPv6 embeddings of private/metadata IPv4 targets in the SSRF guard, via a full 8-word IPv6 expansion that fails closed - report unparseable discovery URLs as 'invalid-url' and per-entry transport failures as 'network-error' instead of misfiling them under 'blocked-host'/'invalid-server-card'; document the SyntaxError cause for non-JSON bodies on the two validation codes - stop listing nested variables of valueless headers in requiredRemoteInputs: resolveRemote never reads them, so a consent UI would prompt for inputs that are then ignored - print 'inline' instead of undefined for inline entries in the docs guide snippet and the discovery example story
- serverCardCatalogEntry re-validates card.name, so a hand-cast card without a namespace fails at boot with a ZodError instead of minting a garbage URN - barrelClean runtime-neutrality traversal now follows ../-relative chunk imports, so nested subpath entries' shared chunks are actually scanned instead of only the thin entry file - getServerCardUrl documents that query and fragment are dropped; readBodyWithCap documents the buffered fallback for stream-less responses; reconcileServerCard documents the namespaced-name comparison; requiredRemoteInputs warns it returns optional inputs too - CLAUDE.md experimental section describes the new server-card subpaths instead of claiming the directory is empty; the vendored fixtures README acknowledges the Prettier reformatting so the refresh workflow does not produce spurious diffs
🦋 Changeset detectedLatest commit: f07aff9 The changes in this PR will be included in the next version bump. This PR includes changesets to release 10 packages
Not sure what this means? Click here to learn what changesets are. Click here if you're a maintainer who wants to add another changeset to this PR |
@modelcontextprotocol/client
@modelcontextprotocol/codemod
@modelcontextprotocol/core
@modelcontextprotocol/server
@modelcontextprotocol/server-legacy
@modelcontextprotocol/express
@modelcontextprotocol/fastify
@modelcontextprotocol/hono
@modelcontextprotocol/node
commit: |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Requested by David Soria Parra · Slack thread
Before / After
Before: a client has no way to learn anything about a remote MCP server before opening a connection. There is no standard place to find a server's endpoints, auth requirements, or capabilities, and no standard way for a domain to list the MCP servers it hosts.
After: servers can publish a Server Card (a JSON document describing the server and its remote endpoints) and an AI Catalog at
/.well-known/ai-cataloglisting the cards a domain hosts or references. Clients can discover a domain's catalog, fetch and validate the cards it points to, resolve a remote entry into a concrete URL plus headers, and after connecting compare the card against what the live server actually reported.How
Everything ships behind a new
./experimental/server-cardsubpath on four packages, so nothing changes for existing users and the stable barrels stay untouched.@modelcontextprotocol/core/experimental/server-card: Zod schemas, inferred types, and wire constants for Server Cards and AI Catalogs (ServerCardSchema,AICatalogSchema,SERVER_CARD_MEDIA_TYPE,AI_CATALOG_WELL_KNOWN_PATH, and friends). Schema tests round-trip the spec repo's fixture corpus.@modelcontextprotocol/server/experimental/server-card:buildServerCard(prefills fromserverInfo, explicit fields win, throwsZodErroron invalid input) plus web-standardserverCardResponseandaiCatalogResponseresponders that take aRequestand return aResponse. The responders handle GET/HEAD/OPTIONS, permissive CORS for public metadata,Cache-Control, and a SHA-256 basedETagwithIf-None-Match304 handling. Helpers cover catalog assembly (serverCardCatalogEntry,buildAICatalog) and URL derivation (getServerCardUrl).@modelcontextprotocol/client/experimental/server-card:fetchServerCard,fetchAICatalog, anddiscoverServerCardswith hardening for fetching attacker-suppliable URLs: HTTPS required by default (localhost exempt), a private/reserved address guard covering IPv4 and IPv6 including NAT64, 6to4, and mapped-IPv4 embeddings, a 1 MiB response size cap, a redirect cap of 3 with every hop re-checked, and per-entry failure isolation in discovery (one bad catalog entry surfaces viaonEntryErrorwithout sinking the rest).resolveRemoteandrequiredRemoteInputsturn a card's remote entry plus user inputs into a connect-ready URL and headers.reconcileServerCardcompares a card against the live server'sserverInfoand returns advisory mismatches only, it never blocks a connection. Errors are typed viaServerCardErrorwith a stable code enum.@modelcontextprotocol/express/experimental/server-card:mcpServerCardRouter, a thin Express adapter over the two responders.Links
Notes
examples/server-card-discoverystory (server + client) picked up by the examples runner.pnpm build:all,pnpm check:all, andpnpm test:allare green at the head commit.Generated by Claude Code