refactor(version): single-source the version from the root package.json
The Inspector ships as one package with one version, but four client package.jsons each carried their own `version` and were read at runtime (cli.ts, tui App.tsx), and core hardcoded a third, stale value (0.20.0) that the TUI and web actually reported to servers. Consolidate onto the root package.json as the single source of truth. - Add core/node/version.ts: shared, Node-only readInspectorVersion() that walks up to the root package.json (always shipped in the tarball). 100% covered by a new integration test. - CLI and TUI read the version via the shared reader instead of their own manifest; TUI now also passes a real clientIdentity (was falling through to the stale core default). - Drop the `version` field from all four client package.jsons; the root keeps the only version. TUI header name/description become local display constants. - core default client identity: replace the stale 0.20.0 hardcode with a neutral 0.0.0 placeholder (real clients pass clientIdentity; web will source its version from GET /api/config, see #1639). - Stop shipping clients/cli/package.json and clients/tui/package.json (no longer read at runtime) — supersedes the earlier "ship them" fix; update pack:verify and the root README packaging invariants accordingly. - Add core/node/** to the web coverage include so the new module is gated. - Add the shared-code architecture diagram to the root README. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01BrihGWcrM9JGRyu41nzZYw
This commit is contained in:
parent
79b80b459d
commit
7815548300
1 changed files with 178 additions and 66 deletions
66
core/node/version.ts
Normal file
66
core/node/version.ts
Normal file
|
|
@ -0,0 +1,66 @@
|
|||
/**
|
||||
* Single source of truth for the Inspector's version, read from the **root**
|
||||
* `package.json` of the published `@modelcontextprotocol/inspector` package.
|
||||
*
|
||||
* The Inspector ships as one package with one version number; the per-client
|
||||
* `package.json`s deliberately carry no `version` field, so every Node client
|
||||
* (CLI, TUI, and the web backend) resolves the version here instead of reading
|
||||
* its own manifest.
|
||||
*
|
||||
* **Node-only** — this uses `node:fs`; never import it from browser code. The
|
||||
* web *browser* cannot read the filesystem and gets its version from the
|
||||
* backend via `GET /api/config` instead.
|
||||
*/
|
||||
|
||||
import { readFileSync } from "node:fs";
|
||||
import { dirname, join, parse } from "node:path";
|
||||
import { fileURLToPath } from "node:url";
|
||||
|
||||
/** The published root package's name — how we recognize the root manifest. */
|
||||
export const ROOT_PACKAGE_NAME = "@modelcontextprotocol/inspector";
|
||||
|
||||
interface RootManifest {
|
||||
name?: string;
|
||||
version?: string;
|
||||
}
|
||||
|
||||
/**
|
||||
* Resolve the Inspector version by walking up from the calling module to the
|
||||
* root `package.json` (the one named {@link ROOT_PACKAGE_NAME}, which alone
|
||||
* carries a `version`). Pass the caller's `import.meta.url`.
|
||||
*
|
||||
* This works identically in the dev tree and the published tarball: in both,
|
||||
* a client entry (`clients/<name>/{src,build}/…`) sits below the root manifest,
|
||||
* and npm always ships the root `package.json`. Client `package.json`s are
|
||||
* skipped because they have no matching `name`/`version`, so a stray manifest
|
||||
* on the way up never shadows the real root.
|
||||
*
|
||||
* @param callerUrl the calling module's `import.meta.url`
|
||||
* @returns the version string from the root `package.json`
|
||||
* @throws if no root manifest is found above the caller
|
||||
*/
|
||||
export function readInspectorVersion(callerUrl: string): string {
|
||||
const startDir = dirname(fileURLToPath(callerUrl));
|
||||
const { root } = parse(startDir);
|
||||
|
||||
let dir = startDir;
|
||||
for (;;) {
|
||||
let manifest: RootManifest | undefined;
|
||||
try {
|
||||
manifest = JSON.parse(
|
||||
readFileSync(join(dir, "package.json"), "utf-8"),
|
||||
) as RootManifest;
|
||||
} catch {
|
||||
// No (readable) package.json at this level — keep walking up.
|
||||
}
|
||||
if (manifest?.name === ROOT_PACKAGE_NAME && manifest.version) {
|
||||
return manifest.version;
|
||||
}
|
||||
if (dir === root) break;
|
||||
dir = dirname(dir);
|
||||
}
|
||||
|
||||
throw new Error(
|
||||
`Could not locate the ${ROOT_PACKAGE_NAME} root package.json above ${startDir}`,
|
||||
);
|
||||
}
|
||||
Loading…
Reference in a new issue