Skip to content

os mcp connect: register the active environment's MCP server with the developer's MCP client in one command, letting the client run the environment's own OAuth (epic cloud#3652; the single-authorization-server question stays on its Later decision card) #30889

Closed

Description

@objectstack-fleet

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

  1. No os mcp command exists. packages/cli/src/commands/ has no mcp topic (objectstack origin/main c50e0dfa, directory listing). (measured)
  2. os dev prints a connect hint for the local server only: Endpoint, Skill, and claude mcp add --transport http objectstack <origin>/api/v1/mcp (packages/cli/src/commands/dev.ts:197-207). The scaffold README has the same line for localhost: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 in packages/cli/src. (measured)
  3. A hosted environment already serves MCP with OAuth discovery. (measured, cloud origin/main 8f574b9e)
    • The kernel mounts the MCP server plugin (packages/objectos-runtime/src/artifact-kernel-factory.ts:1284).
    • /api/v1/mcp answers 401 with resource_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/mcp on the hosted path (packages/objectos-runtime/src/mcp-revoked-grant.ts:3, cloud#3431).
  4. The environment is the authorization server. objectstack's plugin-auth serves /.well-known/oauth-authorization-server and /.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 with resource = the MCP URL (objectstack#30345 "Measured"). (measured)
  5. Revocation is being fixed. objectstack#30345 (p1, dispatched): an MCP JWT access token cannot be revoked today. (measured on the issue)
  6. Whether the environment's sign-in page reuses the platform session (the epic's "platform SSO already signed in") is not measured here.
  7. 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

  1. os mcp connect resolves 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; --url for self-hosted or a local server) and derives its MCP URL.
  2. 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;
    • --client picks the client, and --print only prints.
    • The server name is the environment's, so several environments can sit side by side.
  3. 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.
  4. 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.
  5. "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

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=…:

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

  1. added
    enhancementNew feature or request
    repo:cloudCard routed whole to the cloud PM: needs cloud code, a producer or a fact (cloud owns it to closure)
    pm:blockedBlocked by another issue/PR — body carries Blocked-by: #N
    domain:serverDomain lane — seat card indexed by label:pm:seat
    on Nov 6, 2026
  2. objectstack-fleet commented on Nov 6, 2026

    @objectstack-fleet
    ContributorAuthor

    triage#1 · os-zhuang · session_01AavokzJ5DndAwitDXvKy4U

    Path: none

    Triage: audit · grade kept · domain:server · pm:blocked · 2026-11-06T10:02Z

    ⛔ Not a claim.

    • Epic objectstack-ai/cloud#3652 (the managed-package dev loop) on the maintainer's word. The framework half is objectstack code, so domain:server, and repo:cloud#3 follows it. The grade the filer set is kept. Blocked-by holds.

    Generated by Claude Code

  3. added
    pm:queueReady for the PM dispatch loop
    and removed
    pm:blockedBlocked by another issue/PR — body carries Blocked-by: #N
    on Nov 6, 2026
  4. objectstack-fleet commented on Nov 6, 2026

    @objectstack-fleet
    ContributorAuthor

    domain:server#3 · os-elon-musk · session_016WV4cytQjG9X1CrCgytUM5

    Unblocked · 2026-11-06T14:50Z · Blocked-by: #30871 is cleared: #30871 closed as completed at 2026-11-06T14:34Z. Moved pm:blocked → pm:queue by the server seats' unblock scan; the card keeps its triage audit and grade, and is picked through the live server split. ⛔ Not a claim.

  5. added
    pm:dispatchedDispatched to a dev agent by /pm-dispatch
    and removed
    pm:queueReady for the PM dispatch loop
    on Nov 7, 2026
  6. objectstack-fleet commented on Nov 7, 2026

    @objectstack-fleet
    ContributorAuthor

    domain:server#4 · marchtian · session_017ihL2dLQEXWGsE2SZ3Qn5F

    Claim: PM loop round 1
    Session: session_017ihL2dLQEXWGsE2SZ3Qn5F
    Account: marchtian
    Branch: claude/issue-30889-os-mcp-connect
    Worktree: objectstack-issue-30889
    Domain: domain:server
    Seat: domain:server#4
    File 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: high
    Clause-②: 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

  7. objectstack-fleet commented on Nov 7, 2026

    @objectstack-fleet
    ContributorAuthor

    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"
    }
  8. removed
    repo:cloudCard routed whole to the cloud PM: needs cloud code, a producer or a fact (cloud owns it to closure)
    on Nov 7, 2026
  9. objectstack-fleet commented on Nov 7, 2026

    @objectstack-fleet
    ContributorAuthor

    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).

    Reading Value
    PR state merged, merged_at from the pulls API
    Card state closed completed by Fixes #30889
    origin/main content packages/cli/src/commands/mcp/connect.ts present

    pm:dispatched removed with this record. Docs + skill mention of os mcp connect rides #31143.

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

Metadata

Metadata

Assignees

Labels

domain:serverDomain lane — seat card indexed by label:pm:seatenhancementNew feature or requestpriority:p3

Type

No type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions