Skip to content

plugin-auth: MCP clients cannot register over OAuth DCR — /oauth2/register defaults a missing application_type to web and refuses the http://localhost redirect every local MCP client sends #25118

Closed

Description

@hotlong

Ruled: 6329352251 · letter A1 · 2026-10-22T00:05Z

Filing gate ① a reproduced defect — reach: measured live on ObjectOS Cloud production, https://huayan.objectos.ai (framework pin 9d13b5fc83, @better-auth/oauth-provider 1.7.3), 2026-10-21, with Claude Code 2.1.159 as the MCP client. Filed at the maintainer's request in a cloud session.

Reader (who acts): objectstack triage → the plugin-auth lane.

What fails

Every MCP client that authorizes through Dynamic Client Registration with a loopback redirect — Claude Code, and by the same shape Cursor, VS Code and Codex CLI — cannot connect to an ObjectStack MCP endpoint over OAuth. Registration is refused before the browser ever opens:

SDK auth failed: web clients require https redirect URIs on non-loopback hosts: http://localhost:63678/callback

The OAuth track is otherwise healthy on the same host: /api/v1/mcp answers 401 with WWW-Authenticate: Bearer … resource_metadata=…, /.well-known/oauth-protected-resource and /.well-known/oauth-authorization-server/api/v1/auth both answer 200. Only the registration step fails, so OAuth for MCP works today only for clients with an https callback (the claude.ai web connector). API keys are the sole working path for local agents.

Reproduction (no state written — the request is refused)

curl -sS -X POST https://<env-host>/api/v1/auth/oauth2/register \
  -H 'content-type: application/json' \
  -d '{"client_name":"probe","redirect_uris":["http://localhost:63678/callback"],"grant_types":["authorization_code","refresh_token"],"response_types":["code"],"token_endpoint_auth_method":"none"}'
# → 400 {"error":"invalid_redirect_uri","error_description":"web clients require https redirect URIs on non-loopback hosts: http://localhost:63678/callback"}

This is the body shape an MCP client sends: a public client (token_endpoint_auth_method: none), PKCE, a loopback http redirect, and no application_type.

Cause

@better-auth/oauth-provider 1.7.3 applyOAuthClientRegistrationDefaults(client, defaultApplicationType = "web") fills a missing application_type with web (the OIDC DCR default), and validateClientRedirectUri refuses any http or loopback redirect for web. The default is not configurable from plugin options. Only the client-metadata-document path defaults to native.

The MCP authorization spec, though, has clients register http://localhost / 127.0.0.1 redirects (RFC 8252 §7.3), and the clients in the field do not send application_type. So the provider's spec-correct default meets clients that follow a different spec, and plugin-auth sits between them without reconciling the two.

Not a duplicate

Dedupe: REST search/issues, closed included —

The nearest is #24773 (closed 2026-10-21, fixed by #24992 / #25038 / #25011): the same provider message, but from the Setup form's door POST /api/v1/auth/sys-oauth-application/register, where an administrator now picks Web or Native. This card is the other door — better-auth's own POST /api/v1/auth/oauth2/register (RFC 7591 DCR) that an MCP client calls anonymously, where there is no form and no type. Nothing in plugin-auth on 9d13b5fc83 or on main touches that door's application_type. #20791 (open) verifies the remote MCP + OAuth surface against other clients and would hit this card, but does not name it.

Suggested fix (for the lane to judge)

A before-hook on /oauth2/register in plugin-auth: when the body has no application_type, token_endpoint_auth_method is none, and every redirect_uri is http on exactly localhost, 127.0.0.1 or [::1], register it as native. Every other body keeps the provider's default and its rule. That narrows nothing and widens only the shape RFC 8252 already blesses for native public clients.

Acceptance: the curl above returns 201 with application_type: "native"; the same body with https://evil.example/cb, or with token_endpoint_auth_method: client_secret_basic, is still refused; claude mcp add --transport http x https://<env>/api/v1/mcp then /mcp → Authenticate completes in the browser and lists the tools.

Activity

  1. objectstack-fleet commented on Oct 21, 2026

    @objectstack-fleet
    Contributor

    Claim: PM loop round 1 · 2026-10-21T22:49Z · p1, domain:services, ruling A (6327761812, the maintainer's 「A:用提供方 infer (推荐)」)
    Session: session_01UnHcTADjeg3VwxF21QHj7W (the dispatching seat; the dev is a mode:subagent run of this session, told apart by its branch)
    Account: os-bill (the seat's linked user as GET /user answers it; always the card's assignee)
    Branch: claude/issue-25118-dcr-infer-native
    Worktree: objectstack-issue-25118
    Domain: domain:services
    Seat: domain:services#2 (seat post #21118)
    Ruling-ref: 6327761812
    File surface (line anchors at origin/main abc25a353):


    Generated by Claude Code

  2. objectstack-fleet commented on Oct 21, 2026

    @objectstack-fleet
    Contributor

    os-dev-report
    {
    "issue": 25118,
    "status": "needs_decision",
    "branch": "claude/issue-25118-dcr-infer-native",
    "pr": null,
    "session": "session_01UnHcTADjeg3VwxF21QHj7W — the parent's harness-stamped id (subagent run of the dispatching seat)",
    "premise_still_valid": true,
    "summary": "Stopped at the ruling's first measurement, as ruling 6327761812 directs ('a mismatch is reported as a fork'). No code was written and no PR was opened. The defect is real and the ruled mechanism exists on 1.7.12. With the options AuthManager actually produces, the card's body (public client, loopback http redirect, no application_type) gets 400 'web clients require https redirect URIs on non-loopback hosts'. Adding clientRegistrationDefaultApplicationType: 'infer' turns it into 201 application_type native. GAP 1 CONTRADICTS the decision's assumption. 'infer' classifies by redirect URIs only and never reads token_endpoint_auth_method. So an anonymous CONFIDENTIAL registration that omits application_type and lists a loopback or custom-scheme redirect also becomes native and receives a client_secret: 400 today, 201 with infer (cells R2, R2b, R9). GAP 2 HOLDS: the authorize step without PKCE is refused for every DCR client in every cell. Two facts bound the fork. (F1) The same confidential registration is already accepted anonymously on main today when the body spells application_type native (R2c: 201, client_secret issued), so infer adds no client shape that can be registered; it changes only what an omitted field means. (F2) PKCE stays enforced for all of these clients. Partition-2 assumptions 1 and 2 hold as measured. Assumption 3 holds by code reading and was not measured with DCR off. The fork, with options along the four axes, is in open_questions.",
    "tests": "No product test was written (needs_decision). The measurement was a throwaway vitest probe, never committed. It is archived at /tmp/claude-0/-home-user/1c8ca8e4-330d-5501-a9b1-41cf766c6c6c/scratchpad/issue-25118/zz-probe-25118.test.ts and was deleted from the worktree before cleanup. The probe builds a real AuthManager (OS_MCP_SERVER_ENABLED=true) and captures the exact options it hands oauthProvider (allowDynamicClientRegistration true, allowUnauthenticatedClientRegistration true, clientRegistrationRequirePKCE not passed, clientRegistrationDefaultApplicationType not passed). It then boots the REAL better-auth 1.7.12 + @better-auth/oauth-provider 1.7.12 handler (memory adapter, the auth-manager.mcp-oauth-resource.test.ts harness pattern) twice: once with those options, once with the same options plus 'infer'. It drives POST /api/v1/auth/oauth2/register for each cell, then GET /oauth2/authorize?resource=MCP_URL with and without S256 PKCE for each registered loopback client. Command: bash scripts/pm/os-verify-lock.sh -c 'pnpm --filter @objectstack/plugin-auth exec vitest run --maxWorkers=2 src/zz-probe-25118.test.ts'. Final run: Test Files 1 passed (1), Tests 2 passed (2), os-verify-lock: VERDICT command-exit 0 · held the lock 18s · waited 328s. This was after the pnpm --workspace-concurrency=2 --filter '@objectstack/plugin-auth^...' build closure (VERDICT command-exit 0). Readings as today then with infer: R1 public, loopback, no type: 400 then 201 native. R2 client_secret_basic, loopback, no type: 400 then 201 native with a secret. R2b auth method omitted (provider defaults it to client_secret_basic), loopback, no type: 400 then 201 native with a secret. R2c client_secret_basic, loopback, application_type native: 201 with a secret in BOTH modes. R2d public, loopback, native: 201 in both. R3 public, https://evil.example/cb alone, no type: 201 web in both. R3b public, loopback plus https://evil.example/cb, no type: 400 then 201 native. R3c the same pair with application_type native: 201 in both. R4 public, loopback, application_type web: 400 in both. R5 public, http://evil.example/cb, no type: 400 in both. R6 public, cursor:// custom scheme, no type: 400 then 201 native. R7 public, http://127.0.0.1:5000, no type: 400 then 201 native. R8 public, http://localhost.evil.example, no type: 400 in both. R9 client_secret_basic, cursor:// custom scheme, no type: 400 then 201 native with a secret. Authorize, every registered loopback client in both modes: with PKCE the server sends 302 to /_console/oauth/consent. Without PKCE it sends 302 to the redirect with error=invalid_request, 'pkce is required for public clients' (R1, R2d, R3b, R3c) or 'pkce is required for this client' (R2, R2b, R2c). Code reads in the installed dist (node_modules @better-auth/oauth-provider 1.7.12): the option type is at oauth-DXiqbRzK.d.mts:1469 ('web' | 'native' | 'infer', default 'web'), and clientRegistrationRequirePKCE @default true is at :1444-1446. resolveDynamicRegistrationDefaultApplicationType (authorize-D_jy3s-1.mjs:1718-1722) tests redirect_uris with some(), never the auth method. applyOAuthClientRegistrationDefaults (:1688-1692) fills token_endpoint_auth_method with client_secret_basic and fills application_type only when absent. Every redirect is validated by the resolved type (:1857). registerEndpoint (:1792-1796) throws FORBIDDEN 'Client registration is disabled' when allowDynamicClientRegistration is false. isPKCERequired (utils-BfpTo7XY.mjs:912-920): 'none' always requires PKCE; otherwise requirePKCE ?? true. plugin-auth puts no layer in front of /oauth2/register: auth-manager.ts's global before-hook has no branch for that path, and auth-plugin.ts mounts no raw route on it.",
    "gates": "No diff, so no local gate is owed. node scripts/pm/dispatch-gates.mjs --commands --repo objectstack-ai/objectstack gave exit 2: 'this branch changes nothing against origin/main (merge base abc25a3) — nothing to derive'; '--repo objectstack-ai/objectstack ... it holds'. No PR, so there are no CI check-runs to cite. git status --porcelain was empty and git diff HEAD was empty at abc25a3 before the worktree was removed.",
    "dogfood_battery": "n/a — no PR (stopped at the ruling's fork before any code)",
    "mcp_calls": "0 — no MCP GitHub tool was called; reads went through gh api (REST GET issues/25118, issues/25118/comments, issues/25096/comments for the report shape)",
    "api_writes": "1 — POST /repos//issues/25118/comments (this os-dev-report, via scripts/pm/post-stamped.mjs). Separately, not a REST write: one git push of the empty branch claude/issue-25118-dcr-infer-native at abc25a3 (the write-route probe and the claim's landing marker). No pr_create, no label write, no assignee write.",
    "line_budget": "n/a — no diff",
    "files_changed": [],
    "deviations": [
    "Served-stack readings were NOT MEASURED. Reason: the pnpm dev:crm preflight (scripts/check-dev-prereqs.mjs) requires build artefacts for all 68 workspace packages, which is a full build that does not fit a foreground window on the shared lock, and the fork stops the card before the acceptance stage those readings serve. The readings instead come from the vendor's REAL handler built from the exact options AuthManager produces, and plugin-auth places no layer in front of /oauth2/register (see tests).",
    "The browser walk in the ruling (claude mcp add --transport http NAME https://ENV-HOST/api/v1/mcp, then /mcp, then Authenticate) was NOT MEASURED. Reason: it needs a deployed environment, and the card stopped at the fork.",
    "Partition-2 assumption 3 (the option changes nothing while DCR is off) holds by code reading only (authorize-D_jy3s-1.mjs:1792-1796). It was not measured with DCR off.",
    "In the probe's infer leg, the option was injected into the captured options object rather than written into auth-manager.ts, so that no speculative code was written before the fork is decided."
    ],
    "open_questions": [
    {
    "question": "Fork on ruling A (decision 6318906173, confidence gap 1). Under clientRegistrationDefaultApplicationType: 'infer' the provider classifies by redirect URIs only. An anonymous CONFIDENTIAL registration that omits application_type and lists a loopback or custom-scheme redirect is therefore registered as native and issued a client_secret. Its token_endpoint_auth_method is client_secret_basic, or omitted, which the provider fills as client_secret_basic. Today it gets 400; with infer it gets 201 (R2, R2b, R9). The decision assumed native classification would take effect for public loopback clients only. Bounding facts: (F1) the same confidential registration is ALREADY accepted anonymously on main when the body spells application_type native (R2c: 201 with a secret), so infer widens no set of registrable clients, only the meaning of an omitted field; (F2) PKCE is still required at authorize for every one of these clients ('pkce is required for public clients' is unconditional in the provider; 'pkce is required for this client' follows from clientRegistrationRequirePKCE defaulting to true, which plugin-auth does not override). A related note that is NOT a fork, for the acceptance wording: infer classifies native when ANY redirect qualifies, as the decision states. https://evil.example/cb ALONE stays web (R3: 201 web in both modes). Paired with a loopback URI in one body, it is judged by the native rule and accepted (R3b: 400, then 201), exactly as it is today with application_type native spelled (R3c: 201). Which way should the card go?",
    "options": [
    "A1 — Land ruling A as written: the one option line clientRegistrationDefaultApplicationType: 'infer' plus a comment citing RFC 8252 §7.3 and the MCP authorization spec. Pins run on the vendor's real handler for the four acceptance cells. One more pin records the confidential cell as it really behaves (201 native with a secret, authorize refused without PKCE), so no reader takes the option as public-only. Customer-visible: local MCP clients (Claude Code, Cursor, VS Code, Codex CLI) connect over OAuth. A confidential body that omits application_type with a loopback redirect is now accepted, as it already is with the field spelled. Work: services, 1 PR (one option line, a comment, one test file, a patch changeset), no precedent needed. Axes. Business need: serves the measured pull (Claude Code on production, a token_endpoint_auth_method none body) and spends nothing on the confidential cell, which no measured client sends. Long-term: one classification rule, owned by the provider and evolving with the spec; RFC 8252 §8.4 treats native apps as public clients except where DCR provisions per-instance secrets, which is exactly the R2 shape. AI error: one declarative option, and the confidential pin keeps its real scope explicit. Startup focus: no new mechanism.",
    "A2 — 'infer' plus a plugin-auth before-hook on /oauth2/register that keeps web (so refuses the loopback redirect) when application_type is omitted AND token_endpoint_auth_method is not none. Customer-visible: the same as A1 for public clients; an omitted-type confidential loopback body stays refused. Work: services, 1 PR (about 30-50 lines plus pins), re-verified on every provider bump. Axes. Business need: no measured producer of the cell it refuses. Long-term: a second classification rule beside the provider's, the duplication the decision rejected option B for. AI error: it reads like a boundary, but one field bypasses it (R2c is 201 today and stays 201): a guard that declares more than it enforces. Startup focus: a new hook to maintain.",
    "A3 — A1 now. Separately, if the maintainer wants anonymous registration limited to public clients, a SEPARATE card that refuses every unauthenticated registration whose token_endpoint_auth_method is not none, whatever application_type says. Of the three, only this option moves the boundary F1 describes: it narrows a door that is open on main today, independent of infer. The installed provider has no option for it: 1.7.12 offers only allowUnauthenticatedClientRegistration on or off, and documents that confidential clients receive a one-time client_secret. So it would be a plugin-auth hook. Work: a second PR (about 50 lines plus pins) and its own decision, because it narrows. Axes. Business need: no measured producer either way today. Long-term: a real policy, but outside this card's ruling. AI error: a genuine, loud refusal at the door. Startup focus: a new mechanism, which defaults to no unless the maintainer names it."
    ],
    "recommendation": "A1. On all four axes the reading moves no boundary. Every body that infer newly accepts is accepted today with application_type spelled out (F1), and PKCE stays enforced at authorize for all of them (F2). A2 would add a duplicate rule that one field bypasses, which is the declared-but-not-enforced shape. If a public-only anonymous door is wanted, that is A3's separate decision, and it does not need to hold this card, whose measured pull is a public client. Self-check: A1 on the long-term axis alone; the other three axes do not flip it."
    }
    ],
    "out_of_scope_findings": [
    "carrier: #25118 (the implementing PR under whichever ruling) · noted, not filed: the existing real-provider harness in packages/plugins/plugin-auth/src/auth-manager.mcp-oauth-resource.test.ts (registerDcrClient) registers with an explicit application_type native. That is why the suite stayed green through this defect: the card's body omits the field. The implementing PR's pins should send the card's own body shape (no application_type)."
    ]
    }


    Generated by Claude Code

  3. objectstack-fleet commented on Oct 21, 2026

    @objectstack-fleet
    Contributor

    [Decision] 裁决 A 的置信缺口 1 实测不符:infer 只看回调地址,不看客户端是否公共

    domain:services seat 2 (seat post #21118) · os-bill · session_01UnHcTADjeg3VwxF21QHj7W · 2026-10-21T23:31Z · Thread-read: 6328861459

    Release: session_01UnHcTADjeg3VwxF21QHj7W · Cause: the dev stopped at the fork ruling 6327761812 names ("a mismatch is reported as a fork"), with no code and no PR · Goes to: needs-user-decision. This write's label stroke clears the assignee. The branch claude/issue-25118-dcr-infer-native is empty at abc25a353.

    一句话问题: 打开 infer 后,本地 AI 客户端(Claude Code 等)能通过 OAuth 连上 MCP;但同一开关也会让「未声明类型、带密钥的机密客户端 + 本机回调」的匿名注册从被拒变成被接受。裁决时假设它只放行公共客户端,实测不是。

    背景(开发实测 6328861459;席位已对照提供方 1.7.12 发布包源码复核)

    • 缺陷与修法都成立: 卡面那条请求(公共客户端、本机 http 回调、未声明类型)今天 400;加上 clientRegistrationDefaultApplicationType: 'infer' 后 201,类型为 native。
    • 缺口 1 不符: 提供方的判定函数 resolveDynamicRegistrationDefaultApplicationType 只检查回调地址(任一为非 http(s) 协议或精确本机 http 即判 native),从不读 token_endpoint_auth_method。席位读的是 npm 发布包 @better-auth/oauth-provider@1.7.12 的 dist/authorize-D_jy3s-1.mjs:1718-1722。所以「机密客户端(client_secret_basic,或省略认证方式时提供方默认填 client_secret_basic)+ 本机或自定义协议回调 + 未声明类型」今天 400,开 infer 后 201 并下发 client_secret。
    • 缺口 2 成立: 授权阶段缺 PKCE 时,所有这些客户端一律被拒。公共客户端是无条件的;机密客户端是因为 clientRegistrationRequirePKCE 默认 true,plugin-auth 没有覆盖(dist/utils-BfpTo7XY.mjs:912-920)。
    • 边界事实 F1: 同样的机密注册,只要请求里写明 application_type: native,今天在 main 上就已经被匿名接受(201、带密钥)。infer 不新增任何「能注册的客户端形态」,只改变「没写类型」时的含义。
    • 补充(不是分叉): https://evil.example/cb 单独出现仍按 web 判(201 web,两种模式相同)。它与一个本机回调写在同一请求里时,按 native 规则校验并被接受,与今天写明 native 时完全一样。

    Governing text

    None found in the repo for dynamic client registration (decision 6318906173 searched docs/adr, 0 hits; ruling 6327761812 records the same). External: the MCP authorization spec; RFC 8252 §7.3 / §8.3; RFC 8252 §8.4 (native apps are public clients, except where dynamic registration issues a per-instance secret).

    是否改协议: 否。不碰 spec 或元数据契约,只改 plugin-auth 交给 OAuth 提供方的运行时配置。这是匿名注册入口的安全边界,属人工地板,所以呈维护者,席位不代裁。

    前提(复核命令)

    • 判定只看回调:下载 npm view @better-auth/oauth-provider@1.7.12 dist.tarball 后执行 grep -n "function resolveDynamicRegistrationDefaultApplicationType" -A4 package/dist/authorize-D_jy3s-1.mjs。阳性对照:同文件 grep -n "function isExactLoopbackHttpRedirectUri" 必中。
    • plugin-auth 今天未设该选项:git grep -c clientRegistrationDefaultApplicationType origin/main -- packages/plugins/plugin-auth/src,预期 0。阳性对照:git grep -c allowDynamicClientRegistration origin/main -- packages/plugins/plugin-auth/src/auth-manager.ts,预期 1。

    选项

    选项 做什么 客户可感知后果 工作量
    A1 按裁决 A 原样落地 一行 infer 配置加注释;在提供方真实处理器上钉四个验收格,外加一格如实记录机密客户端的真实行为(201 native 带密钥,缺 PKCE 授权被拒),写明它不是「只放公共客户端」 本地 MCP 客户端(Claude Code、Cursor、VS Code、Codex CLI)走 OAuth 连上;未写类型的机密本机回调注册改为接受,与今天写明类型时一致 services · 1 PR(一行配置 + 一个测试文件 + patch changeset)· 无先例
    A2 infer 加一个注册前钩子 未写类型且认证方式不是 none 时仍按 web 判,即拒绝本机回调 公共客户端同 A1;未写类型的机密本机回调注册仍被拒,但写明 native 即可绕过(今天就能) services · 1 PR(约 30–50 行 + 钉子)· 每次提供方升级要复核 · 无先例
    A3 先 A1,另立一卡 A1 照落;若希望匿名注册只允许公共客户端,另立卡:拒绝所有认证方式不是 none 的匿名注册,不论类型怎么写 本卡同 A1;另卡把今天已开着的「匿名机密注册」入口关掉,是真正收紧边界的那一步 本卡同 A1;另卡 services · 1 PR(约 50 行 + 钉子),需另裁 · 无先例

    业务含义直译:

    • A1:用 OAuth 组件自带的「原生应用识别」开关,把测出来的边界如实写进测试。
    • A2:在开关前面再加一道自家门禁,但这道门写明类型就能绕过,等于只是看起来像边界。
    • A3:本卡按 A1 解决客户连不上的问题;要不要把匿名注册收紧到「只许公共客户端」,另作一个决定。

    四轴(业务立场)

    • 实际业务需求: 真实拉动是生产上实测的 Claude Code,它是公共客户端(token_endpoint_auth_method: none)。机密客户端那一格今天没有测到任何真实客户端在发。A1 满足拉动,对那一格不额外花钱。
    • 项目长远合理性: 分类规则只有一份,由提供方随规范演进。RFC 8252 §8.4 本就允许动态注册给原生应用发每实例密钥,这正是机密那一格的形态。A2 在平台里增生第二份分类规则,正是裁决不取 B 的理由。
    • 防 AI 写错: 出错时谁看到什么。A1 下,带密钥的本机客户端也必须走 PKCE,越界的授权请求会被响亮拒绝。A2 声明了一道边界却兑现不了,写明类型就绕过,属于「声明而不兑现」。
    • 创业阶段不扩散: A1 不新增机制;A2 新增一个要长期维护的钩子;A3 的另卡是新机制,默认不做,除非维护者点名要。

    推荐:A1

    • 只看①得出的终态: 两年后 MCP 授权规范是 AI 客户端接入的行业标准。主流 OAuth 服务(Okta、Auth0、Keycloak)按 RFC 8252 识别原生客户端、放行本机回调并强制 PKCE;是否允许匿名注册机密客户端,是另一条独立策略。平台用提供方那一份分类规则,不自写。
    • 自检行: 只看①选 A1;②③④ 是否翻转:否。
    • 置信缺口: 已部署环境上的浏览器验收(claude mcp add → /mcp → Authenticate)未实测;读数来自用 AuthManager 实际选项构造的提供方真实处理器,plugin-auth 在 /oauth2/register 前没有任何拦截层。
    • 不取 A2: 一条可被一个字段绕过的重复规则。
    • 不取「整卡等 A3」: 匿名机密注册今天就已开着(F1),与 infer 无关,不必拖住这张有真实拉动的卡。

    裁后执行段

    • 若选 A1 或 A3: 本卡回 pm:queue,本席立即派发。派发令带开发的实测格作钉子(含机密格的真实行为),并让钉子发送卡面原样请求(不写 application_type)。现有 auth-manager.mcp-oauth-resource.test.ts 写明了 native,所以这次缺陷没被它抓到。changeset 为 patch。
    • 若选 A3: 另立的那张卡由本席立卡,带「拒绝所有非 none 的匿名注册」的判据与选项,进决策箱另裁;它不阻塞本卡。
    • 若选 A2: 本卡回 pm:queue,派发令按 A2 实现钩子,钉子覆盖「写明 native 仍可注册」这一格,并在 PR 正文写明。

    os-decision-facets

    • ① 项目长远合理性:A1 只用提供方的单一分类规则,不增生平台特例;A2 增生一份可被绕过的重复规则。
    • ② 实际业务拉动:有实测拉动:生产上 Claude Code(公共客户端)连不上;机密本机回调这一格无实测发送方。
    • ③ 防 AI 犯错:A1 下所有新接受的客户端仍须 PKCE,越界授权被响亮拒绝;A2 是声明而不兑现的门禁。
    • ④ 创业阶段不扩散:A1 零新机制;A2 新增钩子;A3 另卡是新机制,默认不做。
      Prior rulings read: dynamic client registration,oauth2/register,oauth-provider → 0 hits in docs/adr; none; thread: 1 ruling (6327761812)

    Related: decision 6318906173; ruling 6327761812; the dev report 6328861459; #24773 (the Setup form's door, closed); #20791 (pm:awaiting-maintainer, the remote MCP + OAuth verification).


    Generated by Claude Code

  4. objectstack-fleet commented on Oct 22, 2026

    @objectstack-fleet
    Contributor

    Ruling: batch #383 item 1 · letter A1 · maintainer 「同意」 2026-10-22T00:05Z

    Director seat, summon #47, session_01TQvbE2ENCniJr1ffCzbPc6 (os-bill, writing through the relay). Presented in this session's chat as batch #383 item 1, from the domain:services seat 2 decision 6328903272 and the dev report 6328861459. The maintainer's own ruling A (6327761812, 「A:用提供方 infer (推荐)」) stands; this ruling settles the confidence gap that ruling named and the dev measured as a mismatch. The director's delta: git grep -c clientRegistrationDefaultApplicationType origin/main -- packages/plugins/plugin-auth/src → 0 (control allowDynamicClientRegistration in auth-manager.ts → 1), so the ruled change is still undone; the director downloaded the npm tarball of @better-auth/oauth-provider@1.7.12 and read dist/authorize-D_jy3s-1.mjs:1718-1722: resolveDynamicRegistrationDefaultApplicationType tests redirect_uris only (isNonHttpRedirectUri or isExactLoopbackHttpRedirectUri) and never reads token_endpoint_auth_method; clientRegistrationRequirePKCE is declared at dist/oauth-DXiqbRzK.d.mts:1446. The seat's and the dev's readings hold. Thread read to its end at this write: 6328903272 is the newest comment.

    Governing text: none in the repo for dynamic client registration (docs/adr → 0 hits, recorded on 6318906173 and 6327761812); external: the MCP authorization specification; RFC 8252 §7.3 / §8.3 (loopback redirects for native apps) and §8.4 (a native app is a public client, except where dynamic registration issues a per-instance secret).

    What the ruling binds

    1. Letter A1. plugin-auth passes clientRegistrationDefaultApplicationType: 'infer' to the OAuth provider, with a comment naming this card and the measured boundary. No hook, no second classification rule.
    2. The measured boundary is pinned as it is, on the provider's real handler: the card's own request (public client, loopback http redirect, no application_type) registers 201 native; https://evil.example/cb alone stays web; http://evil.example/cb and http://localhost.evil.example stay refused; a confidential registration (client_secret_basic, or the auth method omitted) with a loopback or custom-scheme redirect and no type registers 201 native with a client_secret — the pin states this plainly rather than claiming "public clients only"; and the authorize step without PKCE is refused for every one of those clients. The pins send the body as the card spells it, without application_type (the existing auth-manager.mcp-oauth-resource.test.ts spells native, which is why it did not catch this defect).
    3. Why the confidential cell is not a widening: the same confidential registration is accepted anonymously on main today when the body spells application_type: native (the dev's R2c), so infer adds no registrable client shape; it changes only what an omitted field means. Any token it issues still needs the user's consent and PKCE. The question whether anonymous registration should admit confidential clients at all is a separate policy, open today independent of this card; it is not filed now (zero measured senders, and a new gate defaults to no), and the seat may file it as a needs-user-decision card if a producer or an incident gives it a pull.
    4. Not taken: A2 (a platform hook that an explicit application_type: native bypasses — a declared boundary that is not enforced); A3 (A1 plus a second card now — the second card has no pull today, so it is not opened).
    5. Acceptance carried from 6327761812: the environment's Setup → Integrations → Connect an Agent page becomes true; the browser walk claude mcp add --transport http <name> https://<env-host>/api/v1/mcp → /mcp → Authenticate completing and listing the tools is part of the acceptance, measured on a deployed environment after the cloud pin moves.

    Effort of the chosen letter (the card's column): domain:services · 1 PR · one configuration line plus one test file plus a patch changeset · no precedent. Confidence gap carried: the browser acceptance on a deployed environment is not yet measured; the readings come from the provider's real handler built with AuthManager's actual options.

    State: needs-user-decision → pm:queue. domain:services seat 2 (seat post #21118, session_01UnHcTADjeg3VwxF21QHj7W) re-claims and dispatches at once; the dispatch prompt carries the dev's measured cells as the pins. Branch claude/issue-25118-dcr-infer-native is empty at abc25a353; the next claim decides whether to reuse it.


    Generated by Claude Code

  5. objectstack-fleet commented on Oct 22, 2026

    @objectstack-fleet
    Contributor

    Claim: PM loop round 1 · 2026-10-22T00:19Z · p1, domain:services, re-claim under ruling A1 (6329352251, the maintainer's 「同意」), which settles ruling A's (6327761812) confidence gap
    Session: session_01UnHcTADjeg3VwxF21QHj7W (the dispatching seat; the dev is a mode:subagent run of this session, told apart by its branch)
    Account: os-bill (the seat's linked user as GET /user answers it; always the card's assignee)
    Branch: claude/issue-25118-dcr-infer-native (reused; empty at abc25a353)
    Worktree: objectstack-issue-25118
    Domain: domain:services
    Seat: domain:services#2 (seat post #21118)
    Ruling-ref: 6329352251
    File surface (line anchors at origin/main abc25a353):

    • packages/plugins/plugin-auth/src/auth-manager.ts: the oauthProvider({ … }) options (about :4311–:4380), one option, clientRegistrationDefaultApplicationType: 'infer', with a comment naming this card and the measured boundary. Nothing else in the file.
    • Test files under packages/plugins/plugin-auth/src/; .changeset/25118-*.md (a patch).
    • ⛔ No hook and no second classification rule (A2 not taken). Not auth-plugin.ts, not packages/spec/**, not the Setup form's sys-oauth-application door, not pnpm-workspace.yaml or any lockfile.
    • Stop on a breach and explain it in the report.
      Container & model: M, mode:subagent, model: opus (TIER_DEFAULT; dispatch-gates --tier on this surface: "no path-derived mandate … floor sonnet · default opus · ceiling fable"). The dispatch waits for a free slot (batch 3, the maintainer's 「并发 3」).
      Clause-②: no — a runtime security-boundary change (what an anonymous registrant may register). The dispatch rules class that as the human-floor security class, and the maintainer ruled it (A, then A1).
      Responsibility: the vendor's DCR default (web) meets MCP clients that omit application_type, and plugin-auth passes no option | better-auth 1.7.12's clientRegistrationDefaultApplicationType: 'infer', unset today | every local MCP client (Claude Code, Cursor, VS Code, Codex CLI) that registers anonymously over OAuth; measured on production
      Thread-read: 6329352251
      Serial constraints cleared:
    • This seat's plugin-auth: under the open and email_domain postures, a sign-up for an address that already has an account answers the success shape and mails that address (ruling 1B on #25040); without a live mail transport the explicit refusal stays #25060 (PR feat(plugin-auth, plugin-email): under open and email_domain, a sign-up for an existing address answers like a new one and mails that address, while mail is delivered #25145) edits auth-manager.ts in other regions (the sign-up path and the vendor options beside sendVerificationEmail, about :2132–:2150). The file is shared, so whichever lands second merges main.
    • No other open PR touches plugin-auth source (the open PRs' file lists were read this round).

    Generated by Claude Code

  6. objectstack-fleet commented on Oct 22, 2026

    @objectstack-fleet
    Contributor

    os-dev-report
    {
    "issue": 25118,
    "status": "done",
    "branch": "claude/issue-25118-dcr-infer-native",
    "pr": "#25156",
    "session": "session_01UnHcTADjeg3VwxF21QHj7W — the parent's harness-stamped id (mode:subagent run of the dispatching seat)",
    "premise_still_valid": true,
    "summary": "Implemented ruling A1 (6329352251) as written. packages/plugins/plugin-auth/src/auth-manager.ts now passes clientRegistrationDefaultApplicationType: 'infer' to @better-auth/oauth-provider 1.7.12, with a comment that names #25118, the ruling, RFC 8252 §7.3/§8.4, the MCP authorization spec and the measured boundary, the confidential cell included. There is no hook and no second classification rule. A new test file, auth-manager.mcp-oauth-dcr-infer.test.ts, boots the REAL better-auth and oauth-provider 1.7.12 handler from the exact options AuthManager produces. It pins the ruling's cells with bodies that omit application_type (the first is the card's body verbatim, and the test asserts it has no application_type key), plus a PKCE differential for every registered client and a DCR-off cell. A patch changeset (.changeset/25118-dcr-infer-native.md, Clause-②: no, Metadata-protocol: none) rides with it. Draft PR #25156 is open with assignee os-bill. Premises re-verified on origin/main 459afea: the pin is 1.7.12; clientRegistrationDefaultApplicationType had 0 hits in plugin-auth/src (control allowDynamicClientRegistration: 1); the classifier at dist/authorize-D_jy3s-1.mjs:1718-1722 reads redirect_uris only and runs only for registrationSource 'dynamic' (:1826), not for the 'managed' Setup-form door (:2264); plugin-auth has no before-hook branch or raw route on /oauth2/register. Every Partition-2 cell reproduced. The earlier carrier note (the existing harness spells native) is discharged by the new file.",
    "tests": "Readings at 5a7e1f0, shared box. (1) New file: bash scripts/pm/os-verify-lock.sh -c 'pnpm --filter @objectstack/plugin-auth exec vitest run --maxWorkers=2 src/auth-manager.mcp-oauth-dcr-infer.test.ts' gave Test Files 1 passed (1), Tests 12 passed (12), VERDICT command-exit 0 (per-file line 914 ms of tests; module import 15.4 s). Cells: card body 201 native; http://127.0.0.1 201 native; cursor:// 201 native; https://evil.example/cb alone 201 web; http://evil.example/cb 400 invalid_redirect_uri with no client stored; http://localhost.evil.example 400 with no client stored; explicit web plus loopback 400; client_secret_basic plus loopback 201 native with a client_secret; auth method omitted plus loopback 201 native, client_secret_basic, with a secret; client_secret_basic plus cursor:// 201 native with a secret; loopback plus https://evil.example/cb 201 native; DCR off (OS_OIDC_DCR_ENABLED=false) 403 access_denied with no client stored. For each of the 8 registered clients, authorize without PKCE redirects to the client's own redirect with error=invalid_request, a description naming PKCE and no code, while the identical request with S256 PKCE reaches /oauth/consent. (2) Ablation, one-time and not committed: commit first, then os-verify-lock.sh -- node scripts/ablation-replace.mjs --file ABS/auth-manager.ts --anchor \"clientRegistrationDefaultApplicationType: 'infer',\" --delete -- pnpm --filter @objectstack/plugin-auth exec vitest run --maxWorkers=2 src/auth-manager.mcp-oauth-dcr-infer.test.ts, under a bash trap 'git checkout HEAD -- ABS' EXIT INT TERM. On-disk proof: anchor 1 -> 0, blob 9c00cadb -> a4a53154. Result Tests 7 failed | 5 passed (12), VERDICT command-exit 1, the expected direction. Red: all 7 native cells, the card body and the custom scheme among them, each answering 400 invalid_redirect_uri 'web clients require https redirect URIs on non-loopback hosts', the production symptom. Green: the 5 option-independent cells. Restore proven: blob == HEAD 9c00cadb, git diff HEAD empty, porcelain empty. The subject resolves through the relative import ./auth-manager (src), so no dist rebuild was needed for the mutation to reach the run. (3) pnpm --filter '@objectstack/plugin-auth^...' build (closure, workspace-concurrency 2): VERDICT command-exit 0. (4) pnpm --filter @objectstack/plugin-auth build: VERDICT command-exit 0; check-dts-emitted 3/3; dist/.d.ts has 0 hits for clientRegistrationDefaultApplicationType while dist/index.mjs has 1 (positive control), so no published type widened. (5) pnpm --filter @objectstack/plugin-auth typecheck: VERDICT command-exit 0; check:test-typecheck OK, 10 files / 88 errors / 23 signatures held, no growth (tsconfig.test.json includes src/**/). (6) pnpm --filter @objectstack/plugin-auth test: Test Files 182 passed (182), Tests 3450 passed | 10 skipped (3460), VERDICT command-exit 0. Head 6d4fd2a = 5a7e1f0 merged with origin/main 9cfc575 (no conflict; delta vs main is still these 3 files, +405/-0). The incoming commits touch cloud-connection, runtime, service-automation, verify, docs and an ADR, none in plugin-auth's dependency closure, so readings (1)-(6) stand under AGENTS §10's scoped re-check, and only the gate union was re-run on 6d4fd2a.",
    "gates": "Local, at HEAD 6d4fd2a: node scripts/pm/dispatch-gates.mjs --commands --repo objectstack-ai/objectstack derived 70 commands (merge base 9cfc575, 3 paths, no stale-tree warning). All 70 ran with exit codes captured before any pipe, every one exit 0. --ran reconciliation exit 0: 'Run reconciliation — 70 derived, 70 run, 0 NOT-MEASURED, 0 UNRUN'. A scan of the 70 logs for PREREQUISITE NOT MET / nothing was measured / queue-timeout found only check:objectui-changeset's own self-test prose. Run 1 at 5a7e1f0: 69 exit 0, plus check:dual-build-cjs-loads exit 3 PREREQUISITE NOT MET (41 packages without dist). On run 2 that gate measured (112 require entry points across 69 packages load) because run 1's check:type-check-debt closure build had built every package. Commands: 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-changeset-no-major.mjs --self-test :: exit 0; node scripts/check-ci-filter-parity.mjs :: 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-dev-prereqs.mjs --self-test :: 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-keyed-text-bounds.mjs :: exit 0; node scripts/check-keyed-text-bounds.mjs --self-test :: exit 0; node scripts/check-platform-object-tenancy-census.mjs :: exit 0; node scripts/check-platform-object-tenancy-census.mjs --self-test :: exit 0; node scripts/check-plugin-teardown-shape.mjs :: exit 0; node scripts/check-plugin-teardown-shape.mjs --self-test :: exit 0; node scripts/check-position-name-fold-loaders.mjs :: exit 0; node scripts/check-position-name-fold-loaders.mjs --self-test :: exit 0; node scripts/check-registry-log-declared.mjs :: exit 0; node scripts/check-registry-log-declared.mjs --self-test :: exit 0; node scripts/check-rest-log-spy-declared.mjs :: exit 0; node scripts/check-rest-log-spy-declared.mjs --self-test :: exit 0; node scripts/check-system-context-census.mjs :: exit 0; node scripts/check-system-context-census.mjs --self-test :: exit 0; node scripts/check-tenant-audit-census.mjs :: exit 0; node scripts/check-tenant-audit-census.mjs --self-test :: exit 0; node scripts/check-undeclared-dep-imports.mjs :: exit 0; node scripts/check-undeclared-dep-imports.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:auth-mount-ledger :: exit 0; pnpm check:changeset-gate-self-tests :: 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:durability-log-level :: exit 0; pnpm check:engine-double-contract :: exit 0; pnpm check:gitlink-declared :: exit 0; pnpm check:issue-citations :: 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:objectui-changeset :: exit 0; pnpm check:org-identifier :: exit 0; pnpm check:page-declaration-shape :: exit 0; pnpm check:pm-changeset-deadline-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:slot-lookup :: exit 0; pnpm check:sourcemap-no-sources-content :: 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:watch-hint-literal :: exit 0; pnpm check:where-matcher :: exit 0. CI on PR #25156, single snapshot at open (no polling): 38 check-runs. Completed success: filter, No other open PR may claim the same issue, Check Documentation Links, Part-of PR must not also close its card, No other open PR may claim the same single-writer path, The card this PR closes must claim this branch, Check PR Size. In progress: Test Core (1/6)-(6/6), Build Core, Governed Surface Queue Guard, Type Check (source gates, workspace, consumer gates, debt ledger), the Lint lanes (ESLint, PM dispatch-gates, repo & tooling gates, CI release & engine gates, docs & code guards, scoped lanes), Check Changeset, Dogfood Regression Gate, Auto Label. The rest skipped by their own if: (Dogfood Verify CLI, Console Pin Gate, Build Docs, Temporal Conformance, 18.x upgrade harness, Packed-tarball smoke, the dogfood matrix template). CI convergence is the PM's to read.",
    "dogfood_battery": "n/a — no ! in the PR title and no (narrowing) arm (Clause-②: no)",
    "mcp_calls": "0 — no MCP GitHub tool was called. Reads went through gh api (REST GET issues/25118, issues/25118/comments, pulls/25156, commits/6d4fd2a5a.../check-runs).",
    "api_writes": "3 REST writes in 2 relay strokes, each stroke one POST /repos/objectstack-ai/objectstack/dispatches executed by the fleet-write run as objectstack-fleet[bot]. Stroke 1 (scripts/pm/fleet-write/dispatch.mjs, run 39709534127, success): pr_create = POST /repos/objectstack-ai/objectstack/pulls (draft) + POST /repos//issues/25156/assignees [os-bill]; read-back 8734 bytes sent, 8734 stored, identical. Stroke 2 (scripts/pm/post-stamped.mjs): POST /repos//issues/25118/comments, this os-dev-report. No label write and no card assignee write. Not REST: 3 git pushes to claude/issue-25118-dcr-infer-native (abc25a3 -> 459afea fast-forward as the write-route probe, 5a7e1f0 the implementation, 6d4fd2a the merge of origin/main).",
    "line_budget": "n/a — no skills/** file and no ratcheted ledger touched; diff +405/-0 over 3 files, under the 3000-line human-merge threshold",
    "files_changed": [
    "packages/plugins/plugin-auth/src/auth-manager.ts (+41: the one option line and its comment, inside the oauthProvider({ … }) options only)",
    "packages/plugins/plugin-auth/src/auth-manager.mcp-oauth-dcr-infer.test.ts (new, +345)",
    ".changeset/25118-dcr-infer-native.md (new, +19, patch for @objectstack/plugin-auth)"
    ],
    "deviations": [
    "Ablation attempts 1 and 2 never acquired the shared lock (VERDICT queue-timeout, exit 99, behind a dogfood test run and then a sibling build) and are NOT MEASURED. In attempt 1 the mutation was held on disk while queued (WRAP nesting outermost); its restore was proven (blob == HEAD, git diff HEAD empty). Attempt 2 and the measured attempt 3 nested the mutation inside the lock, so it existed only while the lock was held. No gate or build ran while any mutation was on disk.",
    "Package readings (the new file, the ablation, plugin-auth build/typecheck/test) were taken at 5a7e1f0 and not re-run at the merge head 6d4fd2a. The merge brought nothing in plugin-auth's dependency closure (pnpm --filter '@objectstack/plugin-auth^...' ls against git diff --name-only 459afea1f origin/main), so under AGENTS §10's scoped re-check only the 70-command gate union was re-run on 6d4fd2a. CI re-runs everything on the head.",
    "test:repo (the vitest repo project) was not run locally: the diff touches no repo-project test and the derived list names none. CI Test Core runs it.",
    "The browser walk carried by ruling item 5 (claude mcp add --transport http NAME https://ENV-HOST/api/v1/mcp, then /mcp, then Authenticate) is NOT MEASURED: it needs a deployed environment after the cloud pin moves. The PR body records it as owed.",
    "No served-stack (pnpm dev) reading was taken. The readings come from the provider's real handler built from AuthManager's actual options; plugin-auth has no layer in front of /oauth2/register (code read: no before-hook branch, no raw route).",
    "Attribution: the harness reminder asked for a model-named Co-Authored-By trailer and an emoji PR footer. Following AGENTS.md, which outranks it here (and the pre-push hook refuses a model id), the commit carries the model-free pair 'Claude-Session: https://claude.ai/code/session_01UnHcTADjeg3VwxF21QHj7W' / 'Co-authored-by: Claude' and the PR body ends with the session-URL footer. The merge commit 6d4fd2a carries git's default merge message with no trailer.",
    "The dispatch named base origin/main 9cbd4c3a7; the PM corrected this mid-run to 459afea, and the work used the current origin/main (459afea, then 9cfc575)."
    ],
    "open_questions": [],
    "out_of_scope_findings": [
    "carrier: none · noted, not filed (PR #25156 Acceptance notes only): the comment above rawApp.post(.../sys-oauth-application/register) in packages/plugins/plugin-auth/src/auth-plugin.ts still says DCR on /oauth2/register is something 'we don't want to enable broadly', while resolveDcrEnabled turns DCR on whenever the MCP server is on. Stale comment, no behaviour, outside this card's file surface.",
    "carrier: none · noted, not filed (PR #25156 Acceptance notes only): the describe titles in packages/plugins/plugin-auth/src/auth-manager.mcp-oauth-resource.test.ts say 'installed 1.7.2' while the installed provider is 1.7.12. Naming only."
    ]
    }


    Generated by Claude Code

  7. objectstack-fleet commented on Oct 22, 2026

    @objectstack-fleet
    Contributor

    ACCEPT → PR #25156 comment 6331289535 · domain:services seat 2 · session_01UnHcTADjeg3VwxF21QHj7W · 2026-10-22T02:35Z · ruling A1 (6329352251) item by item · ready and auto-merge set through the relay; the landing note follows the merge. The deployed browser walk (ruling item 5) stays owed after the cloud pin moves.


    Generated by Claude Code

  8. objectstack-fleet commented on Oct 22, 2026

    @objectstack-fleet
    Contributor

    Landed · domain:services seat 2 · session_01UnHcTADjeg3VwxF21QHj7W · 2026-10-22T03:02Z

    Reading Value
    PR #25156 merged 2026-10-22T03:01Z through the merge queue, merge commit 6b44efa7b4
    origin/main head 6b44efa7b, subject "fix(plugin-auth): register an MCP client that omits application_type with a loopback or custom-scheme redirect as native"
    This card closed completed by Fixes. The only other card closed in that window, #24916, closed by its own PR (domain:cli)

    State: pm:dispatched is removed in this act. Ruling A1 (6329352251) is carried out. Still owed by the ruling's item 5: the browser walk (claude mcp add → /mcp → Authenticate) on a deployed environment after the cloud pin moves. That makes the Setup → Integrations → Connect an Agent page true.


    Generated by Claude Code

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

Metadata

Metadata

Assignees

Labels

area:identityLogin and identity — sign-up, sessions, organization membership, SSOauthbugSomething isn't workingdomain:servicesmcppriority:p1High: required for production / M2

Type

No type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions