Skip to content

Add Server Cards example (SEP-2127)#2692

Closed
dsp-ant wants to merge 1 commit into
mainfrom
server-card-example
Closed

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

Conversation

@dsp-ant

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

Copy link
Copy Markdown
Member

Summary

Adds a self-contained example package under examples/server-card/ demonstrating
MCP Server Cards (SEP-2127)
in Python: clients consuming and validating a card, and servers generating and
publishing one. It ports the TypeScript source of truth in
experimental-ext-server-card
and follows mcp.types conventions, so the mcp_server_card/ library could later
be lifted into mcp/experimental/server_card/ largely unchanged.

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, so a client can
discover and connect to it before initialization.

Layout

Path Purpose
mcp_server_card/types.py Pydantic port of the ServerCard / Server schema (camelCase wire format, reuses Icon)
mcp_server_card/schema.json Bundled JSON Schema (from the experimental repo)
mcp_server_card/validation.py JSON Schema validation + semantic guards (e.g. rejects version ranges) → typed models
mcp_server_card/client.py fetch_server_card / load_server_card / well_known_url
mcp_server_card/server.py build_server_card / write_server_card / mount_server_card / add_server_card_route
mcp_server_card/cli.py mcp-server-cardvalidate / fetch / schema
examples/, tests/ Runnable client + server flows, round-trip + conformance tests

Design

  • One type port, two consumers. types.py is the only place the schema is expressed; Icon is reused from mcp.types (already in the core spec).
  • Clients validate in two layers via parse_server_card / parse_server: JSON Schema (authoritative structure) + Pydantic constraints and semantic guards JSON Schema can't express. Failures raise ServerCardValidationError carrying every problem at once.
  • Servers build a card once (incl. server_card_from_implementation(...) to derive identity from an MCPServer), then either write_server_card(...) a static file or serve it from the .well-known path on a Starlette app / MCPServer.

Validation

  • pytest: 10 passing (round-trip, discriminated transport/argument unions, camelCase serialization, invalid-card rejection).
  • Cross-checked against the conformance fixtures in experimental-ext-server-card/examples/ — all valid cards parse, all invalid ones are rejected.
  • End-to-end verified: serve_card.py serve exposes the card over HTTP and consume_card.py fetches + validates it; bare-Starlette mount_server_card also verified.
  • ruff clean; package builds and installs with uv (bundled schema.json ships in the wheel; mcp-server-card entry point works).

Open questions

  • Well-known path: uses /.well-known/mcp/server-card per the experimental repo README; schema.ts comment says mcp-server-card (no subpath) — worth reconciling upstream. Parameterized everywhere.
  • Schema distribution: schema.json is bundled for offline validation; a real SDK integration would track the version published at static.modelcontextprotocol.io.
Adds a self-contained example package under examples/server-card showing
how clients can consume and validate an MCP Server Card and how servers
can generate and publish one.

- mcp_server_card/types.py: Pydantic port of the Server Card / Server schema,
  following mcp.types conventions (camelCase wire format, reuses Icon).
- mcp_server_card/validation.py: JSON Schema validation against the bundled
  schema plus semantic guards (e.g. rejecting version ranges).
- mcp_server_card/client.py: fetch/load + validate a card from the conventional
  .well-known location.
- mcp_server_card/server.py: build a card and either write it to a file or serve
  it from a Starlette app / MCPServer.
- mcp_server_card/cli.py: validate, fetch, and print the schema.
- examples/ and tests/ covering both the client and server flows.
for header in remote.headers or []:
flags = "required" if header.is_required else "optional"
secret = ", secret" if header.is_secret else ""
print(f" header {header.name} ({flags}{secret})")
@dsp-ant dsp-ant marked this pull request as draft May 26, 2026 16:11
@dsp-ant

dsp-ant commented May 26, 2026

Copy link
Copy Markdown
Member Author

Superseded by the in-SDK implementation (experimental namespaces) — see the new PR.

@dsp-ant dsp-ant closed this May 26, 2026
@dsp-ant dsp-ant deleted the server-card-example branch May 26, 2026 18:18
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

2 participants