Skip to content

Add experimental Server Cards support (SEP-2127)#2696

Open
dsp-ant wants to merge 1 commit into
mainfrom
experimental-server-card
Open

Add experimental Server Cards support (SEP-2127)#2696
dsp-ant wants to merge 1 commit into
mainfrom
experimental-server-card

Conversation

@dsp-ant
Copy link
Copy Markdown
Member

@dsp-ant dsp-ant commented May 26, 2026

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

Module Purpose
mcp.shared.experimental.server_card Pydantic models (ServerCard, Server, Remote, Package, transports, args, ...), mirroring mcp.types conventions (camelCase wire format, reuses Icon)
mcp.server.experimental.server_card build_server_card (derive from a server's identity) + server_card_route / mount_server_card (Starlette)
mcp.client.experimental.server_card fetch_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).

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.
@maxisbey
Copy link
Copy Markdown
Contributor

Is this SEP going to be in the current spec release? Otherwise maybe this PR should be draft

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants