Skip to content

fix: preserve MCP passthrough account context - #366

Open
alosec wants to merge 2 commits into
mainfrom
codex/mcp-passthrough-account-context
Open

fix: preserve MCP passthrough account context#366
alosec wants to merge 2 commits into
mainfrom
codex/mcp-passthrough-account-context

Conversation

@alosec

@alosec alosec commented Sep 10, 2026

Copy link
Copy Markdown
Contributor

Summary

  • forward a validated X-Account-ID through the existing MCP Token passthrough path
  • preserve an explicitly declared dashboard bearer credential when the gateway calls the Terminal49 API
  • leave the WorkOS/OAuth Bearer resolver path and ordinary API-key Token clients unchanged
  • allow the two passthrough context headers in MCP CORS preflight

Verification

  • MCP test suite: 15 files, 226 tests passed
  • API lint passed (three existing warnings outside this change)
  • root TypeScript check passed
  • formatting check passed
  • Chrome preview QA completed an Eve MCP list_containers call for a signed-in dashboard user without an authorization prompt

No production deployment is included. This PR remains draft.

Related: Terminal49/t49#3543, DEV-12439.


View with [code]smith Autofix with [code]smith
Need help on this PR? Tag @codesmith-bot with what you need. Autofix is disabled.

Greptile Summary

This PR adds an MCP passthrough mode that preserves a dashboard bearer credential and account context while leaving the WorkOS resolver path intact. It also permits the associated headers during CORS preflight and adds handler tests for successful and malformed passthrough requests.

  • Interprets explicitly marked Token credentials as upstream bearer credentials.
  • Validates and forwards account context into the request-scoped Terminal49 client.
  • Adds CORS support and focused authentication tests.
  • Greptile automatically discovered a related ticket that helped explain the purpose of this PR: enabling per-user MCP calls while preserving account authorization boundaries.

Confidence Score: 4/5

The PR should not merge until account context forwarding is restricted to explicitly marked bearer passthrough requests.

Ordinary API-key Token requests can now influence the upstream account header without opting into the new credential mode, changing their established behavior and potentially selecting or rejecting a different account context.

Files Needing Attention: api/mcp.ts

Important Files Changed

Filename Overview
api/mcp.ts Adds validation and forwarding for passthrough bearer/account context, but also forwards account context for unmarked API-key Token requests.
packages/mcp/tests/api-handler.test.ts Adds focused coverage for successful bearer passthrough, missing account context, and malformed account identifiers.

Sequence Diagram

sequenceDiagram
    participant D as Dashboard
    participant M as MCP gateway
    participant S as MCP server
    participant A as Terminal49 API
    D->>M: Token credential + credential type + account ID
    M->>M: Validate passthrough headers
    M->>S: Bearer credential and account context
    S->>A: Authorization: Bearer ... + x-account-id
    A-->>S: Account-scoped result
    S-->>D: MCP response
Loading

Fix all with Greploop Fix All in Codex Fix All in Claude Code

Prompt To Fix All With AI
### Issue 1
api/mcp.ts:527
**Unmarked Tokens Gain Account Context**

`requestedAccountId` is assigned for every `Token` request, even when the request is not marked as an account-scoped bearer credential. As a result, an ordinary API-key client that sends `X-Account-ID` will forward that caller-controlled value on every Terminal49 API request, changing its tenant context despite the requirement that ordinary `Token` clients remain unchanged. Only propagate the account ID when `credentialType === 'bearer'`.

Greptile automatically discovered a related ticket stating that account authorization must not fall back to browser-supplied account mapping, which informed this comment.

```suggestion
      accountId: credentialType === 'bearer' ? requestedAccountId : undefined,
```

---

For each issue above, determine whether it is valid and should be fixed. If so, fix it directly.

Reviews (1): Last reviewed commit: "fix: preserve passthrough bearer type" | Re-trigger Greptile

Greptile also left 1 inline comment on this PR.

Context used:

@vercel

vercel Bot commented Sep 10, 2026

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated
api Ready Ready Preview Sep 10, 2026 9:23pm UTC

Request Review

@dodeja
dodeja marked this pull request as ready for review September 10, 2026 21:54
@chatgpt-codex-connector

chatgpt-codex-connector Bot commented Sep 10, 2026

Copy link
Copy Markdown

Codex Review Summary

This comment shows the latest Codex review activity on this pull request.

Review Status Commit Review trigger
📝 Code Review Completed 2026-09-10T21:57:16.066406Z 891d04f Draft marked ready
ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review" or "@codex security review".

Codex reacts with 👀 while any review is running, comments if it has suggestions, and reacts with 👍 once all reviews finish with no findings.

Comment thread api/mcp.ts
apiToken: callerToken,
apiToken:
credentialType === 'bearer' ? `Bearer ${callerToken}` : callerToken,
accountId: requestedAccountId,

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P1 Unmarked Tokens Gain Account Context

requestedAccountId is assigned for every Token request, even when the request is not marked as an account-scoped bearer credential. As a result, an ordinary API-key client that sends X-Account-ID will forward that caller-controlled value on every Terminal49 API request, changing its tenant context despite the requirement that ordinary Token clients remain unchanged. Only propagate the account ID when credentialType === 'bearer'.

Greptile automatically discovered a related ticket stating that account authorization must not fall back to browser-supplied account mapping, which informed this comment.

Suggested change
accountId: requestedAccountId,
accountId: credentialType === 'bearer' ? requestedAccountId : undefined,

Knowledge Base Used: MCP protocol, transports, and authentication

Source Used: Linear — Resolve and verify per-user MCP OAuth for AskT49/Eve

Prompt To Fix With AI
This is a comment left during a code review.
Path: api/mcp.ts
Line: 527

Comment:
**Unmarked Tokens Gain Account Context**

`requestedAccountId` is assigned for every `Token` request, even when the request is not marked as an account-scoped bearer credential. As a result, an ordinary API-key client that sends `X-Account-ID` will forward that caller-controlled value on every Terminal49 API request, changing its tenant context despite the requirement that ordinary `Token` clients remain unchanged. Only propagate the account ID when `credentialType === 'bearer'`.

Greptile automatically discovered a related ticket stating that account authorization must not fall back to browser-supplied account mapping, which informed this comment.

```suggestion
      accountId: credentialType === 'bearer' ? requestedAccountId : undefined,
```

**Knowledge Base Used:** [MCP protocol, transports, and authentication](https://app.greptile.com/terminal49/-/custom-context/knowledge-base/terminal49/api/-/docs/mcp-protocol-and-authentication.md)

**Source Used:** Linear — [Resolve and verify per-user MCP OAuth for AskT49/Eve](https://linear.app/terminal49/issue/DEV-12439/resolve-and-verify-per-user-mcp-oauth-for-askt49eve)

---

For each issue above, determine whether it is valid and should be fixed. If so, fix it directly.

Fix in Codex Fix in Claude Code

@chatgpt-codex-connector chatgpt-codex-connector 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.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 891d04fc00

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread api/mcp.ts
apiToken: callerToken,
apiToken:
credentialType === 'bearer' ? `Bearer ${callerToken}` : callerToken,
accountId: requestedAccountId,

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Badge Only trust account IDs for declared bearer credentials

When a normal Token API-key caller supplies X-Account-ID without X-T49-Credential-Type, this assignment still accepts and forwards the caller-controlled UUID. Even if the upstream API ignores account context for API keys, createTerminal49McpServer uses this value directly as the PostHog distinctId (packages/mcp/src/server.ts:917-919), so any API-key holder can attribute MCP activity to an arbitrary account and corrupt tenant analytics. Only retain the account ID when the credential type is the account-scoped bearer type.

Useful? React with 👍 / 👎.

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.

1 participant