Add experimental Server Cards support (SEP-2127)

Closed
DS
3mo ago
+1760-0

Summary

Adds first-class SDK support for MCP Server Cards (SEP-2127) under the experimental namespaces, so:

  • servers can quickly generate a card from their identity and hand it to Starlette for serving, and
  • clients can fetch and validate (ingest) one for pre-connection discovery.

A Server Card is a static metadata document (typically at https://<host>/.well-known/mcp/server-card) describing a remote server's identity, transport endpoints, and supported protocol versions. It deliberately omits primitive listings (tools/resources/prompts), which stay subject to runtime listing.

API

# Server: generate from identity, then serve via Starlette
from mcp.server.experimental.server_card import build_server_card, mount_server_card
from mcp.shared.experimental.server_card import Remote

card = build_server_card(
    server,                                              # low-level Server or MCPServer
    name="io.modelcontextprotocol.examples/dice-roller", # reverse-DNS card name
    remotes=[Remote(type="streamable-http", url="https://dice.example.com/mcp")],
)

app = server.streamable_http_app()
mount_server_card(app, card)            # GET /.well-known/mcp/server-card
# or: Starlette(routes=[server_card_route(card)])
# or write a file: card.model_dump_json(by_alias=True, exclude_none=True)

# Client: ingest + validate
from mcp.client.experimental.server_card import fetch_server_card
card = await fetch_server_card("https://dice.example.com")

Layout

ModulePurpose
mcp.shared.experimental.server_cardPydantic models (ServerCard, Server, Remote, Package, transports, args, ...), mirroring mcp.types conventions (camelCase wire format, reuses Icon)
mcp.server.experimental.server_cardbuild_server_card (derive from a server's identity) + server_card_route / mount_server_card (Starlette)
mcp.client.experimental.server_cardfetch_server_card / load_server_card / well_known_url

Design notes

  • Pure-Pydantic validation, consistent with the rest of mcp.types — no separate JSON-Schema/CLI layer. Malformed cards raise pydantic.ValidationError; version ranges (which the field pattern can't express) are rejected by a validator.
  • $schema defaults to the canonical v1 URL so generation needs no boilerplate; ingestion is lenient about a missing $schema.
  • The ServerCard vs Server (registry server.json) split matches the spec; Server adds packages.
  • Experimental: importable from submodules only, not added to the top-level mcp API surface.

Tests

  • 28 tests across tests/experimental/server_card/; 100% line + branch coverage on the three new modules (verified with strict-no-cover).
  • ruff + pyright clean.
  • Round-trips the SEP-2127 conformance example shapes (minimal, templated remote with header variables, registry Server with a package); server→client verified end-to-end over an in-memory ASGI transport.

Supersedes #2692 (which added this only as a standalone example app).