Skip to content

Per-request Notion token passthrough for HTTP transport#315

Merged
ksinder merged 1 commit into
mainfrom
ksinder-http-token-passthrough
Jun 17, 2026
Merged

Per-request Notion token passthrough for HTTP transport#315
ksinder merged 1 commit into
mainfrom
ksinder-http-token-passthrough

Conversation

@ksinder

@ksinder ksinder commented Jun 17, 2026

Copy link
Copy Markdown
Contributor

Description

In Streamable HTTP transport, the Notion token is currently fixed at startup (NOTION_TOKEN / OPENAPI_MCP_HEADERS), which locks one deployment to a single Notion integration. This came up from a self-hosting user who wants one deployed server to serve multiple Notion integrations by passing the key as a header.

This PR adds an opt-in mode where each MCP client supplies its own Notion integration token per connection.

What changed

  • New --enable-token-passthrough flag and ENABLE_TOKEN_PASSTHROUGH=true env var (default off — existing single-token behavior is unchanged).
  • Per-connection token resolution on the initialize request, in order:
    1. dedicated Notion-Token header (preferred; coexists with the server's own Authorization gateway auth),
    2. Authorization: Bearer ntn_… — only when the server's own bearer auth is disabled (--unsafe-disable-auth),
    3. the startup env token as fallback, so passthrough + a default integration can coexist.
  • MCPProxy / initProxy now accept explicit headers; env resolution stays the default when none are passed. Each token is bound to its MCP session.

Security

  • Token shape validated (ntn_ / legacy secret_ prefixes), so a tenant's Notion token can never be confused with the server's gateway secret.
  • Tokens are never logged — only a redacted prefix (ntn_…(NN chars)).
  • A present-but-malformed Notion-Token returns 401 instead of silently falling back.
  • README documents the TLS / gateway-auth guidance for multi-tenant use.

How was this change tested?

  • Automated test (unit, integration, etc.) — new token.test.ts (14 tests) covers token shape-checking and header resolution precedence; proxy.test.ts extended for the explicit-header override. Full suite: 96 passing. npm run build passes.
  • Manual test (provide reproducible testing steps below)

🤖 Generated with Claude Code

@ksinder ksinder requested review from hallie and vshen-notion June 17, 2026 21:32
@ksinder ksinder marked this pull request as ready for review June 17, 2026 21:32
@ksinder ksinder force-pushed the ksinder-http-token-passthrough branch from 81cb5b1 to b96fad4 Compare June 17, 2026 21:34
@ksinder ksinder changed the base branch from main to ksinder-markdown-tools June 17, 2026 21:34
@ksinder

ksinder commented Jun 17, 2026

Copy link
Copy Markdown
Contributor Author

Reconciled with #320 (page-as-Markdown tools) to keep the stack conflict-free.

This branch is now stacked on ksinder-markdown-tools (#320) — base retargeted accordingly. Two coordinated changes so per-request tokens work with the new per-operation Notion-Version model:

Notion-Version is now sourced per-operation from the OpenAPI spec by HttpClient (added in #320), so passthrough requests automatically use 2026-03-11 for the markdown endpoints and 2025-09-03 for everything else — without each connection having to know the version. Tests updated accordingly; full suite green (102 tests).

Merge order: land #320 first, then this. (If #320 is dropped, this needs a one-line revert to put Notion-Version back into notionHeadersForToken.)

@ksinder ksinder force-pushed the ksinder-markdown-tools branch 2 times, most recently from eb9a0eb to 1be339c Compare June 17, 2026 22:03
Base automatically changed from ksinder-markdown-tools to main June 17, 2026 22:06
In Streamable HTTP mode the Notion token was fixed at startup
(NOTION_TOKEN / OPENAPI_MCP_HEADERS), locking one deployment to a single
Notion integration. This adds an opt-in mode where each client supplies
its own Notion integration token per connection, so one deployment can
serve many integrations.

- New `--enable-token-passthrough` flag / `ENABLE_TOKEN_PASSTHROUGH=true`
  env (default off; existing single-token behavior unchanged).
- Token resolved per init request: dedicated `Notion-Token` header first,
  then `Authorization: Bearer ntn_...` when the server's own gateway auth
  is disabled, then the startup env token as fallback.
- Token shape validated; bound per MCP session; never logged (redacted
  prefix only). Malformed explicit token returns 401.
- MCPProxy/initProxy now accept explicit headers; env resolution is the
  default when none are passed.

New token helper module is unit-tested; proxy tests cover the explicit
header override. Full suite green; build passes.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@ksinder ksinder force-pushed the ksinder-http-token-passthrough branch from b96fad4 to 7ea4a71 Compare June 17, 2026 22:07
@ksinder ksinder merged commit 604eef9 into main Jun 17, 2026
8 checks passed
@ksinder ksinder deleted the ksinder-http-token-passthrough branch June 17, 2026 22:09
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.

2 participants