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

docs(mcp): name the Account door for Connect an Agent, not Setup alone#18142
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

@claude
claude Bot added this pull request to the merge queue Sep 14, 2026
Merged via the queue into main with commit c5d270a Sep 14, 2026
44 checks passed
@claude
claude Bot deleted the claude/issue-17648-connect-agent-account-path branch September 14, 2026 07:09
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