Repository navigation
fix(auth): register peer links with authorization_code only - #120
Merged
chrisohearn merged 1 commit intoNov 14, 2026
Merged
chrisohearn merged 1 commit into
chrisohearn merged 1 commit into
Conversation
Upstream T3 Code builds decode the MCP OAuth registration answer with grant_types limited to "authorization_code". Since refresh-token renewal landed, the register endpoint answered every client with ["authorization_code", "refresh_token"], so an upstream build linking to a fork environment failed to decode the answer and reported "answered in a shape this version does not understand". A registration that carries the peer-link software_id now gets ["authorization_code"] only. Other clients still get refresh_token. Peer links never renew, and the token endpoint does not check a client's registered grant types, so a link registered this way still completes its authorization_code exchange. The rest of the link flow (decision redirect, token answer, and the token errors that path can return) already decodes with upstream's contract. The regression test checks the peer-link answer against a local copy of upstream's AuthMcpRegisteredClient and runs the link sign-in through to a working token. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Codex Review Summary: ✅ CompletedThis comment shows the latest Codex review activity on this pull request.
ℹ️ About Codex in GitHubYour team has set up Codex to review pull requests in this repo. Reviews are triggered when you
Codex reacts with 👀 while any review is running, comments if it has suggestions, and reacts with 👍 once all reviews finish with no findings. |
chrisohearn
merged commit Nov 14, 2026
11a166e
into
integration/mcp-events-20261109
18 checks passed
This was referenced Nov 14, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
What broke
Upstream T3 Code builds cannot link to a fork server. Upstream decodes the MCP OAuth register answer with
AuthMcpRegisteredClient, whosegrant_typesonly accepts"authorization_code". Since refresh-token renewal landed on the fork,/oauth/mcp/registeranswered every client with["authorization_code", "refresh_token"]. Upstream's strict schema rejects that, so the link fails with "answered in a shape this version does not understand".The fix
apps/server/src/auth/mcpOAuthHttp.ts: a registration that carries the peer-linksoftware_idnow getsgrant_types: ["authorization_code"]only. Peer links never renew, and the token endpoint does not check a client's registered grant types, so a peer link registered this way still completes itsauthorization_codeexchange. The rest of the link flow (decision redirect, token answer, and the token errors that path can return) already decodes with upstream's contract.Refresh-token renewal for other MCP clients is unchanged. Every registration without the peer-link
software_idstill gets["authorization_code", "refresh_token"].Tests added
In
apps/server/src/auth/McpOAuth.test.ts:AuthMcpRegisteredClient, checks that a non-peer registration does not match that schema (it still listsrefresh_token), then runs the link sign-in through pairing-code approval and the token exchange to a token/mcpaccepts as a peer link.["authorization_code", "refresh_token"].Checks that ran
Node v26.13.1 (engines says 26.12.1); dependencies installed with
npx -y pnpm@12.14.0 install --frozen-lockfile, which exited 0.mcpOAuthHttp.tsreverted,vp test run apps/server/src/auth/McpOAuth.test.tsgave 1 failed, 11 passed. The new test failed withexpected [ 'authorization_code', …(1) ] to deeply equal [ 'authorization_code' ].vp test run apps/server/src/auth/McpOAuth.test.ts: 12 of 12 passed (and again after the lint fix below).vp test run apps/server/src/auth apps/server/src/peer: 17 files, 217 tests, all passed.packages/contracts,vp test run: 32 files, 176 tests, all passed (contracts unchanged).apps/server,vp run typecheckexited 0 with no errors (only the existingeffect(schemaClassMissingBrand)suggestions).vp fmton the 2 changed files: clean.vp lint --report-unused-disable-directiveson the 2 changed files: first run flaggedt3code(no-inline-schema-compile)for an inlineSchema.decodeUnknownEffect; the decoder andSchema.ismoved to module scope and the re-run was clean.vp run check:changed origin/integration/mcp-events-20261109exited 0: format, lint, knip 1/2 and 2/2, graphql (apps/server), typecheck (3 packages) and test (2 files, 14 tests) all passed. (Without a ref it pickedorigin/mainas the base, 43753 files, and crashed withspawn E2BIG.)vp fmt,vp lintandknip:check.🤖 Generated with Claude Code