Skip to content

Fix MCP recovery for invalid agent tokens#137

Merged
khaliqgant merged 4 commits into
mainfrom
codex/fix-mcp-invalid-agent-token
May 22, 2026
Merged

Fix MCP recovery for invalid agent tokens#137
khaliqgant merged 4 commits into
mainfrom
codex/fix-mcp-invalid-agent-token

Conversation

@khaliqgant

Copy link
Copy Markdown
Member

Summary

  • return a structured agent_token_invalid API error for invalid agent tokens
  • teach the TypeScript SDK to preserve that error code
  • clear stale MCP pre-registered tokens and surface a recovery message so agent.register can rotate/re-register under strict identity
  • document the recovery code in README and OpenAPI

Closes #136.

Tests

  • npm --workspace @relaycast/sdk test
  • npm --workspace @relaycast/server test -- --run src/middleware/__tests__/auth.test.ts
  • npm --workspace @relaycast/mcp test
  • npm --workspace @relaycast/sdk run build
  • npm --workspace @relaycast/server run build
  • npm --workspace @relaycast/mcp run build
  • npm --workspace @relaycast/sdk run lint
  • npm --workspace @relaycast/server run lint (existing warnings only)
  • npm --workspace @relaycast/mcp run lint

@coderabbitai

coderabbitai Bot commented May 22, 2026

Copy link
Copy Markdown

Review Change Stack

Warning

Rate limit exceeded

@khaliqgant has exceeded the limit for the number of commits that can be reviewed per hour. Please wait 51 minutes and 19 seconds before requesting another review.

You’ve run out of usage credits. Purchase more in the billing tab.

⌛ How to resolve this issue?

After the wait time has elapsed, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

We recommend that you space out your commits to avoid hitting the rate limit.

🚦 How do rate limits work?

CodeRabbit enforces hourly rate limits for each developer per organization.

Our paid plans have higher rate limits than the trial, open-source and free plans. In all cases, we re-allow further reviews after a brief timeout.

Please see our FAQ for further information.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro Plus

Run ID: dd4debe9-acee-4683-8887-6b8198ed2319

📥 Commits

Reviewing files that changed from the base of the PR and between 0798d12 and 3394d39.

📒 Files selected for processing (4)
  • README.md
  • openapi.yaml
  • packages/mcp/src/__tests__/integration.test.ts
  • packages/mcp/src/server.ts
📝 Walkthrough

Walkthrough

This PR implements structured error handling for expired or invalid pre-registered agent tokens. It adds the agent_token_invalid error code (HTTP 401) across the API, server middleware, SDKs, and MCP server logic, with token invalidation and safe recovery through re-registration.

Changes

Agent token invalidation and recovery

Layer / File(s) Summary
Error code definition and API documentation
README.md, openapi.yaml
agent_token_invalid is documented as the HTTP 401 error code for invalid/expired agent tokens, with recovery guidance to re-register or rotate agent identity.
Server-side invalid token error response
packages/server/src/middleware/auth.ts, packages/server/src/middleware/__tests__/auth.test.ts
INVALID_AGENT_TOKEN_ERROR constant standardizes the 401 JSON response; requireAuth and requireAgentToken both return this constant when agent tokens are invalid, verified by updated tests.
TypeScript SDK error normalization
packages/sdk-typescript/src/errors.ts, packages/sdk-typescript/src/__tests__/errors.test.ts
RelayErrorCode union adds 'agent_token_invalid'; RAW_CODE_MAP normalizes it; tests confirm it normalizes to itself and is non-retryable at 401.
MCP server token invalidation and recovery mechanism
packages/mcp/src/server.ts, packages/mcp/src/__tests__/integration.test.ts
Helper functions detect invalid-token conditions and generate recovery messages; invalidateActiveAgentToken() clears WS bridge, subscriptions, and cached session state; tools/call wrapper intercepts invalid tokens, invalidates state, and appends recovery guidance. Integration test simulates token expiration, verifies recovery through agent.register, and confirms new requests succeed with refreshed token.
Rust SDK Channel type flexibility
packages/sdk-rust/src/types.rs, packages/sdk-rust/tests/parity.rs
Channel.workspace_id and Channel.channel_type are now optional to handle responses where these fields may be absent; parity test validates deserialization of public channel payloads with optional fields.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Poem

🐰 When tokens expire mid-session's flight,
No panic, dear agent—recovery's in sight!
Register anew, clear the stale state,
Your MCP bridge whispers: try again, don't wait.

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly summarizes the main change: fixing MCP recovery when agent tokens become invalid during a session.
Description check ✅ Passed The description is directly related to the changeset, covering structured error returns, SDK updates, token clearing, and documentation.
Linked Issues check ✅ Passed All primary objectives from issue #136 are addressed: structured error code (agent_token_invalid), TypeScript SDK preservation, MCP token recovery mechanism, and documentation added.
Out of Scope Changes check ✅ Passed All changes directly support the stated objectives: error handling, SDK updates, recovery logic, tests, and documentation are all scope-aligned.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch codex/fix-mcp-invalid-agent-token

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🧹 Nitpick comments (2)
packages/mcp/src/server.ts (1)

477-480: ⚡ Quick win

Preserve original tool error details when adding recovery guidance.

This currently replaces result.content entirely, which drops upstream error context. Append the recovery text instead of overwriting.

♻️ Proposed patch
-          return {
-            ...result,
-            content: [{ type: 'text' as const, text: agentTokenRecoveryMessage() }],
-          };
+          return {
+            ...result,
+            content: [
+              ...(Array.isArray(result.content) ? result.content : []),
+              { type: 'text' as const, text: agentTokenRecoveryMessage() },
+            ],
+          };
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@packages/mcp/src/server.ts` around lines 477 - 480, The current return
replaces result.content with only the recovery message, losing upstream error
details; instead append the recovery guidance to the existing result.content
array (preserving its items and types) by concatenating or pushing the object
produced by agentTokenRecoveryMessage() as { type: 'text', text:
agentTokenRecoveryMessage() } so the returned object spreads ...result and
returns content: [...result.content, { type: 'text' as const, text:
agentTokenRecoveryMessage() }].
packages/mcp/src/__tests__/integration.test.ts (1)

196-231: ⚡ Quick win

Add a routed-workspace invalid-token regression case.

Please add a variant where the failed call is routed via workspace_id/workspace_alias and assert the active workspace token remains intact. That will lock in the intended multi-workspace recovery behavior.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@packages/mcp/src/__tests__/integration.test.ts` around lines 196 - 231, Add a
new test variant alongside "clears an invalid pre-registered token so strict
registration can recover" that repeats the failure/recovery flow but routes the
failed call via a workspace identifier (use callTool arguments like workspace_id
or workspace_alias when calling message.post) and then performs the same
agent.register recovery; use the same setup symbols (invalidAgentTokens,
createRelayMcpServer, staleClient, InMemoryTransport.createLinkedPair, captured)
and assert the register request still uses the primary apiKey (expect
registerReq!.headers.authorization toBe 'Bearer rk_live_int1') and additionally
assert the active workspace token for that workspace remains unchanged after
recovery (check whatever in-memory workspace token store or token map your test
suite exposes to confirm the workspace-specific token is intact).
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@packages/mcp/src/server.ts`:
- Around line 292-326: The invalidateActiveAgentToken function currently reads
and mutates session.workspaceKey and the workspace entry for the active
workspace, so calls that are meant to invalidate a token for a different
workspace can accidentally clear the active workspace; update
invalidateActiveAgentToken to accept an explicit workspace identifier (e.g.,
workspaceKey or workspaceAlias) in addition to asAgent, use that identifier to
look up and update the specific session.workspaces entry (and avoid touching
session.workspaceKey) and only stop session.wsBridge / clear
session.subscriptions / setSession global state when the invalidated workspace
matches the current session.workspaceKey; update callers that pass only asAgent
(places that route by workspace_id/workspace_alias) to supply the correct
workspace identifier so the correct workspace entry is updated without mutating
the active workspace state.

---

Nitpick comments:
In `@packages/mcp/src/__tests__/integration.test.ts`:
- Around line 196-231: Add a new test variant alongside "clears an invalid
pre-registered token so strict registration can recover" that repeats the
failure/recovery flow but routes the failed call via a workspace identifier (use
callTool arguments like workspace_id or workspace_alias when calling
message.post) and then performs the same agent.register recovery; use the same
setup symbols (invalidAgentTokens, createRelayMcpServer, staleClient,
InMemoryTransport.createLinkedPair, captured) and assert the register request
still uses the primary apiKey (expect registerReq!.headers.authorization toBe
'Bearer rk_live_int1') and additionally assert the active workspace token for
that workspace remains unchanged after recovery (check whatever in-memory
workspace token store or token map your test suite exposes to confirm the
workspace-specific token is intact).

In `@packages/mcp/src/server.ts`:
- Around line 477-480: The current return replaces result.content with only the
recovery message, losing upstream error details; instead append the recovery
guidance to the existing result.content array (preserving its items and types)
by concatenating or pushing the object produced by agentTokenRecoveryMessage()
as { type: 'text', text: agentTokenRecoveryMessage() } so the returned object
spreads ...result and returns content: [...result.content, { type: 'text' as
const, text: agentTokenRecoveryMessage() }].
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro Plus

Run ID: 96559800-c2c2-4a5c-8cbe-cc21a411eebd

📥 Commits

Reviewing files that changed from the base of the PR and between d6c5ca2 and 0798d12.

📒 Files selected for processing (10)
  • README.md
  • openapi.yaml
  • packages/mcp/src/__tests__/integration.test.ts
  • packages/mcp/src/server.ts
  • packages/sdk-rust/src/types.rs
  • packages/sdk-rust/tests/parity.rs
  • packages/sdk-typescript/src/__tests__/errors.test.ts
  • packages/sdk-typescript/src/errors.ts
  • packages/server/src/middleware/__tests__/auth.test.ts
  • packages/server/src/middleware/auth.ts

Comment thread packages/mcp/src/server.ts Outdated

@cubic-dev-ai cubic-dev-ai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

1 issue found across 10 files

Prompt for AI agents (unresolved issues)

Check if these issues are valid — if so, understand the root cause of each and fix them. If appropriate, use sub-agents to investigate and fix each issue separately.


<file name="packages/mcp/src/server.ts">

<violation number="1" location="packages/mcp/src/server.ts:476">
P2: Use the routed workspace identity when invalidating tokens; this path can clear the active session instead of the workspace that actually failed auth.</violation>
</file>

Reply with feedback, questions, or to request a fix.

Re-trigger cubic

Comment thread packages/mcp/src/server.ts Outdated
&& typeof args.as === 'string'
? args.as
: undefined;
invalidateActiveAgentToken(asAgent);

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2: Use the routed workspace identity when invalidating tokens; this path can clear the active session instead of the workspace that actually failed auth.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At packages/mcp/src/server.ts, line 476:

<comment>Use the routed workspace identity when invalidating tokens; this path can clear the active session instead of the workspace that actually failed auth.</comment>

<file context>
@@ -368,7 +463,37 @@ export function createRelayMcpServer(options: McpServerOptions): McpServer {
+            && typeof args.as === 'string'
+            ? args.as
+            : undefined;
+          invalidateActiveAgentToken(asAgent);
+          return {
+            ...result,
</file context>

@github-actions

Copy link
Copy Markdown

Preview deployed!

Environment URL
API https://pr137-api.relaycast.dev
Health https://pr137-api.relaycast.dev/health
Observer https://pr137-observer.relaycast.dev/observer

This preview shares the staging database and will be cleaned up when the PR is merged or closed.

Run E2E tests

npm run e2e -- https://pr137-api.relaycast.dev --ci

Open observer dashboard

https://pr137-observer.relaycast.dev/observer

@khaliqgant khaliqgant merged commit 62c37dc into main May 22, 2026
6 checks passed
@khaliqgant khaliqgant deleted the codex/fix-mcp-invalid-agent-token branch May 22, 2026 13:57
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Relaycast MCP pre-registered agent token can become invalid mid-session

1 participant