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
Copy link
Copy Markdown
Member

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

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
Copy link
Copy Markdown
Member Author

dsp-ant commented May 26, 2026

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

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants