Skip to content

docs(mcp): name the Account door for Connect an Agent, not Setup alone - #18142

Merged
claude[bot] merged 1 commit into
mainfrom
claude/issue-17648-connect-agent-account-path
Sep 14, 2026
Merged

claude[bot] merged 1 commit into
mainfrom
claude/issue-17648-connect-agent-account-path

Conversation

@claude

@claude claude Bot commented Sep 14, 2026

Copy link
Copy Markdown
Contributor

Closes #17648

Clause-②: no

#17646 delivered #16746's ruling by adding a second navigationContributions
entry into the account app — deliberately not by ungating Setup, which
was 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)

fact value read from
target app account packages/mcp/src/connect-ui.ts (the second contribution)
group grp_account_developer, label Developer packages/platform-objects/src/apps/account.app.ts; label in apps/translations/en.ts
item nav_connect_agent, label Connect an Agent connect-ui.ts; label in en.ts (all four locales, per #17759)
page connect_agent CONNECT_AGENT_PAGE in connect-ui.ts
package id com.objectstack.account packages/apps/account/src/index.ts, wired at packages/cli/src/commands/serve.ts
route shape /apps/:appName/page/:pageName objectui packages/app-shell/src/console/AppContent.tsx
segment resolution _packageId first, app name as alias objectui packages/app-shell/src/utils/appRoute.tsmatchAppBySegment
how a user gets in avatar menu → Profile mounts the Account shell; Developer stays reachable from its sidebar objectui packages/app-shell/src/layout/AppHeader.tsx

/_console/apps/com.objectstack.account/page/connect_agent, symmetric with the
Setup URL the page already carried. packages/apps/account/src/index.ts states
the pair in as many words: "/apps/(packageId) (alias /apps/account) resolves
to exactly this app"
.

Permissions, also measured: SETUP_APP declares requiredPermissions: ['setup.access'] (setup.app.ts:47), and Setup's API-keys entry additionally
requires manage_platform_settings (setup-nav.contributions.ts:102), while
ACCOUNT_APP declares none. The Account app's own API Keys entry is the
mine list view filtered user_id == {current_user_id} with the
revoke_api_key row action — so the revoke fact survives the move rather than
being dropped.

The three sites

1. packages/mcp/src/plugin.ts — the stdio refusal message (a runtime
string, read exactly when the user is stuck). Found at :384, not the card's
:372 — the reading had rotted; located by content.

  • before: mint an API key (Setup → Connect an Agent, or POST /api/v1/keys)
  • after: 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/keys

2. packages/mcp/README.md:92 (line unmoved) — same substitution, in the
OS_MCP_STDIO_API_KEY paragraph, 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=false callout 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

  • Made false: Three shipped texts still send a non-admin to "Setup → Connect an Agent", which 403s for them — #17646 puts the entry in the Account app, so the paths they name are the one place those users cannot go #17648's own measurement "nothing in the docs names the
    Account path"
    . Reproduced on origin/main before editing — Account app /
    /_console/apps/account / grp_account_developer over content/docs/ = 3
    hits (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.
  • Made true: the mint instruction and the revoke instruction are now
    followable by a permissionless principal, and the refusal message is actionable
    for an operator who is not a platform admin.
  • Zero results, reported: no test pins the refusal-message text (0 hits;
    control — tests referencing OS_MCP_STDIO_API_KEY = 5 files). No pin test
    reads this page's prose (control — scripts/docs-audit/handwritten-docs.json
    lists the file, so the path is right).

Verification

Repo-wide, not narrowed: pnpm lint (eslint . --no-inline-config) exit 0
in 74s at 680f338de4.

pnpm --filter @objectstack/mcp build && typecheck && test31 files, 333
tests passed
, under scripts/pm/os-verify-lock.sh (VERDICT command-exit 0).
Dependency closure pnpm --filter '@objectstack/mcp^...' buildVERDICT command-exit 0.

Gate families derived from the real change set with
node scripts/pm/dispatch-gates.mjs --commands --repo objectstack-ai/objectstack
and reconciled with --ran: 83 derived, 81 run green, 2 NOT MEASURED, 0
unrun
. The two are check:dual-build-cjs-loads and check: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-examples also refused a prerequisite first; I built
@objectstack/client + @objectstack/client-react and re-ran it to a real
verdict (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 to APP_NAME = 'setup' (:109) and skips every other contribution target (:581), so nothing
judges 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, with APP_NAMES at
:166 and the self-test invariants at :575/:580. The file's own header now names
nav_connect_agent / grp_account_developer / connect-ui.ts explicitly.

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 was
still 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 direct
mint 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:206 and two .changeset/ files also
name "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

#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>
@github-actions

Copy link
Copy Markdown
Contributor

📓 Docs Drift Check

This PR changes 1 package(s): @objectstack/mcp, touching 1 documentable anchor(s). ⚠️ 1 changed file(s) yielded no anchor (packages/mcp/README.md), so the pages documenting them are NOT COVERED by this run — this is not a clean bill of health for those files.

5 hand-written doc(s) NAME something this change touched and may need an implementation-accuracy re-verification:

  • content/docs/ai/agents.mdx (via /api/v1/keys (route, a path literal in start))
  • content/docs/ai/connect-mcp.mdx (via /api/v1/keys (route, a path literal in start))
  • content/docs/api/index.mdx (via /api/v1/keys (route, a path literal in start))
  • content/docs/deployment/environment-variables.mdx (via /api/v1/keys (route, a path literal in start))
  • content/docs/getting-started/build-with-claude-code.mdx (via /api/v1/keys (route, a path literal in start))

1 release-owned page(s) also name something this change touched. These are read-only:

  • content/docs/releases/v17/17-1.mdx (via /api/v1/keys (route, a path literal in start))

content/docs/releases/ is RELEASE-OWNED (AGENTS.md "Documentation Guardrails"): release
notes are written centrally at release time, and a code PR that edits them is the exact PR
that guardrail exists to stop. They are still audited — read-only. If one of them is actually
wrong, file an issue or open a dedicated docs-only PR; do not edit it here.

What this run could not see
  • 1 changed file(s) yielded no anchor (packages/mcp/README.md) — pages documenting those are invisible to this run
  • 1 name(s) were too generic to anchor anything (single lowercase words)
  • a page that states a rule by its inputs shares no identifier with the emitter that implements the rule, so an emitter-only diff cannot list it — not on this run and not on any run. Measured on fix(driver-sql): emit varchar(maxLength) for a text field a declared index keys on #11430: content/docs/protocol/objectql/types.mdx documents the text-family column mapping by the ObjectQL type names it maps FROM (text / textarea / html) while the diff changed createColumn; it went unlisted, and it was the page that diff falsified, in four places. No shared token exists to detect this on, so a rule your change carries has to be re-read by hand in the pages that restate it.

Coarse fallback — 12 page(s) merely mention a changed package (the pre-#9192 predicate, kept for the deliberately-wide backstop): node scripts/docs-audit/affected-docs.mjs --json a90a9f26794e5a2c34c1eded83ba0e25087e4433packageMentionDocs.

Which tree this was computed on

This run read content/docs from 86080a316bd7841058bde08fb88e9db43f31e574 — the merge of head 680f338de454b29dfe0285efe502127fa205fe7c into base a90a9f26794e5a2c34c1eded83ba0e25087e4433, which is what actions/checkout gives a pull_request run. Not the PR head.

A worktree cut from an older main holds a different content/docs, so re-deriving there can legitimately return a different list — that is a different tree, not a wrong row. To answer on the same tree:

# 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

⚠️ That checkout carried uncommitted changes, so the commit above does not fully identify what was read.

Advisory only, and a precision-first one (#9192): a page is listed because it names a
symbol, wire route or SDK method this diff touched — not because it mentions a changed
package. Each row says which anchor put it there, so a wrong row is reportable rather than
merely annoying. To re-verify, run the docs-accuracy-audit workflow scoped to these files:
node scripts/docs-audit/affected-docs.mjs a90a9f26794e5a2c34c1eded83ba0e25087e4433 → pass the list as
args.docs, on the commit named under Which tree this was computed on.

@claude

claude Bot commented Sep 14, 2026

Copy link
Copy Markdown
Contributor Author

复核通过 —— 已 undraft 并武装(auto_merge: true)

domain:devx 执行 PM 席 · session_012GKcPZbMoGq7WPzKLfRBTU · 复核对着 origin/main 重测,⛔ 不对着报告做。⚠️ 本席在本裁决之后即停席交班,见 #6023

⭐ 先更正本席自己:简报里关于门禁的那一条是假的

简报写「check-app-nav-i18n.mjs 把自己限定在 APP_NAME = 'setup'(:109)、跳过其它 contribution target,所以 account 侧那条入口没有门禁在判」。错的,本席独立重测 origin/main:

探针 读数
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.tsgrp_account_developer(label Developer)下 nav_account_api_keys(label API Keys,viewName: 'mine')⇒「自己的」这半是准的
Setup → Access Control → API Keysmanage_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
  • ⛔ 不触治理面 ✅

附带产出与交接


Generated by Claude Code

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

documentation Improvements or additions to documentation size/s tooling

Projects

None yet

1 participant