docs(mcp): name the Account door for Connect an Agent, not Setup alone - #18142
Conversation
#17646 delivered #16746's ruling as a `navigationContributions` entry in the `account` app and deliberately left Setup gated, so a non-admin reaches the Connect-an-Agent page — but at none of the paths the shipped texts named. - `packages/mcp/src/plugin.ts` — the stdio refusal message now names both doors (Account → Developer for any signed-in user; Setup for admins). - `packages/mcp/README.md` — same, in the `OS_MCP_STDIO_API_KEY` paragraph. - `content/docs/ai/connect-mcp.mdx` — the "Headless: API keys" section now gives both doors with their console URLs, and moves the revoke location to `Account → Developer → API Keys` for the user's own keys, noting the tenant-wide Setup list needs `manage_platform_settings`. The `OS_MCP_SERVER_ENABLED=false` callout no longer calls it a Setup page. Paths, labels and permissions read off the tree, not invented: the account entry at `packages/mcp/src/connect-ui.ts`, the group at `packages/platform-objects/src/apps/account.app.ts`, the package id at `packages/apps/account/src/index.ts`, and the route resolution in objectui's `packages/app-shell/src/utils/appRoute.ts`. Claude-Session: https://claude.ai/code/session_012GKcPZbMoGq7WPzKLfRBTU Co-authored-by: Claude <noreply@anthropic.com>
📓 Docs Drift CheckThis PR changes 1 package(s): 5 hand-written doc(s) NAME something this change touched and may need an implementation-accuracy re-verification:
⛔ 1 release-owned page(s) also name something this change touched. These are read-only:
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 86080a316bd7841058bde08fb88e9db43f31e574 && git checkout 86080a316bd7841058bde08fb88e9db43f31e574
# afterwards, rebuild it from the two parents, which stay fetchable
git fetch origin a90a9f26794e5a2c34c1eded83ba0e25087e4433 680f338de454b29dfe0285efe502127fa205fe7c && git checkout -B drift-repro a90a9f26794e5a2c34c1eded83ba0e25087e4433 && git merge --no-ff 680f338de454b29dfe0285efe502127fa205fe7c
node scripts/docs-audit/affected-docs.mjs --json a90a9f26794e5a2c34c1eded83ba0e25087e4433
|
复核通过 —— 已 undraft 并武装(
|
| 探针 | 读数 |
|---|---|
APP_NAME |
不存在;:160 起是 const APPS = [{setup},{account}],:166 APP_NAMES = APPS.map(...) |
| 该门禁的 roster | :511 明写 { source: '@objectstack/mcp', apps: ['setup','account'], idsByApp: { setup: [], account: ['nav_connect_agent'] } } |
⇒ 那道门禁看得见 account 侧入口(PR #17972 / #17891 拓宽的)。本席这条前提是陈旧的,dev 报回来是对的。
⭐ 但卡的结论仍然成立,理由换了一条,这点 dev 说得准:那道门禁判的是locale bundle 的标签,⛔ 从不判英文散文 —— 所以这三处 prose 断言仍然没有任何机器在看。⇒ 卡该做,做法不变;变的只是"为什么没人看见"。
事实逐条重测(⛔ 不采信报告)
新写进文档的每一条事实,本席都在树里落到了锚点:
| 文档新写的话 | 树里的出处 |
|---|---|
Account 应用直链 com.objectstack.account |
packages/apps/account/src/index.ts ACCOUNT_APP_PACKAGE_ID = 'com.objectstack.account' |
| Account → Developer → API Keys(自己的钥匙) | platform-objects/src/apps/account.app.ts — grp_account_developer(label Developer)下 nav_account_api_keys(label API Keys,viewName: 'mine')⇒「自己的」这半是准的 |
Setup → Access Control → API Keys 需 manage_platform_settings |
setup-nav.contributions.ts:102 nav_api_keys … requiredPermissions: ['manage_platform_settings'];setup.app.ts:76 label: 'Access Control' |
Setup 门需 setup.access |
该权限在树里真实存在 |
nav_connect_agent 落在 grp_account_developer |
packages/mcp/src/connect-ui.ts(并有 connect-agent-account-nav.test.ts 钉着) |
空对照一律读 0;packages/account / packages/setup,实为 packages/apps/**),读到的 0 ⛔ 不是读数 —— 换对路径并加发火对照后才得出上表。一条错路径的零不是一次阅读,本班又一次。
改动本身
三处都名了两道门(任意登录用户走 Account,管理员走 Setup),⭐ 而 Setup 那道原样保留 —— 这正是卡里要求的"不要用一条路替换另一条":#17646 没有动 Setup,管理员的路径依然有效。
connect-mcp.mdx 是整段重读过的,侧栏链接与吊销位置两处同病都改了;并顺手修了同文件同病的 OS_MCP_SERVER_ENABLED 提示框(同一缺陷类、同一文件,⛔ 不算扩面)。
文件面 = 本席声明的三处 + changeset,⛔ 无越界;changeset 用通行的 <issue>-<slug> 拼法。
核过的其余项
check-clause2-carriers --pair 18142→ 两载体一致、diff 无放宽征兆 ✅- CI:40 项,RED: none(23 success / 4 skipped / 13 in_progress);未挂
needs:contract-review✅ - ⛔ 不触治理面 ✅
附带产出与交接
- dev 另立 Four more shipped docs pages still send a non-admin to "Setup → Connect an Agent", the one app that 403s for them #18143:另有四处已发布文档带同样缺陷,在本卡声明的文件面之外(
api/index.mdx与deployment/environment-variables.mdx是直接的铸钥指引,ai/agents.mdx与getting-started/build-with-claude-code.mdx是描述性的)。⛔ 本席停席前不派它。 - dev 明确未立的一项:把 advisory 的 docs-drift 检查扩成「散文里点名的 console 路径必须解析到已注册的 app + page」的真门禁 —— 简报要求"只报不建",dev 照办了。⇒ 这是一条立卡候选,交给接手席位或 Four more shipped docs pages still send a non-admin to "Setup → Connect an Agent", the one app that 403s for them #18143 的接手人。
⚠️ 跨车道声明随本席停席失效:packages/mcp/**属domain:cli行,本席曾点名声明并知会 [PM seat] domain:cli — 🔻 VACANT · R74 CLOSED OUT 04:49Z by os-warren / session_01TbSMtGzMrtPwh925wDEZd5 · 4 landed (#18070→b3d6918b8 · #17865→a26a114d7 · #17630→99119ea05f · #17978→c667d8c807) · 0 rework 0 escalation · 0 in flight · ⚠️ #18066 (p2) is dispatchable and UNBLOCKED — read it to its last comment first · #17964/#3739 blocked on unavailable reach · #18024 pm:blocked on #17516 · #18118 awaits triage grading · PR #17076 + #16502 + #17873 + #17964② q with the maintainer #6024(评论5659612310)。本 PR 已武装、即将落地,该声明对已落的这一笔有效;但 ⛔ 后续任何在packages/mcp/**的动作需要接手席位重新声明并重新知会,⛔ 不得默认继承。
Generated by Claude Code
Closes #17648
Clause-②: no#17646 delivered #16746's ruling by adding a second
navigationContributionsentry into the
accountapp — deliberately not by ungating Setup, whichwas measured to expose 14+ unrelated Setup surfaces. So a non-admin can now
reach the Connect-an-Agent page, but at none of the paths the three shipped
texts named. This edits the prose; nothing else moves.
The Account path, measured (not invented)
accountpackages/mcp/src/connect-ui.ts(the second contribution)grp_account_developer, label Developerpackages/platform-objects/src/apps/account.app.ts; label inapps/translations/en.tsnav_connect_agent, label Connect an Agentconnect-ui.ts; label inen.ts(all four locales, per #17759)connect_agentCONNECT_AGENT_PAGEinconnect-ui.tscom.objectstack.accountpackages/apps/account/src/index.ts, wired atpackages/cli/src/commands/serve.ts/apps/:appName/page/:pageNamepackages/app-shell/src/console/AppContent.tsx_packageIdfirst, appnameas aliaspackages/app-shell/src/utils/appRoute.ts—matchAppBySegmentpackages/app-shell/src/layout/AppHeader.tsx⇒
/_console/apps/com.objectstack.account/page/connect_agent, symmetric with theSetup URL the page already carried.
packages/apps/account/src/index.tsstatesthe pair in as many words: "
/apps/(packageId)(alias/apps/account) resolvesto exactly this app".
Permissions, also measured:
SETUP_APPdeclaresrequiredPermissions: ['setup.access'](setup.app.ts:47), and Setup's API-keys entry additionallyrequires
manage_platform_settings(setup-nav.contributions.ts:102), whileACCOUNT_APPdeclares none. The Account app's own API Keys entry is theminelist view filtereduser_id == {current_user_id}with therevoke_api_keyrow action — so the revoke fact survives the move rather thanbeing dropped.
The three sites
1.
packages/mcp/src/plugin.ts— the stdio refusal message (a runtimestring, read exactly when the user is stuck). Found at :384, not the card's
:372— the reading had rotted; located by content.mint an API key (Setup → Connect an Agent, or POST /api/v1/keys)mint an API key on the Connect an Agent page (Account → Developer for any signed-in user; Setup → Connect an Agent for admins), or POST /api/v1/keys2.
packages/mcp/README.md:92(line unmoved) — same substitution, in theOS_MCP_STDIO_API_KEYparagraph, with the README's existing bold convention.3.
content/docs/ai/connect-mcp.mdx— the "Headless: API keys" section(97–104, unmoved) rewritten as one page, two doors, each with its console
URL and its permission; the revoke sentence now sends a user to Account →
Developer → API Keys and labels the tenant-wide Setup → Access Control → API
Keys list with the permission it needs.
Bounded in-place fix in the same file and defect class: the
OS_MCP_SERVER_ENABLED=falsecallout at :14 also called it "the Setup →Connect an Agent page". It now says "the Connect an Agent page … along
with both its Setup and Account navigation entries", which is what #17646's own
changeset measured (an opted-out deployment gets no page and neither entry).
Reverse-read, both directions
Account path". Reproduced on
origin/mainbefore editing —Account app//_console/apps/account/grp_account_developerovercontent/docs/= 3hits (an authorization note, an objectui action target, a v17-0 release page),
all unrelated; firing control on the same expression = 5. That count is the
card's, not shipped prose, and is history once this lands.
followable by a permissionless principal, and the refusal message is actionable
for an operator who is not a platform admin.
control — tests referencing
OS_MCP_STDIO_API_KEY= 5 files). No pin testreads this page's prose (control —
scripts/docs-audit/handwritten-docs.jsonlists the file, so the path is right).
Verification
Repo-wide, not narrowed:
pnpm lint(eslint . --no-inline-config) exit 0in 74s at
680f338de4.pnpm --filter @objectstack/mcp build && typecheck && test— 31 files, 333tests passed, under
scripts/pm/os-verify-lock.sh(VERDICT command-exit 0).Dependency closure
pnpm --filter '@objectstack/mcp^...' build—VERDICT command-exit 0.Gate families derived from the real change set with
node scripts/pm/dispatch-gates.mjs --commands --repo objectstack-ai/objectstackand reconciled with
--ran: 83 derived, 81 run green, 2 NOT MEASURED, 0unrun. The two are
check:dual-build-cjs-loadsandcheck:lean-entry-closure,both exit 3 / PREREQUISITE NOT MET — they read built output across ~77
packages this worktree has not built. ⛔ Not read as passes; declared to CI's
Build Core job.
check:skill-examplesalso refused a prerequisite first; I built@objectstack/client+@objectstack/client-reactand re-ran it to a realverdict (258 prose examples type-check across 3 surfaces).
Control-character self-scan over the four touched files: clean, with a firing
control (
grep -naP '[\x00-\x08\x0b\x0c\x0e-\x1f\x7f]').No ablation: the change is prose and one string literal — there is no guard to
delete and no assertion whose failure mode could be proven by mutation.
验收备注
A brief premise that is now FALSE, and worth the seat's attention. The
dispatch and the card both state that
packages/cli/scripts/check-app-nav-i18n.mjs"scopes itself toAPP_NAME = 'setup'(:109) and skips every other contribution target (:581), so nothingjudges the account-side entry". That was true when #17648 was filed and is
superseded: PR #17972 (#17891) widened it to a declared population,
APPS = [{ name: 'setup' }, { name: 'account' }]at :161-164, withAPP_NAMESat:166 and the self-test invariants at :575/:580. The file's own header now names
nav_connect_agent/grp_account_developer/connect-ui.tsexplicitly.The card's conclusion still holds, for a different reason than it gave: that
gate judges locale-bundle labels, never English prose in docs, a README, or a
thrown
Error. Nothing machine-checks these three claims, so the prose edit wasstill the only remedy.
Out of scope, filed separately as #18143 — four more shipped pages carry the identical
defect but lie outside this card's declared file surface:
content/docs/ai/agents.mdx:55,content/docs/api/index.mdx:68,content/docs/getting-started/build-with-claude-code.mdx:435,content/docs/deployment/environment-variables.mdx:259. The last two are directmint instructions, the same shape as the three fixed here.
Noted, not filed:
docs/adr/0101-mcp-stdio-principal-admission.md:104,docs/qa/platform-checklist/areas/ai.json:206and two.changeset/files alsoname "Setup → Connect an Agent". All four are dated records — a ruling, a test
checklist and shipped release history — so ⛔ not edited and ⛔ not filed.
Card candidate deliberately NOT built here: a cheap way to make these claims
machine-checkable would be to extend the docs-drift check from advisory to a real
gate over "console path named in prose resolves to a registered app + page".
Out of scope for a p1 prose fix; reported rather than built, per the brief.
🤖 Generated with Claude Code
https://claude.ai/code/session_012GKcPZbMoGq7WPzKLfRBTU
Generated by Claude Code