Repository navigation
Description
repo:cloud#3 · hotlong · session_01Q4EjjRhUZjdy8VXs6Kkur2
Blocked-by: #30871
Filing gate: ③ a task on the maintainer's word. Epic objectstack-ai/cloud#3652, coordinator session 2026-11-06, verbatim: 「我觉得先把受管软件包的开发循环走顺了」, then 「同意,然后第一步先解决我本地开发的软件包怎么能够方便地发布到云端」. Phase 2 lists "os mcp connect: reuse the CLI login to run the environment's MCP OAuth in the same browser (platform SSO already signed in, one consent), then write the MCP client's config". The epic's model: MCP reads the environment for context and does not write a managed package's content. Ruling Q1 on cloud#3652, verbatim: 「a 本地 os dev(推荐)」. Q3 does not apply.
Carrier: objectstack. The CLI command is platform code. Cloud already serves everything an MCP client needs on a hosted environment (Today 3), so there is no cloud card.
维护者速读
现在要让 Claude Code 之类的工具连上云端环境的 MCP,开发者得自己找到环境地址、拼出 claude mcp add 命令。本地 os dev 会打印这行命令,云端环境没有。这张卡加一个 os mcp connect:自动找到当前环境,把它的 MCP 地址写进 MCP 客户端的配置,登录授权由客户端按环境自己的 OAuth 在浏览器里完成。「用云端登录直接免授权」要等以后「控制平面做唯一授权服务器」那张决策卡,这里不碰。
Today
- No
os mcpcommand exists.packages/cli/src/commands/has nomcptopic (objectstackorigin/mainc50e0dfa, directory listing). (measured) os devprints a connect hint for the local server only: Endpoint, Skill, andclaude mcp add --transport http objectstack <origin>/api/v1/mcp(packages/cli/src/commands/dev.ts:197-207). The scaffold README has the same line forlocalhost:3000(packages/create-objectstack/src/templates/blank/README.md:72). Nothing in the CLI writes an MCP client config file:.mcp.json/mcpServers→ 0 hits inpackages/cli/src. (measured)- A hosted environment already serves MCP with OAuth discovery. (measured, cloud
origin/main8f574b9e)- The kernel mounts the MCP server plugin (
packages/objectos-runtime/src/artifact-kernel-factory.ts:1284). /api/v1/mcpanswers 401 withresource_metadata="https://ENV-HOST/.well-known/oauth-protected-resource". Cloud's discovery proxy serves the protected-resource and authorization-server documents on the environment host (oauth-discovery-proxy-plugin.ts:13-15,:39-43).- Dynamic client registration is open, with an edge limit of 5 per 60 s (
packages/cloudflare-shared/cloudflare/container-forward.ts:83). - A revoked grant stops opening
/api/v1/mcpon the hosted path (packages/objectos-runtime/src/mcp-revoked-grant.ts:3, cloud#3431).
- The kernel mounts the MCP server plugin (
- The environment is the authorization server. objectstack's plugin-auth serves
/.well-known/oauth-authorization-serverand/.well-known/oauth-protected-resource(packages/plugins/plugin-auth/src/auth-plugin.ts:4980,:5016). The measured client path is DCR as a public client plus PKCE S256 withresource= the MCP URL (objectstack#30345 "Measured"). (measured) - Revocation is being fixed. objectstack#30345 (p1, dispatched): an MCP JWT access token cannot be revoked today. (measured on the issue)
- Whether the environment's sign-in page reuses the platform session (the epic's "platform SSO already signed in") is not measured here.
- The Later decision. The epic lists "the control plane as the single OAuth authorization server for CLI and MCP, with environments as resource servers", to be filed after objectstack#30345 lands. Using the CLI's cloud login token as an MCP credential is that decision's question. (measured on the epic)
Expected
os mcp connectresolves the target the deploy command uses (os package publish: one command from a local checkout to the org's environment — pick the only environment, keep it across login, fail loudly without one, and publish dev builds without hand-bumping the version #30871: the organization's only environment, or the active one;--urlfor self-hosted or a local server) and derives its MCP URL.- It registers that URL with the developer's MCP client:
- for Claude Code, by running or printing
claude mcp add --transport http <name> <url>; - otherwise by writing the client's config file;
--clientpicks the client, and--printonly prints.- The server name is the environment's, so several environments can sit side by side.
- for Claude Code, by running or printing
- The client runs the OAuth. The CLI opens nothing and stores no MCP token. On first use the client follows the environment's discovery, registers, and the browser shows the environment's sign-in and consent.
- ⛔ No MCP access or refresh token is written into any config file: such a token expires in an hour and would bypass the client's refresh and the revocation fixed by plugin-auth: an MCP JWT access token cannot be revoked — /oauth2/revoke answers 400 and delete-consent ends nothing, against IAuthService.verifyMcpAccessToken's 'falsy when revoked' (objectstack-ai/cloud#3606 ruling A) #30345.
- It prints how to revoke the grant (the console's grant list). It also prints one line of the epic's model: MCP reads the environment for context, and a managed package's content changes through source and deploy, not through MCP.
- "Reuse the CLI login for one consent" is out of scope. It returns if the Later decision card rules the control plane the single authorization server.
Acceptance
- Tests: with a stubbed control plane answering one environment,
os mcp connect --printprints that environment's MCP URL and theclaude mcp addline;--url http://localhost:3000prints the local one; several environments without--env→ non-zero, as the deploy command refuses; no file the command writes contains a token. - Manual on staging:
os mcp connectfrom a logged-in CLI, then Claude Code lists the environment's tools after one browser consent. Revoking the grant in the console makes the next call fail (after plugin-auth: an MCP JWT access token cannot be revoked — /oauth2/revoke answers 400 and delete-consent ends nothing, against IAuthService.verifyMcpAccessToken's 'falsy when revoked' (objectstack-ai/cloud#3606 ruling A) #30345 and its cloud half cloud#3606).
Serial: after #30871, whose environment resolution this reuses (⛔ not a second copy). Recommended after objectstack#30345, so every connection this command creates is revocable.
Dedupe (2026-11-06), run as gh api -X GET search/issues -f q=…:
repo:objectstack-ai/objectstack "os mcp" is:issue→ 751 hits (broad). The open ones shown are os package publish: one command from a local checkout to the org's environment — pick the only environment, keep it across login, fail loudly without one, and publish dev builds without hand-bumping the version #30871 and QA run · release-18.3.0-w4-mcp (5/5) · 7fa9873f · 2026-11-02 · 5 PASS / 0 PARTIAL / 0 FAIL / 0 BLOCKED / 0 NOT-RUN #28937 (QA run). None is a connect command.repo:objectstack-ai/objectstack mcp connect cli oauth is:issue is:open→ 19 hits. mcp distribution: verify the remote MCP + OAuth surface against ChatGPT connectors and Codex CLI, and submit the metadata-only registry listings — the remainder of #2714 #20791 (verify remote MCP + OAuth against ChatGPT connectors and Codex CLI) is related, not this. [Epic] ADR-0025 plugin package distribution — the next development phase: M0 record v2, M1 local T0 install loop (first-party, self-signed), M2 cloud fetch + per-plugin permission enforcement, M3 third-party sandbox tier #27108 and MCP delete_record refuses a call without confirm: true (ACTION_CONFIRMATION_REQUIRED), the run_action confirmation gate widened to deletes #30813 are unrelated.repo:objectstack-ai/cloud mcp connect cli is:issue→ 34 hits. Open: chore(release)!: enter RC pre-mode and anchor the train as the v17 major #3600, 中文工作区:分配类站内消息未国际化,且未读状态无法清除 #3353, feat(analytics): honour widget dateGranularity, sortBy/sortOrder, and limit in dataset queries (#3588) #3652, chore: version packages #786 (connector-directory entry shape, related). None.repo:objectstack-ai/cloud authorization server control plane mcp is:issue is:open→ 3 hits: feat(cli): surface migration guide when specVersion trails the installed platform #2914, feat(analytics): honour widget dateGranularity, sortBy/sortOrder, and limit in dataset queries (#3588) #3652, chore: version packages #786. The Later decision card is not filed yet.
Suggested-grade: p3 · a convenience on top of a loop that already works by hand. Its credential half waits on a decision.
Clause-②: no · a CLI command and a local client config. No spec key, route or error code.
🤖 Generated with Claude Code
Activity
objectstack-fleet commentedon Nov 6, 2026
triage#1 · os-zhuang · session_01AavokzJ5DndAwitDXvKy4U
Path: none
Triage: audit · grade kept ·
domain:server·pm:blocked· 2026-11-06T10:02Z⛔ Not a claim.
domain:server, andrepo:cloud#3follows it. The grade the filer set is kept.Blocked-byholds.Generated by Claude Code
objectstack-fleet commentedon Nov 6, 2026
domain:server#3 · os-elon-musk · session_016WV4cytQjG9X1CrCgytUM5
Unblocked · 2026-11-06T14:50Z ·
Blocked-by: #30871is cleared: #30871 closed as completed at 2026-11-06T14:34Z. Movedpm:blocked→pm:queueby the server seats' unblock scan; the card keeps its triage audit and grade, and is picked through the live server split. ⛔ Not a claim.objectstack-fleet commentedon Nov 7, 2026
domain:server#4 · marchtian · session_017ihL2dLQEXWGsE2SZ3Qn5F
Claim: PM loop round 1
Session:
session_017ihL2dLQEXWGsE2SZ3Qn5FAccount:
marchtianBranch:
claude/issue-30889-os-mcp-connectWorktree:
objectstack-issue-30889Domain:
domain:serverSeat:
domain:server#4File surface:
packages/cli/src/commands/mcp/**(new),packages/cli/src/commands/dev.ts(shared connect hint only), the CLI target-resolution helpers #30871 added (read, minimal edits), CLI tests,.changeset/(stop on breach; explain in the report)Container & model:
M,mode:subagent(in-seat background subagent, the project's standing dev form),model: default(dispatch-gates --tier: no path-derived mandate; PM judgment, default tier),effort: highClause-②: no
Metadata-protocol: none
Responsibility: n/a — not a defect card
Thread-read: 6687668354
Premise: holds
Serial constraints cleared: #30871 and #30345 closed completed; sibling #30888 (seat 4) in parallel on the CLI target helpers, later lander merges origin/main
objectstack-fleet commentedon Nov 7, 2026
domain:server#4 · marchtian · session_017ihL2dLQEXWGsE2SZ3Qn5F
os-dev-report
{ "pr": "https://github.com/objectstack-ai/objectstack/pull/31149", "premise_still_valid": true, "gates": { "head": "df587407a", "derived": [ "node scripts/check-adr-0087-registration.mjs --base origin/main :: exit 0", "node scripts/check-adr-0087-registration.mjs --self-test :: exit 0", "node scripts/check-changeset-no-major.mjs --base origin/main :: exit 0", "node scripts/check-closing-keyword-parity.mjs :: exit 0", "node scripts/check-closing-keyword-parity.mjs --self-test :: exit 0", "node scripts/check-comment-mask-adoption.mjs :: exit 0", "node scripts/check-comment-mask-adoption.mjs --self-test :: exit 0", "node scripts/check-comment-mask-corpus.mjs :: exit 0", "node scripts/check-dts-emitted.mjs --self-test :: exit 0", "node scripts/check-empty-changeset.mjs --base origin/main :: exit 0", "node scripts/check-empty-changeset.mjs --self-test :: exit 0", "node scripts/check-issue-citations.mjs :: exit 0", "node scripts/check-plugin-teardown-shape.mjs :: exit 0", "node scripts/check-plugin-teardown-shape.mjs --self-test :: exit 0", "node scripts/ci/assert-turbo-remote-cache.mjs --self-test :: exit 0", "node scripts/ci/plan-test-core-shards.mjs --self-test :: exit 0", "node scripts/ci/prune-turbo-cache.mjs --self-test :: exit 0", "node scripts/ci/snapshot-next.mjs --self-test :: exit 0", "node scripts/docs-audit/check-affected-docs.mjs :: exit 0", "node scripts/docs-audit/check-drift-comment.mjs :: exit 0", "node scripts/pm/release-rehearsal-clone.mjs --self-test :: exit 0", "node scripts/release-pending-publish.mjs --self-test :: exit 0", "pnpm --filter @objectstack/spec run check:duration-unit-keys :: exit 0", "pnpm check:changeset-gate-self-tests :: exit 0", "pnpm check:ci-filter-parity :: exit 0", "pnpm check:cli-test-child-env :: exit 0", "pnpm check:closed-ceremony-text :: exit 0", "pnpm check:cross-package-test-inputs :: exit 0", "pnpm check:dispatcher-error-vocabulary :: exit 0", "pnpm check:doc-authoring :: exit 0", "pnpm check:driver-memory-census :: exit 0", "pnpm check:dts-closure :: exit 0", "pnpm check:dual-build-cjs-loads :: exit 0", "pnpm check:engine-double-contract :: exit 0", "pnpm check:gitlink-declared :: exit 0", "pnpm check:i18n :: exit 0", "pnpm check:i18n-coverage :: exit 0", "pnpm check:i18n-walk-parity :: exit 0", "pnpm check:issue-citations :: exit 0", "pnpm check:keyed-text-bounds :: exit 0", "pnpm check:lean-entry-closure :: exit 0", "pnpm check:logger-receiver-detach :: exit 0", "pnpm check:nul-bytes :: exit 0", "pnpm check:objectql-double-limit :: exit 0", "pnpm check:org-identifier :: exit 0", "pnpm check:page-declaration-shape :: exit 0", "pnpm check:platform-object-tenancy-census :: exit 0", "pnpm check:published-files :: exit 0", "pnpm check:query-options-erasure :: exit 0", "pnpm check:refd-timer-probe :: exit 0", "pnpm check:registry-log-declared :: exit 0", "pnpm check:rest-log-spy-declared :: exit 0", "pnpm check:slot-lookup :: exit 0", "pnpm check:sourcemap-no-sources-content :: exit 0", "pnpm check:system-context-census :: exit 0", "pnpm check:test-source-alias :: exit 0", "pnpm check:tier-file-adoption :: exit 0", "pnpm check:type-check-coverage :: exit 0", "pnpm check:type-check-debt :: exit 0", "pnpm check:undeclared-dep-imports :: exit 0", "pnpm check:vouch-for-reference-callers :: exit 0", "pnpm check:watch-hint-literal :: exit 0", "pnpm check:where-matcher :: exit 0" ], "ran_reconciliation": "dispatch-gates --ran: 63 derived, 63 run, 0 NOT-MEASURED (derived zero: all 63 recorded an exit code, none is 3)", "package": [ "pnpm --filter @objectstack/cli exec vitest run --project unit --maxWorkers=2 :: exit 0 (Test Files 397/397, Tests 5593/5593)", "pnpm --filter @objectstack/cli typecheck :: exit 0", "node --stack-size=4000 node_modules/eslint/bin/eslint.js packages/cli --no-inline-config :: exit 0" ], "ci": "in_progress at hand-off; integration tier declared to CI", "first_pass_exit3_rebuilt": [ "check:dual-build-cjs-loads and check:i18n-coverage exited 3 for missing dist on 10 packages; built them and reran (exit 0)", "check-plugin-teardown-shape --self-test exited 3 for an unreachable pinned commit; it read the commit on rerun (exit 0)", "check:type-check-debt exited 3 on a lock queue-timeout; exit 0 on the final-head run" ], "first_pass_red_fixed": "check:test-source-alias exit 1: the new test reached serve.ts dynamic imports through dev.ts; the dev-equality case moved into dev-mcp-connect-hint-origin.test.ts with the prescribed module-top imports, and the new test no longer imports dev/serve (exit 0 on final head)" }, "line_budget": { "files": 7, "insertions": 686, "deletions": 18, "source": "connect.ts 292, served-version.ts +40/-11, install-target.ts +7/-3, dev.ts +7/-3", "tests": "mcp-connect.test.ts 298, dev-mcp-connect-hint-origin.test.ts +28/-1", "changeset": 14 }, "files_changed": [ ".changeset/30889-os-mcp-connect.md", "packages/cli/src/commands/dev-mcp-connect-hint-origin.test.ts", "packages/cli/src/commands/dev.ts", "packages/cli/src/commands/mcp/connect.ts", "packages/cli/src/utils/install-target.ts", "packages/cli/src/utils/served-version.ts", "packages/cli/test/mcp-connect.test.ts" ], "deviations": [ "staging manual check NOT RUN (no staging login in this container). Steps: os cloud login against staging; os mcp connect; in Claude Code run /mcp, authenticate once in the browser, confirm the environment's tools are listed; revoke the assistant in the environment console (Account, Connect an AI Assistant); confirm the next tool call fails.", "file surface: packages/cli/src/utils/served-version.ts was edited (readCloudEnvironment extracted; fetchEnvironmentOrigin wraps it). It is the #30871 environment-read helper, so it is read as part of the claim's target-resolution helpers (minimal edit).", "--client supports claude-code only; other clients' config shapes not written (none citable from client docs in this run); --print is the path for them.", "content/docs/deployment/cli.mdx not updated (outside the claim's file surface).", "origin/main merged once before the PR (no-op at 5f0037449); 7 later main commits touch no file of this diff." ], "open_questions": [ { "question": "Name for a --url server: default objectstack (matches the os dev banner and the served SKILL.md) or derive from the host?", "options": [ "A objectstack, --name to override (shipped)", "B host-derived name" ], "recommendation": "A, because one endpoint keeps one name across the banner, the skill and this command" } ], "out_of_scope_findings": [ "carrier: none · follow-up: --client values for Cursor, VS Code, Claude Desktop and Codex with config shapes cited from each client's docs · noted, not filed", "carrier: none · follow-up: document os mcp connect in content/docs/deployment/cli.mdx · noted, not filed" ], "mcp_calls": "0", "api_writes": "1 so far: pr_create via fleet-write relay (POST /repos/objectstack-ai/objectstack/dispatches then run 42383299318 then POST /pulls + POST /issues/31149/assignees); this report comment is the 2nd (post-stamped.mjs)", "security_findings": "none" }objectstack-fleet commentedon Nov 7, 2026
domain:server#4 · marchtian · session_017ihL2dLQEXWGsE2SZ3Qn5F
Landed · PR #31149 merged by the merge queue as
21c1f74850eade99b4cb30b86b271d6a9dbd71c7(second queue entry, after one ejection on the #31171 timing flake; audit 6709131544).merged_atfrom the pulls APIcompletedbyFixes #30889origin/maincontentpackages/cli/src/commands/mcp/connect.tspresentpm:dispatchedremoved with this record. Docs + skill mention ofos mcp connectrides #31143.