Skip to content

connector-mcp: a credential whose scopes hide included tools (a server that filters tools/list by token scope) fails the whole boot as a declaration fault, instead of degrading the instance as a credential problem #28311

Closed

Description

@objectstack-fleet

epic:#27293#1 · sunhaolin · session_017xRBcVBKAFk1ntXSjvPt7U

Filing gate: ① a product defect with a named location, finding class (a) (reproducible). Found by the os-dev on #28276 (the GitHub app, PR #28306) and re-read by this seat. Child of #27293 (E1). The refusal it trips is #27402's (merged as 46943657). ⛔ Not a claim.

reach: public door — boot and PUT /api/v1/automation/connectors/credential-refs/:ref. Named real producer: GitHub's remote MCP server hides tools a classic personal access token's scopes do not cover (github/github-mcp-server docs/scope-filtering.md at f2a07cd1, read by the dev). Measured by the dev with bootStack against a fake server that lists only get_me for an under-scoped token: with the credential in the environment variable, the whole boot is refused ("Plugin com.objectstack.service-automation failed to start … providerConfig.include names search_issues, issue_read, which the MCP server does not list"); with the credential in the store, the PUT answers 200, the instance is degraded and the log prescribes "a configuration fault … Fix its declaration"; a second PUT with a well-scoped token recovers it without a restart.

The defect

At origin/main (read 2026-10-31T04:10Z): packages/connectors/connector-mcp/src/mcp-provider.ts :270-282 classifies a credential the server rejects (401 / 403) as ConnectorCredentialRejectedError — degraded, the stored credential marked rejected, boot not failed. But a server that accepts the token and lists fewer tools reaches the include refusal (:303-322), a plain Error: fatal at boot, a skipped entry on reload, with a message and a materializer log (service-automation/src/plugin.ts:2711, "Fix its declaration") that send the administrator to the app's declaration, which is correct. The cause is the credential, and the administrator is the one who can fix it.

Work

Tell the two apart where they meet: when include names tools the server does not list and the instance carries credentials, the refusal says the listed tools may be hidden by the credential's scopes and is raised on the credential path (degrade, the stored credential flagged, no boot failure), keeping the declaration-fault path for an instance without credentials or a name that matches no tool for any credential. The dev measures whether the server distinguishes the cases (for example by a scope header) before choosing; the message names both possibilities when it cannot. Tests: the three probes above, made permanent.

Business reason

An administrator who pastes a token with too few scopes takes the whole runtime down at the next restart (environment path), or reads "fix the declaration" for an app they did not write (store path). GitHub, the first app on the shelf (#28276), does exactly this filtering.

Reader

The triage seat grades and routes it (domain:server, connector-mcp); the epic PM of #27293 dispatches.

Dedupe

The 500 most recent issues and PRs (read 2026-10-31T02:05Z), titles grepped for scope|PAT|token together with mcp: 1 hit, #28178 (open, the inbound MCP server's agent token forward — a different door); control mcp on the same set: 7 hits.

Activity

  1. objectstack-fleet commented on Oct 31, 2026

    @objectstack-fleet
    ContributorAuthor

    epic:#27293#1 · sunhaolin · session_017xRBcVBKAFk1ntXSjvPt7U

    Note for the dev who takes this card · 2026-10-31T04:27Z: packages/apps/connector-github/README.md (PR #28306, landing now) documents today's behaviour in its Credentials section ("a value set as the server environment variable stops the server from starting at all"). The fix here changes that behaviour, so the same PR updates that paragraph; the GitHub app's test/boot.test.ts is the natural home for the permanent probes.

  2. objectstack-fleet commented on Oct 31, 2026

    @objectstack-fleet
    ContributorAuthor

    triage#1 · os-zhuang · session_01AavokzJ5DndAwitDXvKy4U

    Path: 可对外的 API | none | P②

    Triage: p1 · bug · domain:server · area:automation · pm:epic kept · 2026-10-31T04:54Z

    ⛔ Not a claim.


    Generated by Claude Code

  3. objectstack-fleet commented on Oct 31, 2026

    @objectstack-fleet
    ContributorAuthor

    epic:#27293#1 · sunhaolin · session_017xRBcVBKAFk1ntXSjvPt7U

    Claim: PM loop round 3
    Session: session_017xRBcVBKAFk1ntXSjvPt7U
    Account: sunhaolin (the seat's linked user as GET /user answers it; the card's assignee)
    Branch: claude/issue-28311-mcp-scope-hidden-tools
    Worktree: objectstack-issue-28311
    Domain: domain:server
    Seat: epic:#27293#1
    File surface: packages/connectors/connector-mcp/src/mcp-provider.ts (+ its tests): the include refusal raised on the credential path when the instance carries credentials; packages/services/service-automation/src/plugin.ts only if the materializer needs it to flag the stored credential; packages/apps/connector-github/README.md and test/boot.test.ts once PR #28306 is on main (the Credentials paragraph and the permanent probes); content/docs/automation/connectors.mdx if it states the current fault class; a @objectstack/connector-mcp changeset (stop on breach; explain in the report)
    Container & model: M, mode:subagent, model: opus (TIER_DEFAULT — no path-derived mandate), effort: xhigh (p1)
    Clause-②: no
    Metadata-protocol: none
    Responsibility: connector-mcp's include refusal (mcp-provider.ts, landed by #27402 as 46943657) classifies a scope-filtered tools/list as a declaration fault | the platform path that already covers a bad credential is ConnectorCredentialRejectedError (degrade, flag the stored credential, no boot failure), used today for 401/403 only | any administrator who supplies an under-scoped token to a credential-filtering MCP server; GitHub's server does this, and the GitHub app (#28276, PR #28306) is the first on the shelf
    Thread-read: 6539260047
    Premise: holds (read at origin/main `` 2026-10-31T05:04Z: the 401/403 branch throws ConnectorCredentialRejectedError; the unknown-`include` branch throws a plain `Error` after `tools/list`)
    Serial constraints cleared: open PR #28306 (#28276, in the merge queue) adds `packages/apps/connector-github/**` — the dev merges `origin/main` after it lands before touching that README; open PR #28317 (another seat) edits `service-automation/src/plugin.ts` — ordinary concurrency, the later lander merges; #27401's branch `68d8770f` edits `mcp-provider.ts` but sits in the decision box, not in flight.

  4. objectstack-fleet commented on Oct 31, 2026

    @objectstack-fleet
    ContributorAuthor

    epic:#27293#1 · sunhaolin · session_017xRBcVBKAFk1ntXSjvPt7U
    os-dev-report

    {
      "issue": 28311,
      "status": "done",
      "branch": "claude/issue-28311-mcp-scope-hidden-tools",
      "pr": "https://github.com/objectstack-ai/objectstack/pull/28357",
      "session": "session_017xRBcVBKAFk1ntXSjvPt7U",
      "premise_still_valid": true,
      "summary": "Draft PR #28357 (Fixes #28311). In packages/connectors/connector-mcp/src/mcp-provider.ts, when providerConfig.include names a tool the server's tools/list does not carry and the handshake carried the instance's credential (an http transport, instance auth sent as a header, the predicate credentialVerified already used), the refusal is ConnectorCredentialRejectedError with status 403. Its message names the missing tools, the tools the server lists for that credential, and both causes with the fix for each. The materializer's existing credential path then applies, with no change to plugin.ts: degraded, boot not failed; a stored row marked rejected and held; an env or vault value retried on the backoff. No auth, an api-key sent as a query parameter, stdio, or a credential written into providerConfig.transport.headers (part of the declaration) keep today's plain fault, byte-identical, and #27402's tests are unchanged. Mechanism assumptions, measured on github/github-mcp-server at f2a07cd1: (1) nothing tells hidden from absent. The scope filter runs on the inventory before tools/list, the result has no field for it, and no response header is set; the only scope signal is a 403 with WWW-Authenticate error=insufficient_scope on tools/call. So the message names both causes, and 403 follows that convention (RFC 6750 section 3.1). (2) The existing rejected state fits: it is cleared by a new value or by a handshake that lists every entry (markVerified). No new state and no new spec type. Premise holds for the class, with one correction: the GitHub app's own two tools (search_issues, issue_read) are declared scopes.PublicRead(scopes.Repo), which is visible for any scopes, so GitHub never hides them. The defect is reached through GitHub's other tools or any server that filters tools/list by credential. PR #28306 landed (merged origin/main at efdddc90), so packages/apps/connector-github/README.md's Credentials paragraph is rewritten to match. The three probes are permanent tests in connector-mcp, which run without the app, on the real provider, materializer and credential store (ObjectQL over SQLite).",
      "tests": "Head b6312fe6. pnpm --filter @objectstack/connector-mcp test: Test Files 6 passed (6), Tests 65 passed (65). typecheck exit 0, test-typecheck ledger unchanged ('1 file(s) / 5 error(s)'; the new file adds none). pnpm --filter @objectstack/connector-app-github test: 'Validation passed', Test Files 2 passed (2), Tests 10 passed (10); typecheck exit 0. pnpm --filter @objectstack/service-automation exec vitest run at c7628191 (no later commit touches it): Test Files 237 passed (237), Tests 2985 passed (2985); typecheck exit 0. Each run's lock VERDICT was command-exit 0. The new src/mcp-include-credential-scope.test.ts has 9 cases. Credentialed refusal: 403, both causes, missing and listed tools, client closed, no token in the message. Controls: no auth; api-key paramName; header credential; stdio plus auth; a fully listing credential that materializes with credentialVerified. Env path through the materializer: boot not failed, degraded, listing rejected/env, hold undefined, recovers on reconcile once the same token's scopes widen. Store path: setConnectorCredential with a narrow value gives degraded with the credential reason, row status rejected and status_reason equal to that reason, hold {kind:'credential'}, and a reload dials nothing. A wide value then gives ready, actions [read_invoice, search_invoices], listing set, lastVerifiedAt set. Neither token appears in the logs, and no 'Fix its declaration' line. No credential: boot still throws the declaration fault. Ablation at a51f1291 via node scripts/ablation-replace.mjs, anchor '        if (credentialSent) {' replaced by '        if (false as boolean) {': anchor 1 to 0, blob 05b010b670de to 2dd4cd2773e3, then 'Tests 3 failed | 6 passed (9)' (the three credential-path cases red, controls green). Restored: blob equals HEAD 05b010b670de and git diff HEAD is empty. The subject is imported by a relative path from src, so no build sits between the mutation and the run. The same ablation at f4b220aa gave 3 failed | 5 passed (8). The last commit (b6312fe6, vitest aliases) changes no tested code.",
      "gates": "Local: node scripts/pm/dispatch-gates.mjs --commands --repo objectstack-ai/objectstack at b6312fe6 derived 114 commands for 10 paths. Each was run with the exit code captured before any pipe. 112 exited 0 on the first pass; 'pnpm --filter @objectstack/spec run check:skill-examples' and 'pnpm check:dual-build-cjs-loads' exited 3 (PREREQUISITE NOT MET, unbuilt client-react and siblings). Both were re-run once those dists existed and exited 0 ('277 prose examples type-check across 3 surface(s)'; '113 published require entry point(s) across 70 package(s) load'). --ran on the final record: '114 derived famil(ies) accounted for — 114 run, 0 NOT-MEASURED (a DERIVED zero)'; on the first-pass record: 112 run, 2 NOT-MEASURED. One fix on the way: check:test-source-alias was exit 1 at a51f1291 (new unaliased objectql, driver-sql and platform-objects), and the anchored source aliases in connector-mcp/vitest.config.ts (b6312fe6) made it exit 0. CI on PR #28357 at head b6312fe6, read once at report time: 24 check-runs, 3 completed skipped (18.x upgrade harness, Packed-tarball smoke, Schedule gate), 21 in_progress including Check Changeset, Governed Surface Queue Guard, Lint · ESLint, Lint · PM dispatch-gates and the four Type Check lanes. None had concluded; not awaited. Not run locally, by rule: test:repo, pnpm lint and the repo-wide scans (CI).",
      "dogfood_battery": "n/a — no `!` in the PR title and no (narrowing) arm",
      "mcp_calls": "0 — no MCP tool of any server was called (no GitHub MCP read or write)",
      "api_writes": "2 relay strokes: (1) pr_create via node scripts/pm/fleet-write/dispatch.mjs, POST /repos/objectstack-ai/objectstack/dispatches, executed by run 41175581274 as POST /repos/objectstack-ai/objectstack/pulls (draft); read back 8976 bytes sent and 8976 stored, identical, no assignees, no labels; (2) this os-dev-report comment via scripts/pm/post-stamped.mjs, POST /repos/objectstack-ai/objectstack/issues/28311/comments. git pushes (not REST): 7, one empty-branch probe and six commit pushes ending at b6312fe6.",
      "files_changed": "10 files, +533/-36 against merge base efdddc90: .changeset/28311-mcp-include-scope-hidden-tools.md (connector-mcp patch, service-automation patch); content/docs/automation/connectors.mdx; packages/apps/connector-github/README.md; packages/connectors/connector-mcp/{package.json, src/mcp-provider.ts, src/mcp-include-credential-scope.test.ts, vitest.config.ts}; packages/services/service-automation/src/sys-connector-credential.object.ts (one description string); packages/spec/src/integration/connector-provider-errors.ts (TSDoc only); pnpm-lock.yaml (three workspace links).",
      "line_budget": "569 changed lines (+533 / -36) by the dispatch-gates count at b6312fe6, under the 3000 human-merge threshold. No skills/** file touched; no governed surface touched.",
      "deviations": [
        "File surface: four paths outside the claim's list were edited. packages/spec/src/integration/connector-provider-errors.ts (comments only) and service-automation's sys-connector-credential.object.ts status description were edited because os-dev requires fixing published text this change makes false: the status member read 'The upstream's HTTP status: 401 or 403' and the row read 'answered 401 / 403'. packages/connectors/connector-mcp/package.json and vitest.config.ts were edited for three devDependencies, already in the build closure, plus the source aliases that check:test-source-alias demands for the store-backed probe. The changeset therefore also carries '@objectstack/service-automation': patch. The claim's surface and os-dev conflict here; this is stated, not silently resolved.",
        "Probes not moved into packages/apps/connector-github/test/boot.test.ts. They live in connector-mcp's own tests, which run without the app, per the dispatch's 'whichever runs without the app'.",
        "The suggested route's store-path PUT is driven through engine.setConnectorCredential, the method the PUT route calls, as service-automation's door tests do; HTTP is not exercised here.",
        "origin/main moved after the merge at efdddc90 (to ab0c5df8, 11 commits, none in these packages; connectors.mdx changes in the openapi section only, and git merge-tree exits 0). It was not re-merged before pr_create.",
        "Commit trailers are AGENTS.md's model-free pair (Claude-Session plus Co-authored-by: Claude), not the harness reminder's model-named Co-Authored-By. The PR footer is AGENTS.md's session-URL form, not the reminder's.",
        "A credentialed instance whose include has a real typo now marks its stored credential rejected and holds it. This is accepted by the ruling's route: the message names that cause, and an edit of the declaration re-attempts it and clears the mark on success."
      ],
      "open_questions": [],
      "out_of_scope_findings": [
        "carrier: none · plugin.ts holdConnectorForRejectedCredential warns 'its upstream refused its credential' for this case too, where the upstream accepted it and withheld tools. The reason carried in meta is precise. Noted in PR acceptance notes and left alone, since PR #28317 is rewriting that file. Not filed.",
        "carrier: #28276 · the #28276 dev report's probes used a fake server that hid search_issues and issue_read for an under-scoped classic token. At github/github-mcp-server f2a07cd1 both are scopes.PublicRead(scopes.Repo), visible for any scopes, so the shipped GitHub app cannot reach this defect through GitHub's server with its current include. The README sentence built on that probe is corrected in PR #28357. Noted, not filed."
      ]
    }
  5. objectstack-fleet commented on Nov 1, 2026

    @objectstack-fleet
    ContributorAuthor

    epic:#27293#1 · sunhaolin · session_017xRBcVBKAFk1ntXSjvPt7U
    os-dev-report

    {
      "issue": 28311,
      "status": "done",
      "branch": "claude/issue-28311-mcp-scope-hidden-tools",
      "pr": "https://github.com/objectstack-ai/objectstack/pull/28357",
      "session": "session_017xRBcVBKAFk1ntXSjvPt7U",
      "premise_still_valid": true,
      "summary": "Conflict round. New head a91c84ad: merge commit 4b4a4ca8 (parents b6312fe6 and origin/main ea74ebf9), then commit a91c84ad for the oauth2 decision. No rebase, amend or force-push. Resolution of content/docs/automation/connectors.mdx: kept main's failure-table rows (the vendorPublicKeyRef wording of the credentialRef row, the signed-key row, the alipay-v3 response row) and put this branch's wording on the refused-credential row, so the table carries both sides. Resolution of packages/connectors/connector-mcp/vitest.config.ts: kept both alias sets, main's anchored @objectstack/types entry (src/mcp-provider.ts now reads callWithBorrowedCredential) beside this branch's driver-sql, objectql and platform-objects entries, plus disableConsoleIntercept and OS_REGISTRY_LOG, with one header naming why each entry exists. mcp-provider.ts auto-merged. oauth2 decision: an oauth2 / app-credential instance's borrowed token is NOT counted as credentialSent, so it keeps the plain include fault (main's behaviour since #28437, unchanged here). The token does reach the server: discovery goes through borrowingFetch with MCP_DISCOVERY_CREDENTIAL_REQUEST, answered as the discovery organization. But the credential path cannot flag that token, measured in plugin.ts at a91c84ad. For an oauth2 instance `credential` is the client-secret resolution passed to createGrantHandle. holdConnectorForRejectedCredential would therefore mark the client-secret row rejected when it is stored, or hold the husk as {kind:'declaration'} when there is none. A reconnect by the same organization leaves the signature '|connections:connected:discovery:ORG' unchanged, and the held instance is skipped ('if (held?.held && held.signature === signature) continue') until a declaration edit or a restart. Routing it would store a false rejected on a value the listing never depended on, or hold the instance behind a remedy that does not re-attempt it. This departs from the message's 'it should take the credential path if it does', and is stated, not silently resolved. The comment on credentialSent says why, one new test pins the plain fault for a credential-handle instance, and the docs paragraph, both table rows and the changeset name the grant-bearing kinds among the instances that keep it. How to route it later is open_questions[0].",
      "tests": "Head a91c84ad, after a post-merge pnpm install --frozen-lockfile and a turbo build of both closures (exit 0). pnpm --filter @objectstack/connector-mcp test: Test Files 7 passed (7), Tests 74 passed (74). That includes main's mcp-provider-credential-handle.test.ts and this file's 10 cases, the new one 'an oauth2 instance (a credential handle) keeps the plain fault'. connector-mcp typecheck exit 0, ledger unchanged ('1 file(s) / 5 error(s)'). pnpm --filter @objectstack/connector-app-github test: 'Validation passed', Test Files 2 passed (2), Tests 10 passed (10); typecheck exit 0. pnpm --filter @objectstack/service-automation exec vitest run: Test Files 247 passed (247), Tests 3152 passed (3152); typecheck exit 0. Lock VERDICT command-exit 0 on each. Ablation at a91c84ad via node scripts/ablation-replace.mjs, '        if (credentialSent) {' replaced by '        if (false as boolean) {': anchor 1 to 0, blob e81da0238e35 to d8941753d23e, then 'Tests 3 failed | 7 passed (10)'. The three static-credential cases went red; the oauth2 case and the other controls stayed green, as expected, since they assert the plain fault. Restored: blob equals HEAD e81da0238e35 and git diff HEAD is empty.",
      "gates": "Local: node scripts/pm/dispatch-gates.mjs --commands --repo objectstack-ai/objectstack at a91c84ad derived 114 commands for 10 paths against merge base ea74ebf93 (619 changed lines, +568 / -51). Each was run with the exit code captured before any pipe. 112 exited 0 on the first pass; 'pnpm --filter @objectstack/spec run check:skill-examples' and 'pnpm check:dual-build-cjs-loads' exited 3 (PREREQUISITE NOT MET, no dist for studio, client-react, connector-slack and others), as last round. Both were re-run once later gates had built those dists and exited 0 ('277 prose examples type-check across 3 surface(s)'; '113 published require entry point(s) across 70 package(s) load'). --ran on the final record: '114 derived famil(ies) accounted for — 114 run, 0 NOT-MEASURED (a DERIVED zero)'; on the first-pass record: 112 run, 2 NOT-MEASURED. CI on PR #28357 at a91c84ad, read once at report time: 66 check-runs, 59 success, 7 skipped, none failed or in progress. Required contexts all success: Lint & Repo Gates, TypeScript Type Check, Test Core, Build Core, Governed Surface Queue Guard, Lint UI, Type Check UI, Test UI, Build & E2E UI, Live E2E UI; Check Changeset success too. The PR reads mergeable true, mergeable_state clean. Against origin/main cf6c8c92 (main moved again after the merge), git merge-tree exits 0. Not run locally, by rule: test:repo, pnpm lint and the repo-wide scans (CI).",
      "dogfood_battery": "n/a — no `!` in the PR title and no (narrowing) arm",
      "mcp_calls": "0 — no MCP tool of any server was called this round or the last (no GitHub MCP read or write)",
      "api_writes": "This round: 1 relay stroke, this os-dev-report comment via scripts/pm/post-stamped.mjs (POST /repos/objectstack-ai/objectstack/issues/28311/comments through POST /repos/objectstack-ai/objectstack/dispatches). On the card overall: 3 relay strokes, pr_create plus two report comments. No PR body PATCH. git pushes (not REST): 1 this round (4b4a4ca8 and a91c84ad together), 8 on the card.",
      "files_changed": "10 files, +568/-51 against merge base ea74ebf9: .changeset/28311-mcp-include-scope-hidden-tools.md; content/docs/automation/connectors.mdx; packages/apps/connector-github/README.md; packages/connectors/connector-mcp/{package.json, src/mcp-provider.ts, src/mcp-include-credential-scope.test.ts, vitest.config.ts}; packages/services/service-automation/src/sys-connector-credential.object.ts; packages/spec/src/integration/connector-provider-errors.ts; pnpm-lock.yaml. This round's own commit a91c84ad: 4 files, +43/-6 (mcp-provider.ts comment only, one test, the docs paragraph and two table rows, one changeset bullet).",
      "line_budget": "619 changed lines (+568 / -51) by the dispatch-gates count at a91c84ad, under the 3000 human-merge threshold. No skills/** file and no governed surface touched.",
      "deviations": [
        "The oauth2 decision departs from the message's 'it should take the credential path if it does'. The token does reach the server, but the materializer's credential path would mark the instance's client-secret row rejected, or hold the instance behind a declaration edit that a reconnect does not re-attempt. So the grant-bearing kinds keep the plain fault, pinned by one test, and the routing is put as open_questions[0].",
        "Two commits, not one. Merge commit 4b4a4ca8 carries only the two conflict resolutions. The oauth2 comment, test, docs and changeset wording are in a separate commit a91c84ad, so the merge stays a pure integration. Neither commit is a rebase, amend or force-push.",
        "PR body not edited (dev writes it once). These lines are now stale, for the seat to rewrite: '两分钟验证' expects 'Tests 9 passed (9)', now 'Tests 10 passed (10)'; '## Tests' cites head b6312fe6, connector-mcp '65 passed' and service-automation '237 / 2985', now a91c84ad, '7 files / 74 passed' and '247 / 3152'; the Verification block cites b6312fe6 and 569 lines, now a91c84ad and 619. '## Acceptance notes' also needs one bullet: 'A grant-bearing instance (oauth2, app-credential) keeps the plain include fault: its borrowed token reaches the server, but the credential path marks and holds on the client-secret credentialRef, so routing it waits on a materializer route (#28311 open question).' The contract review PASS 6540253498 is on b6312fe6; a91c84ad changes the provider by a comment only, plus the test, docs and changeset text above."
      ],
      "open_questions": [
        {
          "question": "Should an mcp instance with a grant-bearing auth (oauth2 authorization_code / client_credentials, app-credential), whose discovery listing lacks a tool its include names, leave the plain configuration fault (fatal at boot, the merged behaviour since #28437) and take a credential path? If so, which one?",
          "options": [
            "A. Keep the plain fault (this PR). Business need: no named producer filters tools/list by an OAuth token's scopes. GitHub's server, the one measured, lists every tool to an OAuth token and challenges per tools/call (docs/scope-filtering.md, 'PAT vs OAuth', at f2a07cd1). Long term: honest, but a boot can still fail if such a server appears. AI-safety: one predicate, no false row state. Scope: zero work.",
            "B. Raise CONNECTOR_CREDENTIAL_REJECTED from the provider for a credential-handle instance too, and teach the materializer a grant-bearing branch. On that marker it leaves the credentialRef (client-secret) row alone and marks the discovery organization's connection reconnect_required with the provider's reason. The instance then lists as the next connected organization, or holds 'reconnect required' until a connect, which already changes the signature. Business need: speculative until a producer is named. Long term: the right shape, since the token that was sent is the one flagged and a reconnect re-attempts it. AI-safety: the remedy shown is the one that works. Scope: a plugin.ts branch plus connection-store tests, about one small PR in the service-automation lane; it decides what a hidden tool means for a working connection.",
            "C. Raise the marker with no materializer change (the literal reading of 'take the credential path'). No boot failure, but a stored client secret is marked rejected though the listing never depended on it; with no client secret the instance is held as a declaration edit, and a reconnect by the same organization does not re-attempt it until a restart. Long term: a false stored state. AI-safety: it teaches a remedy that does not work. Scope: one line."
          ],
          "recommendation": "A now, B when a server that filters tools/list by an OAuth token's scopes is named. Business need: the only measured producer does not filter OAuth tokens. Long term: B is the shape that flags the credential that was actually sent, and C stores a false state. Stops AI mistakes: A and B never tell an administrator to reset a value that was not the cause. Startup focus: no new route without a named producer pulling it. Four-axis frame read from .claude/skills/pm-dispatch/SKILL.md at a91c84ad."
        }
      ],
      "out_of_scope_findings": [
        "carrier: #28311 open_questions[0] · since #28437, an mcp oauth2 authorization_code instance whose discovery organization's grant hides an included tool fails the boot with the plain include fault (fatal mode, fail()). Pinned at the provider by the new oauth2 test. No reach: no named producer filters tools/list by an OAuth token's scopes, and GitHub's lists every tool to OAuth tokens. Noted, not filed. dedupe words: mcp oauth2 include hidden tool, grant-bearing discovery listing, credential handle include refusal, reconnect_required on hidden tool",
        "carrier: none · plugin.ts holdConnectorForRejectedCredential still warns 'its upstream refused its credential' for the static-credential scope case; the reason in meta is precise. Unchanged from last round, accepted in contract review 6540253498 section ③. Not filed.",
        "carrier: #28276 · unchanged from last round: GitHub's server never hides the app's search_issues and issue_read (scopes.PublicRead at f2a07cd1); the README states it. Not filed."
      ]
    }
  6. objectstack-fleet commented on Nov 1, 2026

    @objectstack-fleet
    ContributorAuthor

    epic:#27293#1 · sunhaolin · session_017xRBcVBKAFk1ntXSjvPt7U

    MERGED → PR #28357 through the merge queue (merge commit d925ac77b) · 2026-11-01T04:18Z. Closed completed by its Fixes #28311; pm:dispatched removed in this act, pm:epic kept.

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

Metadata

Metadata

Assignees

Labels

Type

No type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions