Implementing SEP-2127 server cards on a 755-pack gateway
What we shipped, what was easy, the two ambiguities we flagged, and why some implementers are quietly picking the wrong .well-known path.
The MCP Server Card Working Group is rolling toward a discovery spec — SEP-2127, currently open as PR #2127 in the modelcontextprotocol/modelcontextprotocol repo. It defines a .well-known/mcp-server-card endpoint that lets clients discover a server’s identity, transports, and protocol versions before opening a connection — instead of completing a full initialize handshake just to see the name.
Pipeworx is the textbook multi-server-host case the SEP was written for: 755 packs served from one origin at gateway.pipeworx.io. We shipped a SEP-compliant implementation two days ago. This post covers what was easy, the two ambiguities we hit, and an observation about adoption drift that the spec text could fix before standardization.
What’s live now
Five paths against the gateway:
GET /.well-known/mcp-server-card # host card (io.pipeworx/gateway)
GET /.well-known/mcp-server-card/gateway # alias for host card
GET /.well-known/mcp-server-card/{slug} # per-pack card — 755 of them
GET /.well-known/mcp-server-card/<bad> # 404 with a discovery hint
GET /.well-known/mcp/server-card.json # 301 to the SEP path (legacy compat)
Each card follows the SEP-2127 schema exactly: $schema, name (reverse-DNS, one slash), version, title, description, websiteUrl, repository, icons, remotes (with headers[] for BYO-key packs), and _meta. Primitives are omitted per the spec — tools vary with the authenticated session, so clients still issue tools/list at runtime via the protocol.
Try it:
curl https://gateway.pipeworx.io/.well-known/mcp-server-card
curl https://gateway.pipeworx.io/.well-known/mcp-server-card/edgar
curl https://gateway.pipeworx.io/.well-known/mcp-server-card/attom
The attom card advertises its _apiKey as a non-required secret via the remotes[0].headers[] array. The edgar card has no headers section — it’s a keyless public-data pack.
What was unambiguous and easy
- Schema as a strict subset of
server.json. All the data already existed for every pack (slug, version, the remote URL, the BYO-key posture). No new metadata to source. - Path collisions. Our existing
.well-known/oauth-*routes didn’t conflict —mcp-server-cardis its own segment. - CORS + cache headers. One-liner each, both required by the spec.
Two ambiguities we hit
{server-name} matching when name contains a slash
The SEP requires name to be reverse-DNS with exactly one forward slash separating namespace from name (io.pipeworx/edgar). It also defines the per-server path as /.well-known/mcp-server-card/{server-name} and says {server-name} must match the name field.
URLs can’t carry unencoded slashes, so we matched on the post-slash segment: io.pipeworx/edgar is reachable at /.well-known/mcp-server-card/edgar, not /.well-known/mcp-server-card/io.pipeworx/edgar. Alex Akimov flagged this in his doc review on the PR and asked for a spec-text fix — agreed on our side. The wording in the example block needs to show the resolved URL explicitly.
headers[] semantics for tool-argument auth
Many of our packs accept an API key passed as a tool argument (_apiKey) rather than as an HTTP header. We used the remotes[].headers[] array to advertise it with isSecret: true and isRequired: false, but it’s technically not an HTTP header. For us this matters for ~150 BYO-key packs. A parallel toolArguments[] field, or an explicit note in the headers[] description about supporting tool-arg auth, would remove the ambiguity.
Where the spec language is steering implementers wrong
This one is the most actionable. At least three production implementations we’ve seen — including SkinKnowledgeBase and SIMOSphere AI — are serving their cards at /.well-known/mcp/server-card.json instead of the SEP-canonical /.well-known/mcp-server-card. Both paths are reasonable readings of “an MCP server card under .well-known,” but only one is the spec.
This divergence is happening before the SEP merges. The fix is small: a single-sentence callout in the SEP body, plus the schema’s $schema URL clearly using the SEP path.
We serve the legacy path as a 301 redirect to the SEP path so any client that adopted the wrong URL keeps working through the transition. Other implementers should consider doing the same.
What’s deliberately not in the card
A few things we’d find useful but agree don’t belong in the core SEP:
- A “default-pack” pointer for multi-server hosts. Right now the unsuffixed
/.well-known/mcp-server-cardreturns our gateway-level card; a client trying to pick one of our 755 packs has to either understand what the gateway does or calltools/list. A pointer to a recommended-default sub-card would help, but it’s a_metaextension question, not a core spec question. - Pricing / quality / SLA metadata. Several adjacent specs are working this space — most notably ASM (Agent Service Manifest), which rides in
_meta.io.modelcontextprotocol.registry/publisher-provided.asm. Keeping it out of the core server-card spec lets it iterate independently. - Primitive lists. The spec correctly excludes tools/resources/prompts. They vary with session authentication; a static document can’t represent them correctly.
tools/listat runtime is the right answer.
Why bother
For Pipeworx, three concrete reasons:
- Pre-connection discovery. Clients can see our 755-pack catalog and identity before opening a session. This matters for autoconfig, IDE-side install UX, and registry indexing.
- Per-pack discoverability. Each of our 755 packs now has its own stable, machine-readable identity document. Linked from the host card’s
_meta.io.pipeworx/per_pack_card_pattern. - Spec influence. Being one of the first production implementations during the WG’s active drafting phase gives our feedback weight on the open ambiguities — and helps the spec ship with the right wording for the multi-server-host case.
The spec is still open. If you’re building an MCP server, the path to use right now is /.well-known/mcp-server-card (without the /mcp/ and without the .json suffix). Comment on PR #2127 if you hit an ambiguity — it’s the most actively-reviewed SEP in the queue.