ext-server-card/scripts/generate-schema.ts
Tadas Antanavicius 7a3ff5d452 Bootstrap repo with TypeScript source-of-truth and generated JSON Schema
Tracks SEP-2127 (MCP Server Cards) as an experimental extension while the SEP
itself is under review. The schema.ts is structured to be lifted directly into
schema/draft/schema.ts of the main spec when Server Cards graduate -- only the
inlined MetaObject and Icon definitions need to be removed during migration.

- schema.ts: ServerCard, Server, Repository, Remote, Package, Argument, Input,
  KeyValueInput, etc. plus inlined MetaObject and Icon
- scripts/generate-schema.ts: typescript-json-schema with 2020-12 transforms
- scripts/validate-examples.ts: ajv validator over examples/{ServerCard,Server}/
- examples/: 2 valid + 4 invalid Server Card cases, 1 valid Server case
- README: status, layout, schema URL conventions, graduation plan
- CI workflow: check + validate + format on every PR
2026-04-27 19:47:01 +00:00

56 lines
1.7 KiB
TypeScript

#!/usr/bin/env tsx
import { exec } from "child_process";
import { readFileSync, writeFileSync } from "fs";
import { promisify } from "util";
const execAsync = promisify(exec);
const SCHEMA_TS = "schema.ts";
const SCHEMA_JSON = "schema.json";
const CHECK_MODE = process.argv.includes("--check");
/**
* Apply JSON Schema 2020-12 transformations to the generated schema string.
*
* `typescript-json-schema` emits draft-07 by default; mirror the same
* post-processing the upstream MCP spec applies to its 2020-12 schemas.
*/
function applyJsonSchema202012Transformations(content: string): string {
return content
.replace(
/http:\/\/json-schema\.org\/draft-07\/schema#/g,
"https://json-schema.org/draft/2020-12/schema",
)
.replace(/"definitions":/g, '"$defs":')
.replace(/#\/definitions\//g, "#/$defs/");
}
async function generate(): Promise<string> {
const { stdout } = await execAsync(
`npx typescript-json-schema --defaultNumberType integer --required --skipLibCheck "${SCHEMA_TS}" "*"`,
);
return applyJsonSchema202012Transformations(stdout).trim() + "\n";
}
async function main(): Promise<void> {
if (CHECK_MODE) {
const existing = readFileSync(SCHEMA_JSON, "utf-8");
const expected = await generate();
if (existing.trim() !== expected.trim()) {
console.error(`✗ ${SCHEMA_JSON} is out of date. Run: npm run generate`);
process.exit(1);
}
console.log(`✓ ${SCHEMA_JSON} is up to date`);
} else {
const content = await generate();
writeFileSync(SCHEMA_JSON, content, "utf-8");
console.log(`✓ Generated ${SCHEMA_JSON}`);
}
}
main().catch((error) => {
console.error("Schema generation failed:", error);
process.exit(1);
});