perf(catalog): record default catalog digests at build time (#4041)

Every `doctor`, `mcp doctor` and MCP server that reports or compares its
catalog identity rebuilt all advertised JSON Schemas and canonicalized
them to compute the default digests, about 200 ms of CPU per process.
The default (full input and output schema) catalog is a deterministic
function of the compiled contracts and pinned schema converter, so
compilation now records it once into dist and the runtime reuses it.
Compact and output-schema-off identities, and source runs, still compute
their digests on first use.

Refs #3813

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
Kaoru0822andClaude Opus 5.5 authored and GitHub committed 2026-10-15 02:19:38 -07:00
1 parent d7651b6a0d
commit c61c108b52
5 files changed
+43 -4

No files matched your search

+2 -2
View File
@@ -291,8 +291,8 @@
"verify:solidity:metadata": "npm run build:cached && node scripts/verify-solidity-metadata.mjs",
"verify:recorded:crash": "npm run build:cached && node scripts/verify-recorded-crash.mjs",
"mcp-catalog:check:built": "node scripts/generate-mcp-tool-catalog.mjs --check",
"compile": "node scripts/clean-build-output.mjs && tsc -p tsconfig.build.json && node scripts/bundle-runtime.mjs",
"compile:termux": "node scripts/clean-build-output.mjs && node --max-old-space-size=3072 node_modules/typescript-termux/lib/tsc.js -p tsconfig.build.json && node scripts/bundle-runtime.mjs",
"compile": "node scripts/clean-build-output.mjs && tsc -p tsconfig.build.json && node scripts/record-catalog-digests.mjs && node scripts/bundle-runtime.mjs",
"compile:termux": "node scripts/clean-build-output.mjs && node --max-old-space-size=3072 node_modules/typescript-termux/lib/tsc.js -p tsconfig.build.json && node scripts/record-catalog-digests.mjs && node scripts/bundle-runtime.mjs",
"artifacts:mcp-catalog": "node scripts/generate-mcp-tool-catalog.mjs",
"artifacts:skills": "node scripts/generate-skill-metadata.mjs",
"artifacts:product-catalog": "node scripts/generate-product-catalog.mjs",
+19
View File
@@ -0,0 +1,19 @@
import { writeFile } from "node:fs/promises";
import { dirname, join, resolve } from "node:path";
import { fileURLToPath, pathToFileURL } from "node:url";
// Record the default catalog digests into the compiled runtime. Run after
// `tsc` and before bundling, so the digests come from the same compiled
// contracts and pinned schema converter that the runtime serves.
const compiledRoot = resolve(
dirname(fileURLToPath(import.meta.url)),
"../dist",
);
const { createCatalogDigests } = await import(
pathToFileURL(join(compiledRoot, "catalogIdentity.js")).href
);
await writeFile(
join(compiledRoot, "recordedCatalogDigests.js"),
`/** Recorded from the compiled default catalog by scripts/record-catalog-digests.mjs; do not edit. */\nexport const RECORDED_CATALOG_DIGESTS = Object.freeze(${JSON.stringify(createCatalogDigests())});\n`,
"utf8",
);
+13 -2
View File
@@ -17,6 +17,7 @@ import {
type CompactInputSchemaPresentation,
} from "./contracts/compactInputSchema.js";
import type { McpOutputSchemaProfile } from "./config/mcpOutputSchemaProfile.js";
import { RECORDED_CATALOG_DIGESTS } from "./recordedCatalogDigests.js";
const sortByName = (
contracts: readonly ToolContract[],
@@ -102,6 +103,8 @@ export const createCatalogDigests = (
} as const;
};
export type CatalogDigests = ReturnType<typeof createCatalogDigests>;
export const createCatalogIdentity = (
compactPresentation: CompactInputSchemaPresentation | undefined = undefined,
outputSchemaProfile: McpOutputSchemaProfile = "full",
@@ -110,7 +113,12 @@ export const createCatalogIdentity = (
compactPresentation === undefined
? undefined
: { budgetBytes: compactPresentation.budgetBytes };
let catalogDigests: ReturnType<typeof createCatalogDigests> | undefined;
// Building and canonicalizing every advertised schema dominates doctor and
// server startup, so the compiled runtime records the default digests once.
let catalogDigests: CatalogDigests | undefined =
selectedPresentation === undefined && outputSchemaProfile === "full"
? RECORDED_CATALOG_DIGESTS
: undefined;
return {
counts: {
cli_commands: CLI_COMMAND_NAMES.length,
@@ -128,5 +136,8 @@ export const createCatalogIdentity = (
} as const;
};
/** Stable, schema-sensitive identity; schema digests are computed on first use. */
/**
* Stable, schema-sensitive identity; schema digests are recorded at build time
* or computed on first use.
*/
export const CATALOG_IDENTITY = createCatalogIdentity();
+8
View File
@@ -0,0 +1,8 @@
import type { CatalogDigests } from "./catalogIdentity.js";
/**
* Digests of the default catalog (full input and output schemas), recorded
* from the compiled contracts by `scripts/record-catalog-digests.mjs` after
* compilation. Source runs leave this undefined and compute them on first use.
*/
export const RECORDED_CATALOG_DIGESTS: CatalogDigests | undefined = undefined;
+1
View File
@@ -13,6 +13,7 @@
"scripts/check-dependency-install.mjs",
"scripts/clean-build-output.mjs",
"scripts/bundle-runtime.mjs",
"scripts/record-catalog-digests.mjs",
"scripts/generate-package-metadata.mjs",
"src/**/*.{cts,mts,ts,tsx}",
"src/**/package.json",