Add Server Card / server.json TypeScript types (SEP-2127)#2652
Closed
tadasant wants to merge 7 commits into
Closed
Add Server Card / server.json TypeScript types (SEP-2127)#2652tadasant wants to merge 7 commits into
tadasant wants to merge 7 commits into
Conversation
Adds the typed surface for SEP-2127 (MCP Server Cards) to schema/draft/schema.ts so the existing schema-generation pipeline emits a JSON Schema for the Server Card and server.json shapes alongside the protocol types. ServerCard describes the discovery-oriented document published at /.well-known/mcp-server-card. Server extends ServerCard with packages? for the registry's server.json shape. Field set mirrors the registry's docs/reference/server-json/draft/server.schema.json with the SEP's intentional modifications (structured headers on Remote in place of variables, plus supportedProtocolVersions on Remote and Package). Reuses existing MetaObject and Icon from schema.ts rather than redefining them. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
…default tags Per fresh-eyes review of modelcontextprotocol#2652: - Add `^(?!latest$).+$` pattern on `Package.version` to match the registry's intent of rejecting the literal string "latest" (registry expresses this as `not: { const: "latest" }`; we use a negative-lookahead pattern since typescript-json-schema can't generate `not`). - Add `@default` JSDoc tags on `Input.format` (`"string"`), `Input.isRequired` (`false`), `Input.isSecret` (`false`), `PositionalArgument.isRepeated` (`false`), and `NamedArgument.isRepeated` (`false`) to match registry default values. - Add JSDoc note on `PositionalArgument.valueHint` describing the "set at least one of valueHint or value" requirement (registry expresses this as `anyOf: [required: [valueHint], required: [value]]`; typescript-json-schema cannot generate this, so we document it as a SHOULD in JSDoc).
Remove the bespoke `RemoteHeader` interface and reuse `KeyValueInput[]` for
`Remote.headers`, matching what the registry's `StreamableHttpTransport` and
`SseTransport` already use. Restore `Remote.variables: { [k]: Input }` and a
templated `url` pattern (`^(https?://[^\s]+|\{[a-zA-Z_]...$`) that allows
`{var}` placeholders, matching registry's URL template support.
This narrows the Server Card / registry deviation list considerably:
`Remote.headers`, `Remote.variables`, and `Remote.url` are now structurally
aligned with the registry; the only intentional Remote-side deviation that
remains is the `supportedProtocolVersions` addition (already called out as
its own deviation).
The shape difference vs registry — flat `Remote` with a `type` discriminator
field instead of `allOf [{anyOf [StreamableHttp, Sse]}, {variables}]` — is
preserved for ergonomic TypeScript usage; both produce equivalent validation.
Remove the `@pattern ^(?!latest$).+$` constraint and corresponding description notes from `Package.version`. The "no `latest`" rule is a registry-level publishing concern (the registry rejects unpinned versions because it serves as a catalog of fixed releases), not a property of the wire format / shape itself. Server Card / `server.json` consumers outside the registry context shouldn't have this constraint forced on them at the schema level. Validation tests updated to drop the corresponding negative case.
Was missed in the previous commit (Package.version no-latest removal).
Existing schema.ts has zero `@default` JSDoc tags anywhere in the file — introducing them only on the Server Card section is inconsistent with the file's conventions. The defaults that the registry asserts (`isRequired: false`, `isSecret: false`, `isRepeated: false`, `format: "string"`) are already implicit from the optional `?` and the standard "absent boolean is falsy" reading of optional fields, and they appear in the registry only because the registry's schema is auto-generated from an OpenAPI spec where default values are conventional. We don't have that constraint here.
Make `ServerCard.$schema` required (matches the SEP body's "Field Descriptions §0" entry which already lists it as required) and constrain the value to a URL of the form `https://static.modelcontextprotocol.io/schemas/v1/<name>.schema.json`. The `/v1/` segment replaces the registry's current date-based path (e.g. `/schemas/2025-12-11/`). Versioning by `vN` rather than by date means that minor, additive revisions of the v1 shape don't bump every published document's `$schema` URL — only a true breaking change to the wire format would warrant `/v2/`. The pattern intentionally accepts any filename under `/schemas/v1/` (rather than enumerating `server-card.schema.json` and `server.schema.json`) so that sibling files can be added later without a schema-source change here.
4 tasks
Member
Author
|
Closing this in favor of relaunching as an experimental extension while SEP-2127 is still under review: ➡️ modelcontextprotocol/experimental-ext-server-card#1 — modelcontextprotocol/experimental-ext-server-card#1 The TypeScript source-of-truth and generated JSON Schema move into the experimental-ext repo verbatim (with |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Adds the typed surface for SEP-2127 to
schema/draft/schema.tsso the existing schema-generation pipeline emits a JSON Schema for the Server Card andserver.jsonshapes alongside the protocol types.ServerCard— the discovery-oriented document intended for/.well-known/mcp/server-card.Server extends ServerCard— a strict superset addingpackages?for the registry'sserver.jsonshape.Repository,Remote,Package,PackageTransport(StdioTransport|StreamableHttpPackageTransport|SsePackageTransport),Argument(PositionalArgument|NamedArgument),Input,InputWithVariables,KeyValueInput.MetaObjectandIcontypes fromschema.tsrather than redefining them.Why
SEP-2127 introduces the Server Card as a static metadata document for HTTP-based MCP servers and re-frames the registry's
server.jsonasServerCard + packages. Today the SEP PR only contains the markdown spec — there are no machine-readable types, so consumers can't validate documents against the spec or generate clients/SDKs.This PR adds the artifact-engineering side: TS types whose generated JSON Schema mirrors the registry's
server.schema.json, with the SEP's intentional modifications applied.Approach
Single-file (Option A). Adds the new types into the existing
schema/draft/schema.tsrather than creating a separateserver-card.schema.ts/server.schema.tspair. This keeps the change minimal and follows the precedent of the existing pipeline (scripts/generate-schemas.ts) emitting oneschema.jsonper draft. The SEP's "two distinct schema URLs" intent (server-card.schema.jsonandserver.schema.json) is intentionally deferred — file/URL splitting can land in a follow-up PR if/when SEP-2127 lands. We acknowledge Server Cards aren't JSON-RPC, so co-locating them in the JSON-RPC schema file is conceptually a bit messy; the trade-off is a much smaller change footprint.Intentional deviations from the registry's
server.schema.jsonGenerated against
registry/main/docs/reference/server-json/draft/server.schema.json:Server._metareferences the protocol's existingMetaObject(Record<string, unknown>) rather than the registry's structured_metashape withio.modelcontextprotocol.registry/publisher-provided._metasemantics at the protocol's existing_metadefinition. The registry's structured shape can still be expressed viaMetaObjectsince it's an open record.Remote.supportedProtocolVersionsandPackage.supportedProtocolVersions(string[], optional).Iconreuses the existingschema.tstype —mimeTypeis an open string andsrchas nomaxLength: 255, where the registry'sIconconstrains both.Iconis already approved with these looser constraints; tightening it would be an out-of-scope protocol change.typefields emitted asconst(e.g.,"const": "stdio") rather thanenum(e.g.,"enum": ["stdio"]).typescript-json-schemalowering TS string literals. Validation is identical.allOfchains for shared types (Input⟶InputWithVariables⟶KeyValueInput) and anallOf [{anyOf [StreamableHttpTransport, SseTransport]}, {variables}]chain forRemoteTransport; ours emits flat objects with all fields inlined, withRemotecarrying thetypediscriminator directly.StreamableHttpTransportandSseTransportalready share the sameheaders: KeyValueInput[]and templatedurlpattern thatRemoteuses.LocalTransport→PackageTransport;StreamableHttpTransport→StreamableHttpPackageTransport;SseTransport→SsePackageTransport."streamable-http" | "sse"discriminators onRemote(Server-Card-only) so consumers can tell the package-side transport types apart from the remote-side ones. Downstream code generators that mirror registry naming will need a rename map.Package.versiondoes not enforce the registry's "no'latest'" rule.latest" constraint is a registry-level publishing policy (the registry catalogs fixed releases, so unpinned versions are rejected at publish time), not a property of theserver.jsonwire format. Server Card /server.jsonconsumers outside the registry context shouldn't have it forced on them at the schema level.PositionalArgumentdoes not enforce "at least one ofvalueHintorvalueis set" at the schema level.anyOf: [required: [valueHint], required: [value]];typescript-json-schemacannot generateanyOf-required. Documented as a SHOULD on the JSDoc forvalueHint. Concrete documents that include neither field will validate against the schema but are non-conformant per the SEP.$schemais required onServerCard/Server, withpatternrestricting the value tohttps://static.modelcontextprotocol.io/schemas/v1/<name>.schema.json.$schemaas optional and emits date-based (/schemas/2025-12-11/...) URLs. See the dedicated section below for rationale on switching to/v1/.The diff was produced by a script (
tmp/diff-schemas.mjs) that walks both schemas and reports per-property differences. After excluding the deviations above, the generatedServer$def matchesServerDetailexactly.$schemafield — required,/v1/URLs$schemais required onServerCard(and therefore onServer), matching the SEP body's "Field Descriptions" §0. The value is constrained bypatternto URLs of the formhttps://static.modelcontextprotocol.io/schemas/v1/<name>.schema.json— versioning the schema byvNsegment rather than by date (the registry currently emits/schemas/2025-12-11/server.schema.json-style URLs) means minor additive revisions of the v1 shape won't bump every published document's$schemaURL. Only a true breaking change to the wire format would warrant/v2/. This is a behavioral departure from the registry's publishedserver.schema.json(registry treats$schemaas optional and currently emits date-based URLs); flagging here so that the registry side can adopt the same convention if/when SEP-2127 lands.Note for SEP finalization
.well-knownURL —mcp-server-cardvsmcp/server-card. The SEP body uses/.well-known/mcp-server-cardconsistently (including the IANA registration paragraph saying "URI suffix:mcp-server-card"), but the SEP PR description in SEP-2127: MCP Server Cards - HTTP Server Discovery via .well-known #2127 uses/.well-known/mcp/server-card.json. This PR uses/.well-known/mcp/server-cardto match the PR description (and keep room for future siblings under/.well-known/mcp/), but the SEP markdown should be updated for consistency.Verification
npm run checkpasses locally (schema-ts, schema-json, schema-examples, schema-md, docs-format, docs-js-comments, docs-broken-links, seps).schema/draft/schema.jsonregenerated and committed; new$defs:ServerCard,Server,Repository,Remote,Package,PackageTransport,StdioTransport,StreamableHttpPackageTransport,SsePackageTransport,Argument,Input,InputWithVariables,KeyValueInput,PositionalArgument,NamedArgument.docs/specification/draft/schema.mdxregenerated to render the new## Server Cardssection.Remoteshape aligned with registry'sRemoteTransport(commit08b83b80):headersreusesKeyValueInput[],variables: { [k]: Input }map is restored, andurlcarries the registry's templated pattern (^(https?://[^\s]+|\{[a-zA-Z_][a-zA-Z0-9_]*\}[^\s]*)$) sohttps://{region}.api.example.com/mcp-style URLs validate. The bespokeRemoteHeaderinterface was removed.SEP example documents validate against the generated schema (Ajv 2020-12 against
#/$defs/ServerCardand#/$defs/Server). Both example documents fromseps/2127-mcp-server-cards.mdvalidate clean, including a templated remote URL withheaders(asKeyValueInput), avariablesmap, and the new required$schemafield set to a/v1/URL.Negative tests pass (9/9): missing
name, missingversion, missingdescription, malformedname(no slash), unknown remotetype(e.g.,"websocket"),description > 100chars, missing$schema, date-based$schemaURL (e.g.,/schemas/2025-12-11/...), and off-host$schemaURL are all correctly rejected.JSON-Schema diff against registry —
tmp/diff-schemas.mjswalks both schemas (withallOf/anyOfflattening) and reports per-property differences. After excluding the deviations above, the generatedServermatches the registry'sServerDetailexactly;RemotematchesRemoteTransportexactly modulo thesupportedProtocolVersionsaddition (deviation Some way to substitute resources into prompts #2).Fresh-eyes subagent review completed; feedback addressed (
PositionalArgumentSHOULD JSDoc, naming deviation row,$schemaSEP/registry note) in commit3f0ed648. The@defaultJSDoc tags suggested in review were rolled back in9eac472a—schema.tsdoesn't use@defaultanywhere else, so introducing them only here was inconsistent.CI green on latest revision (commit
ad1fa665):validate,format,handle,labelall passing. Latest run.What this PR does NOT do
scripts/generate-schemas.tsor any generator.server-card.schema.json/server.schema.jsonfiles (deferred — see above).2024-11-05,2025-03-26,2025-06-18) or the released2025-11-25schema. New types are scoped todraftsince SEP-2127 is Draft status.schema/draft/examples/. The SEP's inline examples are validated locally during this PR's verification but not committed as repo fixtures (follow-up).Related
server.schema.json