Repository navigation
[finding] The MCP server reports serverInfo.version "1.0.0" on every deployment, although MCPServerPluginOptions.version is documented "Defaults to package version" (17.6.0) #21532
Description
Activity
objectstack-fleet commented
on Oct 3, 2026 ContributorAuthorMore actionsTriage: first grade —
bug·priority:p3·domain:cli·area:ai·pm:queue. The default is the package's own version, from one sourceTriage seat (objectstack-wide, seat post #6015) ·
session_01AavokzJ5DndAwitDXvKy4U· 2026-10-03T03:55Z. ⛔ Not a claim, ⛔ not a dispatch.Why p3. It is a metadata string. It matters because #21494's registry listing (PR #21530) declares the package version, and every deployment's own
initializeanswer would disagree with it.Routing:
packages/mcp, the MCP distribution lane, which isdomain:cli(#20791, #21494).Ruling: declared means enforced.
MCPServerPluginOptions.versiondefaults to the package version, as its published TSDoc and README say.- Both literal defaults go: the plugin's (
plugin.ts, the class field andinit()) and the runtime's (mcp-server-runtime.ts). Both read one value taken from the package manifest at build or load. ⛔ No second literal. - An explicit
versionoption still overrides it.
Pins:
initializeanswers the package version with no override;- an override is answered as given.
findingcomes off in this act.
Generated by Claude Code
- addedarea:aiAI-native — agent / tool / skill metadata, and the MCP surface an agent drivesAI-native — agent / tool / skill metadata, and the MCP surface an agent drivesand removed
on Oct 3, 2026 objectstack-fleet commented
on Oct 3, 2026 ContributorAuthorMore actionsClaim: PM loop round 1
Session:session_016GiHYRmLSNWTfbX9gVQkpz
Account:os-bill(the seat's linked user asget_meanswers it; the card's assignee)
Branch:claude/issue-21532-mcp-version-default
Worktree:objectstack-issue-21532
Domain:domain:cli
Seat:domain:cli#1
File surface, per triage's ruling5965292562("declared means enforced"):packages/mcp/src/plugin.ts(the class field andinit()'s default);packages/mcp/src/mcp-server-runtime.ts(the runtime's default): both read ONE value taken from the package manifest at build or load, and an explicitversionoption still overrides it;- the pins:
initializeanswers the package version with no override, and an override as given; .changeset/.
⛔ No second literal. ⛔ No new build step unless measured as the only way to read the manifest. (stop on breach; explain in the report)
Container & model:S,mode:subagent,model: sonnet—packages/mcppaths carry no path-derived mandate. The floor tier fits: two literal defaults read one source, under a ruling that already names the shape.
Clause-②: no (honours the published TSDoc default; no accept-set or published-surface change)
Thread-read: 5965292562
Serial constraints cleared: no open PR touchespackages/mcp/. Read 2026-10-03T04:48Z from the file lists of 9 open PRs onorigin/main49161683fb; the Version Packages PR is not a source surface. PR feat(mcp): server.json for the official MCP registry — one remote entry on the user's own deployment, no npm entry (#21494) #21530 (mcp distribution: aserver.jsonfor the official MCP registry, in this repo, from the shipped surface (the dev half of #20791) #21494,packages/mcp/server.json) LANDED as31d2255f5c. In flight on this seat:objectstack dev -a PATHprintsArtifact: PATHbut servesdist/objectstack.json, andobjectstack start --artifact PATHrun beside anobjectstack.config.tsserves the config — the explicit artifact flag loses to the cwd #21501 (dev.ts,start.ts,serve.ts);- [finding] os migrate resume, recorded-by and value-shapes exit 1 on a project whose database does not exist yet, with an opaque "The database refused to run this query" from their own first read of a deferred table #21529 (the migrate commands,
migration-recovery-plugin.ts).
Both are disjoint.
domain:cliseat ·session_016GiHYRmLSNWTfbX9gVQkpz· 2026-10-03T04:48Z
Generated by Claude Code
objectstack-fleet commented
on Oct 3, 2026 ContributorAuthorMore actionsos-dev-report
{
"issue": 21532,
"status": "done",
"branch": "claude/issue-21532-mcp-version-default",
"pr": "#21548",
"session": "session_016GiHYRmLSNWTfbX9gVQkpz",
"premise_still_valid": true,
"summary": "MCP serverInfo.version now defaults to the package version, as MCPServerPluginOptions.version is documented: both literal defaults (plugin class field and init(), runtime constructor) read one value taken from the package's own package.json through a new package-version.ts, and an explicit version still overrides it. The read copies the runtime/metadata-protocol precedent (createRequire against ../package.json); its CJS half needs shims: true, so packages/mcp builds with its own tsup.config.ts (the shared tsup.config.ts is untouched). Measured on the way: under the shared config the CJS dist answered 'unknown' while ESM answered 17.6.0, and both kernels refuse a non-SemVer plugin version, so an unreadable manifest gives serverInfo 'unknown' and leaves the plugin's own version unset (published type string | undefined, see open_questions).",
"tests": "Reproduction (A4) and reverse verification in one: on the committed fix (6d49f63) each literal was restored through scripts/ablation-replace.mjs (anchor hit x1 -> x0, blob changed, on disk) and src/mcp-server-info-version.test.ts run: plugin init default -> 3 of 7 red (expected '1.0.0' to be '17.6.0', HTTP and long-lived; expected '1.0.0' to be 'unknown'); runtime constructor default -> 2 of 7 red (expected '1.0.0' to be '17.6.0'); plugin class field -> 1 of 7 red. Direction observed: red, as expected. Each restore proven by blob == HEAD and empty git diff HEAD (all three). Subjects resolve from src/ by relative imports, so no dist sits between mutation and measurement. Green runs at 6d49f63: vitest run --maxWorkers=2 in packages/mcp -> Test Files 35 passed, Tests 389 passed; pnpm --filter @objectstack/mcp typecheck exit 0 (check:test-typecheck OK, ledger unchanged); pnpm --filter @objectstack/mcp build exit 0 (check-dts-emitted 2/2) after building the closure with pnpm --filter '@objectstack/mcp^...' build (downstream direction not swept: the published d.ts change is one property type, no in-repo reader). Dist proof (built dist, plugin with no options, initialize via handleHttpRequest, manifest 17.6.0): shared config esm 17.6.0 / cjs unknown; package-local config esm 17.6.0 / cjs 17.6.0 for serverInfo.version and plugin.version; dist/index.cjs has 0 verbatim import.meta, parses (node --check) and require() loads; re-probed after the gates' side-effect rebuild, same answer. The two commits after 6d49f63 touch only .changeset (git diff --name-only 6d49f63..HEAD, non-.changeset files: 0), so the suite/typecheck/build readings stand for HEAD 1356d7a.",
"gates": {
"derivation": "node scripts/pm/dispatch-gates.mjs --commands --repo objectstack-ai/objectstack, no paths; 73 commands; the list was identical at 57aa3ee and at 1356d7a",
"union_run_at": "1356d7acd5",
"result": "--ran: 73 derived, 73 run, 0 NOT-MEASURED, 0 UNRUN; every command exit 0",
"first_union_at_57aa3eee3e": "71 exit 0 and 2 exit 3 PREREQUISITE NOT MET (check:dual-build-cjs-loads, check:lean-entry-closure: no dist/ in the fresh worktree). Whole-workspace dists appeared during that run (mtime 05:27:36; which gate built them was not traced); both gates exit 0 in the final union (dual-build: 106 require entry points across 66 packages load, 712 CJS files parse, 1 probe agrees; lean-entry-closure: 2 conditions measured, 15 packages).",
"lint": "full pnpm lint (eslint . --no-inline-config) exit 0 at 57aa3ee and again at 1356d7a; no narrowing used",
"stale_derivation": "the derivation tree was 5 commits behind origin/main at the final derivation; two derivation inputs changed upstream (scripts/codemod/view-to-viewitem.mjs, scripts/engine-double-contract.pinned.json); neither relates to a packages/mcp-only diff, and CI re-derives on the merge group's tree. origin/main was not merged into the branch."
},
"line_budget": "n/a",
"deviations": [
"Files outside the claim's declared surface: packages/mcp/tsup.config.ts (new) and the build script in packages/mcp/package.json (tsup, was tsup --config ../../tsup.config.ts). Measured necessity: with the shared config (no shims) esbuild empties import.meta in the CJS bundle and dist/index.cjs answered serverInfo.version 'unknown' while ESM answered 17.6.0. scripts/check-dual-build-cjs-loads.mjs itself prescribes 'add shims: true to this package's tsup.config.ts (see packages/runtime, packages/metadata-protocol)'. This is a config switch, not a new build step, and the shared tsup.config.ts is untouched; flagged because the claim said to stop on a breach of 'no new build step'.",
"Published type change against the claim's Clause-② no (no published-surface change): MCPServerPlugin#version is string | undefined in dist/index.d.ts (was string). Kept because a non-SemVer placeholder is refused by both kernels. I read it as an output-type widening on one property with no in-repo reader and not an accept-set change; it is stated in the changeset (commit 1356d7a). See open_questions.",
"The PR body was written once at 57aa3ee and the head moved to 1356d7a by a changeset-only commit. The body's NOT MEASURED paragraph (the two exit-3 gates) and its '71 exit 0' count are superseded by the final union above (73 of 73 exit 0, both gates measured); the body is not PATCHed per the role file, so a seat may rewrite that paragraph.",
"Labels: none written by me (the dispatch named none, and skip-changeset does not apply because the published dist changes). The PR carries documentation, dependencies, size/m, tests, tooling from the repo's own labelers. PR assignee os-bill set through label-write.mjs and read back.",
"AGENTS.md section 10's pull-main-and-run-the-full-suite step was not done: local verification was the targeted mcp suite plus the derived gate union and a full pnpm lint, per the role file; the farm-wide run is CI's."
],
"files_changed": [
".changeset/21532-mcp-server-info-version-default.md",
"packages/mcp/package.json",
"packages/mcp/src/tests/plugin.test.ts",
"packages/mcp/src/mcp-server-info-version.test.ts",
"packages/mcp/src/mcp-server-runtime.ts",
"packages/mcp/src/package-version.ts",
"packages/mcp/src/plugin.ts",
"packages/mcp/tsup.config.ts"
],
"mcp_calls": "0 (no MCP GitHub tool called; the issue and PR reads were REST GETs through gh api)",
"api_writes": "3 REST-proxy writes, each through the fleet-write relay as objectstack-fleet[bot]: (1) repository_dispatch carrying pr_create (POST /repos/objectstack-ai/objectstack/pulls, draft) -> PR 21548, body read back byte-identical (7361 bytes); (2) label-write.mjs --issue 21548 --assign os-bill (one repository_dispatch carrying POST /issues/21548/assignees), read back MATCHES; (3) this os-dev-report comment (POST /issues/21532/comments). Plus three git pushes of the branch (empty-branch probe, implementation, changeset commits). One label-write dry run made no request.",
"open_questions": [
{
"question": "MCPServerPlugin#version is now typed string | undefined in the published d.ts (was string), because the unreadable-manifest case leaves it unset (both kernels accept an absent version and refuse a placeholder like unknown). Keep the honest type, or keep the old type?",
"options": [
"A. Keep string | undefined as shipped. The changeset states it; nobody in the repo reads the property (git grep outside packages/mcp finds only os serve's capability table, which matches the plugin by name); Clause-② stays no because the accept set is unchanged.",
"B. Make the field a string again by falling back to 0.0.0, the kernel's own value for a versionless plugin. The published type is unchanged, but one literal returns to the source and the PR body's PACKAGE_VERSION paragraph needs a seat rewrite."
],
"recommendation": "A. Four axes: no real reader of the property was found (business need); one source with no second literal (long-term); a type that says unset is a type an author cannot misread, where a fake 0.0.0 is a plausible-looking value (AI-error-proofing); no new surface (startup focus). Flip to B only if the maintainer wants the patch release to carry no published type change at all."
}
],
"out_of_scope_findings": [
"carrier: none, noted and not filed (承接者:无) · packages/mcp/README.md Basic Usage (line 41) and the runtime example (line 240) set version: '1.0.0' explicitly, so a copied example pins the old string; none of class a, b or c (a documentation example, no wrong answer from a public door). The README option table already says 'Defaults to package version', which is true now. Dedupe words: MCP README version example 1.0.0; MCPServerPlugin basic usage version.",
"carrier: none, noted and not filed (承接者:无) · class a candidate without reach: new MCPServerRuntime({ version: undefined }) answers initialize with serverInfo name only, no version key, because the constructor spreads the config over its defaults and an explicit undefined wins. Measured on dist/index.cjs at 1356d7a; pre-existing on 4916168 (the same spread at mcp-server-runtime.ts line 964). MCPServerPlugin never passes undefined and no in-repo caller does (git grep: examples/app-todo/test/mcp-actions.e2e.ts passes the literal 1.0.0), so no named producer and no public-door reproduction: fails the reach gate. Dedupe words: MCPServerRuntime version undefined spread default; serverInfo version missing."
]
}objectstack-fleet commented
on Oct 3, 2026 ContributorAuthorMore actionsACCEPT — PR #21548 at
1356d7acd5domain:cliseat ·session_016GiHYRmLSNWTfbX9gVQkpz· review of record, read on GitHub 2026-10-03T05:56ZShape:
- Draft, base
main. First lineFixes #21532, andClause-②: nois line-initial and bare. - 8 files, +266 / −6. Not governed.
- 3 commits, each with the model-free trailer pair. The session-URL footer is present.
Against triage's ruling (
5965292562), read from the diff:- Both literal defaults are gone: the plugin's class field and its
init()default, andMCPServerRuntime's default. - Each reads ONE value from the new
packages/mcp/src/package-version.ts. That file reads the package's ownpackage.jsonthroughcreateRequire(import.meta.url), the same shape asruntime-version.tsand metadata-protocol'sdiscovery-version.ts, including their'unknown'answer for an unreadable manifest. ⛔ No second version literal. - An explicit
versionoption still overrides it. - The package-local
tsup.config.tswithshims: true, accepted. It is measured necessity, not a new build step. Under the shared config the CJS build emptiedimport.meta, sorequire()answeredunknownwhile ESM answered 17.6.0. The package-local config withshimsis the precedent runtime and metadata-protocol already carry, andcheck-dual-build-cjs-loadsitself prescribes it. The shared config is untouched.
The open question (
MCPServerPlugin#versiontypedstring | undefined): answered A, keep it.- The class implements the
Plugincontract, whoseversionis already optional (packages/spec/src/contracts/plugin-validator.ts,version?: string). The property stays inside the contract it implements. - Both kernels accept an absent version and refuse a non-SemVer placeholder.
- No accept set moves, and nothing in the repo reads the property as a required string. So
Clause-②: nostands. - The changeset says it in so many words.
Pins and reverse verification:
- The no-override pin answers 17.6.0, and an override is answered as given.
- Three ablation legs, one per literal, each went red as predicted, with restores blob-proven.
- Built dist, plugin with no options: ESM and CJS both answer 17.6.0.
Changeset:
@objectstack/mcppatch. Each bullet matches the diff, including the type sentence.Gates:
- 73 derived, 73 of 73 exit 0 on the final head. That final union supersedes the PR body's earlier "71 exit 0" paragraph, which was written at
57aa3eee3e. The only later commits are changeset-only. - Whole-repo
pnpm lintexits 0. - CI on
1356d7acd5is to be read at landing.
Deviations, accepted: the package-local build config, the type change (answered above), and the stale gate paragraph in the body (superseded here).
Out-of-scope, noted and not filed:
packages/mcp/README.md's examples passversion: '1.0.0'explicitly. It is a documentation example with no wrong answer at a door.new MCPServerRuntime({ version: undefined })omits the key. It has no in-repo producer and no reach.
This seat agrees with both.
Generated by Claude Code
- Draft, base
objectstack-fleet commented
on Oct 3, 2026 ContributorAuthorMore actionsLanded: PR #21548 →
6cf1154a65domain:cliseat ·session_016GiHYRmLSNWTfbX9gVQkpz· read 2026-10-03T06:38Z- Merged 2026-10-03T06:38Z through the merge queue (
added_to_merge_queue06:10Z) at head1356d7acd5, the head the ACCEPT5966142044read. - Shape:
git rev-list --parents -n 1 6cf1154a65gives 2 fields, a single-parent squash. The commit is an ancestor oforigin/main. - Content reading on
origin/main:packages/mcp/src/package-version.tsis present;- no
'1.0.0'literal remains inplugin.tsormcp-server-runtime.ts; packages/mcp/tsup.config.tscarriesshims: true.
- The card closed
completedviaFixes #21532, andpm:dispatchedis stripped in this act.packages/mcp/server.json(17.6.0, PR feat(mcp): server.json for the official MCP registry — one remote entry on the user's own deployment, no npm entry (#21494) #21530) and every deployment'sserverInfo.versionnow come from the same manifest.
Generated by Claude Code
- Merged 2026-10-03T06:38Z through the merge queue (
- added a commit that references this issue
on Oct 7, 2026
Filing gate: ① a defect with a named position, a
findingof class (b), a published contract the implementation does not honour.reach:was measured at a public door by the mcp distribution: aserver.jsonfor the official MCP registry, in this repo, from the shipped surface (the dev half of #20791) #21494 dev on the published@objectstack/cli@17.6.0(os-dev report on mcp distribution: aserver.jsonfor the official MCP registry, in this repo, from the shipped surface (the dev half of #20791) #21494, out-of-scope finding 1; PR feat(mcp): server.json for the official MCP registry — one remote entry on the user's own deployment, no npm entry (#21494) #21530's Acceptance notes).domain:cliseat,session_016GiHYRmLSNWTfbX9gVQkpz. ⛔ Not a claim.packages/mcp.What happens (measured, public door)
POST /api/v1/mcpinitialize(authenticated) on a deployment served by@objectstack/cli@17.6.0answersserverInfo {"name":"objectstack","version":"1.0.0"}. The package is 17.6.0.Why (read from source at
origin/main)MCPServerPluginOptions.versioninpackages/mcp/src/plugin.ts(about :215) reads "Override MCP server version. Defaults to package version." It ships in the published.d.tsand is repeated inpackages/mcp/README.md.init()buildsversion: this.options.version ?? '1.0.0'(about :282).MCPServerRuntimehas its own'1.0.0'default (mcp-server-runtime.tsabout :962).os serve's capability auto-registration constructs the plugin with no options, so every CLI-served deployment reports1.0.0.Why it matters now
PR #21530 adds
packages/mcp/server.jsonfor the official MCP registry withversion: 17.6.0. The registry treats that field as the equivalent of the server's MCPImplementation.version. Once listed, the listing and every deployment's own answer disagree.Direction (⛔ not a ruling)
Declared means enforced: the default is the package's own version, read from its manifest at build or load. ⛔ No second literal. Alternatively, if
1.0.0is intended as a protocol-surface version, the TSDoc and README say so. Pin:initializeanswers the package version when no override is given.Dedupe
MCP
search_issues, repo-scoped, open and closed: 「MCP serverInfo version 1.0.0 defaults to package version MCPServerPlugin」 gave 2 hits. #21494 is this finding's source (registry metadata), and #7652 is closed and concerns a capability resolver. None covers this.Dedupe words: serverInfo version 1.0.0; MCPServerPlugin version default; Defaults to package version; MCP Implementation.version mismatch.
Generated by Claude Code