build(schema): TypeScript source-of-truth pipeline for server.json + OpenAPI inversion#1380
Draft
tadasant wants to merge 4 commits into
Draft
build(schema): TypeScript source-of-truth pipeline for server.json + OpenAPI inversion#1380tadasant wants to merge 4 commits into
tadasant wants to merge 4 commits into
Conversation
…son schema
Introduce the schema-definition layout used by the experimental Server Card
repo: a TypeScript source of truth (schema.ts) that generates the committed
draft/server.schema.json, plus generate/check/validate npm scripts and
example fixtures.
This is a strict no-op for the schema content. The generator reproduces Go's
encoding/json MarshalIndent output byte-for-byte (recursive key sort, HTML
escaping of <, >, &, U+2028/U+2029, two-space indent, trailing newline), so
draft/server.schema.json is regenerated identically -- there is no change to
that file in this diff.
- docs/reference/server-json/schema.ts: object-literal source of truth
- scripts/generate-schema.ts: generator with --check drift mode
- scripts/validate-examples.ts: Ajv (draft-07) validation of examples/
- examples/{valid,invalid}: fixtures, incl. cases exercising the not/anyOf
constraints that the typescript-json-schema idiom cannot express
- Makefile: repoint generate-schema/check-schema to the TS pipeline while
retaining the Go extract-server-schema -check as an openapi.yaml drift gate
- ci.yml: add Node setup so `make validate` can run the TS pipeline
- CONTRIBUTING.md: document the new schema.ts workflow
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Portability nit from self-review (Windows-safe vs file.split("/")).
Display-only; no behaviour change.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Complete the source-of-truth inversion: schema.ts is now the single source. openapi.yaml no longer duplicates the server.json definitions -- its components/schemas exposes each of the 15 server.json types as a thin external $ref into the generated draft/server.schema.json. The hand-authored REST-only schemas (ServerList, ServerResponse, StatusUpdateRequest, AllVersionsStatusResponse) stay inline. This removes the previous dual-source arrangement (the Go extract-server-schema drift gate and the "edit both files" burden). The Go tool is deleted; a small TS check (scripts/check-openapi.ts) asserts every definition has a stub and every stub targets a real definition. Semantic no-op for the schema content: running origin/main's openapi.yaml through the old extract transform reproduces the committed server.schema.json byte-for-byte, so the external $refs now resolve to identical content. The only textual change to server.schema.json is the $comment provenance line (now points at schema.ts); all schema constraints are byte-identical. - openapi.yaml: 15 inline server.json schemas -> external $ref stubs (-346 lines) - tools/extract-server-schema: removed (openapi is no longer the source) - scripts/check-openapi.ts: new stub/definition consistency check (uses yaml) - Makefile check-schema: drop the Go drift gate (now covered by the TS check) - schema.ts/server.schema.json: $comment provenance -> schema.ts - CONTRIBUTING.md: document the single-source workflow Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…ckout The external $ref stubs are relative-path refs into draft/server.schema.json, so the reference openapi.yaml is no longer self-contained for standalone tooling. Clarify that the self-contained, runtime-served spec is unaffected. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
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
Adopts the schema-definition infrastructure used by the experimental Server Card repo for the registry's
server.jsonschema, and makes that TypeScript file the single source of truth:schema.ts) generates the committeddraft/server.schema.json, plusgenerate/check/validatescripts and example fixtures.docs/reference/api/openapi.yamlno longer duplicates the 15server.jsondefinitions — itscomponents/schemasnow references the generated schema via thin external$refstubs. The old Go drift-gate (tools/extract-server-schema) and the "edit both files" burden are gone.This PR is a strict no-op for the schema content. It changes how
server.schema.jsonis produced and validated, not what constraints it contains. None of the Server Card proposal's actual schema changes are adopted here. The only textual change toserver.schema.jsonis its one-line$commentprovenance string (now points atschema.ts); every validation constraint is byte-identical.How it's a no-op (and proof)
The generator (
scripts/generate-schema.ts) reproduces Go'sencoding/jsonMarshalIndent(v, "", " ")output exactly: object keys sorted ascending, array order preserved, HTML escaping of<>&U+2028 U+2029 (Go'sSetEscapeHTMLdefault), two-space indent + trailing newline.Proof the inversion preserves content: running
origin/main'sopenapi.yamlthrough the oldextract-server-schematransform reproduces the previously-committedserver.schema.jsonbyte-for-byte (verified with a faithful reimplementation of the Go extractor: 15 definitions extracted, output=== committed file). The old inline OpenAPI definitions therefore defined exactly the schema content that the external$refs now resolve to. The git diff onserver.schema.jsonis a single line:Why an object literal (not
typescript-json-schemainterfaces)Server Card authors its schema as TS
interfaces + JSDoc and runstypescript-json-schema. That idiom cannot reproduce this schema as a no-op — several constructs have no faithful interface representation:Package.versionusesnot: { const: "latest" };PositionalArgumentusesanyOf: [{ required: ["valueHint"] }, { required: ["value"] }]; plusallOf+$refcomposition and heavyexampleannotations. Soschema.tsis a typed object literal holding the full JSON Schema document. Twoexamples/invalid/fixtures (package-version-latest.json,positional-argument-missing-value.json) deliberately exercise thenot/anyOfconstraints to lock in that these are preserved.The OpenAPI inversion
Previously
server.schema.jsonwas extracted fromopenapi.yamland a Go tool gated drift — makingopenapi.yamla second source you had to keep in sync. Now:openapi.yaml'scomponents/schemasexposes each of the 15 server.json types as{$ref: '../server-json/draft/server.schema.json#/definitions/<Name>'}. The 4 REST-only schemas (ServerList,ServerResponse,StatusUpdateRequest,AllVersionsStatusResponse) stay inline; their internal refs resolve to the stub aliases.tools/extract-server-schemais deleted.scripts/check-openapi.ts) asserts everyschema.tsdefinition has a matching stub and every stub targets a real definition — catching dangling refs and added/removed definitions. Wired intonpm run check(and thus CI'smake validate).Tradeoff (documented in
CONTRIBUTING.md): the referenceopenapi.yamlis no longer self-contained — its relative$refs need the repo checkout to resolve. This is reference-doc-only: the spec subregistries consume is the runtime one atregistry.modelcontextprotocol.io/openapi.yaml, generated independently from the Go types by huma, and is unaffected. The pre-existing OAS-3.1-doc → draft-07-file dialect mix is likewise inert (nothing dereferences the cross-dialect ref at runtime).Changes
docs/reference/server-json/schema.ts— object-literal source of truth ($comment→schema.ts)docs/reference/server-json/scripts/generate-schema.ts— generator +--checkdrift modedocs/reference/server-json/scripts/check-openapi.ts— new openapi$ref-stub consistency checkdocs/reference/server-json/scripts/validate-examples.ts— Ajv (draft-07 + formats) example validationdocs/reference/server-json/examples/{valid,invalid}/— fixturesdocs/reference/api/openapi.yaml— 15 inline server.json schemas → external$refstubstools/extract-server-schema/— deleted (openapi is no longer a source)package.json/package-lock.json/tsconfig.json/.gitignore— isolated Node project (tsx,typescript,ajv,ajv-formats,yaml)Makefile—generate-schema/check-schemaon the TS pipeline; Go drift gate removed.github/workflows/ci.yml— Node setup somake validateruns the TS pipelinedocs/reference/server-json/CONTRIBUTING.md— single-source workflowVerification
extract-server-schematransform, run onorigin/main'sopenapi.yaml, reproduces the previously-committedserver.schema.jsonbyte-for-byte (15 defs). The only diff in this PR'sserver.schema.jsonis the one-line$commentprovenance string; all constraints are byte-identical.npm ci):npm run generate→ regenerates byte-identicallynpm run check→✓ schema up to date+✓ openapi.yaml references all 15 definitions via external $ref+tsc --noEmitcleannpm run validate→ all 8 examples pass (3 valid clean, 5 invalid rejected;not/anyOfconfirmed exercised)check-openapi.tscatches drift — empirically tested in review: missing stub → exit 1; dangling ref → exit 1; inline-shape drift → exit 1..go/Makefile/CI/shell references remain;gopkg.in/yaml.v3is still used byinternal/api/openapi_compliance_test.go(go.mod stays tidy); that compliance test compares onlypaths(untouched), so it is unaffected.$refpath, stub↔definition completeness (all 15), REST-only schemas intact, drift-detection, and CI wiring. One should-fix doc note applied (openapi.yaml now needs the repo checkout to resolve).95b78db): Build/Lint/Validate (make validateruns the new openapi check), Tests, Analyze (go), Analyze (actions), CodeQL.🤖 Generated with Claude Code