Add experimental Server Cards support (SEP-2127)#2951
Draft
SamMorrowDrums wants to merge 3 commits into
Draft
Conversation
Adds SDK support for MCP Server Cards: static metadata documents that describe a remote server's identity, transport endpoints, and supported protocol versions for pre-connection discovery. - mcp.shared.experimental.server_card: Pydantic models (ServerCard, Server, Remote, Package, ...) mirroring mcp.types conventions and validating purely through Pydantic. - mcp.server.experimental.server_card: build_server_card derives a card from a server's identity; server_card_route / mount_server_card serve it from a Starlette app at /.well-known/mcp/server-card. - mcp.client.experimental.server_card: fetch_server_card / load_server_card / well_known_url ingest and validate a card. Full test coverage for the new modules.
Server cards are no longer served from a fixed .well-known path. Discovery now goes through an AI Catalog (https://github.com/Agent-Card/ai-catalog) published at /.well-known/ai-catalog.json, whose entries point at server cards hosted anywhere: - Add mcp.shared.experimental.ai_catalog: Pydantic models for the AI Catalog CDDL schema (entries, host, publisher, trust manifest), enforcing the url/data exclusivity and trust-manifest identity binding rules. The transitional MCP Catalog (/.well-known/mcp/catalog.json) is a structural subset and parses with the same models. - Add mcp.server.experimental.ai_catalog: build catalog entries from server cards (urn:mcp:server:<name>) and serve catalogs from the well-known path. - Add discover_server_cards(): fetch a host's catalog (AI Catalog path with fallback to the MCP Catalog path), then fetch or inline-validate every MCP server entry. Non-http(s) card URLs from the catalog are rejected. - Drop WELL_KNOWN_PATH and well_known_url; fetch_server_card now takes the card URL directly and server_card_route/mount_server_card require an explicit path. Review fixes: - Fix the version-range validator rejecting valid semver prereleases like 1.0.0-x; wildcard segments now only count in the release part, and bare "x"/"*" are caught. - Serve discovery documents with the CORS headers the spec requires (MUST) and Cache-Control (SHOULD), exported as DISCOVERY_HEADERS. - Restrict URL resolution to http(s) schemes to match its error message. - Rename httpx_client to http_client and default to create_mcp_http_client() (30s timeout) to match SDK conventions. - Document that lenient ingestion defaults a missing $schema/specVersion, diverging from the JSON Schema's required fields. - Correct the mount_server_card docstring: mounting does not bypass auth middleware. - Add missing test package __init__.py files; assert response headers and bodies in route tests; patch the SDK's own client factory instead of httpx.AsyncClient.
ffce78e to
e087080
Compare
SamMorrowDrums
commented
Jun 22, 2026
| display_name: str | ||
| """Human-readable name for the artifact.""" | ||
|
|
||
| media_type: str |
Author
There was a problem hiding this comment.
Note for reviewers: this field stays mediaType (its serialized alias) to match the currently merged discovery spec.
The extension repo's PR modelcontextprotocol/experimental-ext-server-card#32 (ADR 0014) renames the Catalog Entry mediaType member to type, but a maintainer has it on hold pending the upstream AI Catalog PR. So I'm intentionally not renaming this yet — I'll flip the alias here (and the client-side check in client/experimental/server_card.py) to type once #32 lands, rather than pre-empting an unresolved spec change. The value itself (application/mcp-server-card+json) is unaffected by that rename.
ac79b6f to
254b791
Compare
Bring the experimental Server Card support up to date with the latest extension spec (modelcontextprotocol/experimental-ext-server-card) and the AI Catalog discovery docs. This takes over and supersedes modelcontextprotocol#2696, which was stacked on the now-removed Tasks (SEP-1686) work. Conformance fixes: - Pin the Server Card `$schema` to `.../schemas/v1/server-card.schema.json` instead of accepting any `/v1/*.schema.json`; a card referencing the registry `server.schema.json` is now correctly rejected. - Use the canonical artifact media type `application/mcp-server-card+json` when serving and in catalog entries. - Derive AI Catalog entry identifiers as `urn:air:{publisher}:{name}`: the card name's reverse-DNS namespace is turned back into the publisher's forward-DNS domain (`com.example/weather` -> `urn:air:example.com:weather`), replacing the old `urn:mcp:server:` scheme. - Drop the registry-shaped `Server`/`packages` types (and the removed `server.schema.json` reference); v1 is card-only, with locally-runnable package metadata owned by the MCP Registry. `variables` now lives directly on `KeyValueInput`. - Default `server_card_route`/`mount_server_card` to the spec-reserved `/server-card` path. Restore the `experimental/__init__.py` package markers (regular packages, as on the original branch) so `py.typed` propagates and pyright stays clean now that the modules are no longer carried by the Tasks work. Document that a Server Card must be registered in an AI Catalog to be discoverable: clients learn a card's URL from a catalog entry rather than guessing it. Co-authored-by: David Soria Parra <davidsp@anthropic.com> Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
254b791 to
321181b
Compare
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.
Closes modelcontextprotocol/experimental-ext-server-card#16
Summary
Experimental SDK support for MCP Server Cards (SEP-2127) and AI Catalog discovery, opt-in under
mcp.{shared,server,client}.experimental. As an experimental extension, these APIs may change without notice.Takes over #2696 (thanks @dsp-ant — the original two commits are preserved with authorship), rebased cleanly onto current
main. #2696 was stacked on the Tasks (SEP-1686) work, since removed frommain(#2714).Opening as a draft for maintainer review — per the extension repo's contribution rules, AI-assisted changes are not self-merged.
What it does
A server builds a Server Card from its own identity, serves it from a Starlette app, and advertises it through an AI Catalog. A client discovers a host's catalog, then fetches and validates the cards it references. Cards describe identity and remote (HTTP) transport only — locally-runnable package metadata stays in the MCP Registry's
server.json.Conformance to the v1 schema
$schemais pinned tohttps://static.modelcontextprotocol.io/schemas/v1/server-card.schema.json; a card pointing at the registry'sserver.schema.jsonis rejected. A document that omits$schemais defaulted to the v1 URL rather than rejected.application/mcp-server-card+json; the client recognizes only this type and skips catalog entries declaring anything else.urn:air:{publisher}:{name}— the card name's reverse-DNS namespace turned back into the publisher's forward-DNS domain (com.example/weather→urn:air:example.com:weather)./.well-known/ai-catalog.json, with the MCP-scoped/.well-known/mcp/catalog.jsonas a fallback. Discovery responses carry the CORS headers the spec requires (MUST) andCache-Control(SHOULD).versionaccepts exact versions only; ranges and wildcards (^1.2.3,~1.2.3,>=1.2.3,1.x,1 || 2,1 - 2) are rejected.Verification
pytest tests/experimental/,ruff check/ruff format --check, andpyrightall pass; the full CI matrix (Python 3.10–3.14, Ubuntu + Windows, client/server conformance) is green.🤖 Conformance commit assisted by GitHub Copilot.