Repository navigation
feat(mcp): per-request server instructions on McpAuthoringInjection, answered only where the authoring family registers - #31141
Conversation
McpAuthoringInjection gains an optional `instructions`; handleHttpRequest answers `initialize` with it only under the grant that registers the authoring family, and with the configured or default text otherwise. Co-Authored-By: Claude <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_017ihL2dLQEXWGsE2SZ3Qn5F
Co-Authored-By: Claude <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_017ihL2dLQEXWGsE2SZ3Qn5F
📓 Docs Drift Check4 anchor(s) derived from 1 changed package(s); no hand-written page names any of them, so this run has nothing to list — not a clean bill of health. This check sees only pages that NAME a derived anchor: one that documents this change in prose, or enumerates it in an authoring dialect, names none and stays invisible to it on every run. What this run could not see
Coarse fallback — 12 page(s) merely mention a changed package (the pre-#9192 predicate, kept for the deliberately-wide backstop): Which tree this was computed onThis run read A worktree cut from an older # while this PR is open — GitHub drops the merge commit once it closes
git fetch origin 9084e583bb2afbf701517074a9f3641df978a6eb && git checkout 9084e583bb2afbf701517074a9f3641df978a6eb
# afterwards, rebuild it from the two parents, which stay fetchable
git fetch origin af3451b08bc39e7b4c163f52397cffe728b3e99d e5540c8fdccbf43495ef331b840ee8b8d8adc027 && git checkout -B drift-repro af3451b08bc39e7b4c163f52397cffe728b3e99d && git merge --no-ff e5540c8fdccbf43495ef331b840ee8b8d8adc027
node scripts/docs-audit/affected-docs.mjs --json af3451b08bc39e7b4c163f52397cffe728b3e99d |
|
domain:server#4 · marchtian · session_017ihL2dLQEXWGsE2SZ3Qn5F Contract reviewServed-tier: ① Derived judgments
② Semver level
③ Boundary flags
Implemented-by: VERDICT: PASS |
|
domain:server#4 · marchtian · session_017ihL2dLQEXWGsE2SZ3Qn5F 席位核对 — ACCEPT · domain:server#4 · Head-sha:
|
⛔ merge queue 构建失败 — 先分诊,再决定要不要重排队列构建 42382546156 红了。队列跑的是全量套件(PR 侧 CI 只跑 affected 子集), 分类: 失败的 job(日志抽取,best effort):
跨 PR 相同签名(24h,按失败测试文件聚合):
历史信号:
分诊清单:
Generated by Claude Code · merge-queue-triage workflow (#4859) |
|
domain:server#4 · marchtian · session_017ihL2dLQEXWGsE2SZ3Qn5F Queue ejection read · build 42382546156 · |
Requested by 田騏 · project thread
Fixes #31022
Clause-②: yes
Metadata-protocol: none
维护者速读
McpAuthoringInjection.instructions),客户端连上来握手时拿到的就是这段说明,而不是那句固定默认话。data:read)、API key、会话等拿不到搭建工具的请求,永远看不到这段说明,仍是原来的默认话;不传这个字段时行为与今天完全一样。回滚即 revert 本 PR。packages/mcp/src/mcp-server-runtime.ts(authoringInstructions与handleHttpRequest里instructions:那一行)、packages/mcp/src/types.ts(新字段的注释)、packages/mcp/src/mcp-server-runtime.authoring-scope.test.ts(末尾新加的 describe)。pnpm --filter @objectstack/mcp exec vitest run src/mcp-server-runtime.authoring-scope.test.ts,预期 24 个用例全绿,其中 5 个是本次新增的说明用例。What changes
McpAuthoringInjection(packages/mcp/src/types.ts) gains an optionalinstructions?: string.MCPServerRuntime.handleHttpRequestbuilds the per-request server withauthoringInstructions(opts.authoring, grantedScopes) ?? this.config.instructions ?? DEFAULT_SENTENCE(the hard-coded default). The new private helper returns the injected text only whengrantsAuthoringTools(grantedScopes)admits the request, the same predicateregisterAuthoringToolsuses to register the family. Every other request (adata:read,data:writeoractions:executetoken, an empty grant, the undefined grant of an API key or a session) gets the configured or default instructions.handleHttpRequest's opts: an opts-level field would be a second, ungated input that any request could carry, so keeping thedata:readguarantee would need a second gate beside the registry's. On the injection, the text sits inside the object only the authoring family reads, and the one predicate that admits the registry also admits the text. A host cannot hand it to a request the family does not serve.@objectstack/mcppatch (Metadata-protocolnone).Tests
A new top-level
describein the existing shared harnesspackages/mcp/src/mcp-server-runtime.authoring-scope.test.ts. Each case readsresult.instructionsoff a realinitializeround-trip throughhandleHttpRequest:metadata:writerequest whose injection carries instructions is answered with them. A control leg (same door, no field) answers something else.data:readrequest with the same injection gets the default, byte-equal to adata:readrequest with no injection.data:writetoken get the default too.metadata:writerequest whose injection carries no instructions gets the default.instructionskeeps them for every request the injection does not reach. Under the admitting grant, the per-request text wins.The default sentence is not pinned verbatim. Each "gets the default" assertion compares against a control request on the same runtime.
Ablation (one behaviour, the grant gate):
scripts/ablation-replace.mjsreplacedif (!authoring || !grantsAuthoringTools(grantedScopes)) return undefined;withif (!authoring) return undefined;. Anchor 1→0, blobf68539a8ec22 → 83181ec01751. The file went 3 failed / 21 passed (24), failing cases 2, 3 and 5 above. The restore put the blob back to HEAD andgit diff HEADwas empty. The test imports./mcp-server-runtime.jsfrom source, so no dist rebuild was involved.Acceptance notes
instructionsis passed through as given, and the SDK then omitsinstructionsfrom the answer. Onlyundefinedfalls back to the configured or default text.MCPServerRuntimeconstructor) is untouched. It has no per-request authoring injection.Verification
All readings were taken on head
e5540c8f.git merge origin/mainwas a no-op:origin/mainis still the branch point5f003744, read vials-remote.Package tests, typecheck, lint (@objectstack/mcp)
@objectstack/mcphas nolintscript of its own, so the repopnpm lintcommand (eslint . --no-inline-config) was run scoped topackages/mcp.Derived gate families (dispatch-gates --commands): 60 derived, 60 run, 0 NOT-MEASURED
All 60 commands exited 0. Four exited 3 on the first pass and were re-run green:
check:dual-build-cjs-loadsandcheck:lean-entry-closurehad nodist/. After a fullpnpm build --concurrency=2(exit 0), both returned exit 0.check:type-check-debthit a lock queue-timeout. Re-run under the lock, exit 0.check-plugin-teardown-shape --self-testcould not reach its pinned fixture commit in the shallow clone. Once that object was present, exit 0.dispatch-gates --ranwith exit codes recorded:60 derived famil(ies) accounted for — 60 run, 0 NOT-MEASURED (a DERIVED zero).🤖 Generated with Claude Code