Skip to content

Reuse downstream MCP sessions across tool calls#1435

Merged
RhysSullivan merged 1 commit into
mainfrom
fix/mcp-downstream-session-reuse
Jul 20, 2026
Merged

Reuse downstream MCP sessions across tool calls#1435
RhysSullivan merged 1 commit into
mainfrom
fix/mcp-downstream-session-reuse

Conversation

@RhysSullivan

Copy link
Copy Markdown
Collaborator

Problem

Every MCP tool invocation dialed a fresh downstream connection and closed it after the call, so each call ran initialize and got a new Mcp-Session-Id. Downstream servers that key state by session (Render's select_workspacelist_logs, and similar) lose that state between calls: the first call succeeds, the next one lands in a brand-new session and fails.

Observed in production against Render MCP (select_workspace ok → list_logs/get_selected_workspace failing while list_deploys — which takes an explicit serviceId — survives), and reproduced wire-level: two calls in one sandbox execution arrived at the fixture on two distinct session ids.

Fix

Remote MCP invocations now lease connections from a per-plugin-instance pool:

  • One idle connection kept per resolved identity (endpoint + transport + headers/query + credential); leases are exclusive per invoke, so the per-invocation elicitation and tools/list_changed handlers keep their current semantics.
  • Idle sessions expire after 5 minutes (lazy eviction, no timers).
  • A connection is only returned to the pool on clean release; connection-level failures and transport-level invocation failures (new transportFailure flag on McpInvocationError) close and drop it.
  • A reused session rejected with HTTP 404 (the spec's session-expired answer, rejected before the tool executes) is redialed once transparently. Other failures never auto-retry, so side-effectful tools cannot double-execute.
  • The pool closes through the plugin close hook. stdio transports and endpoint probing/discovery remain strictly per-call.

Tests

  • New e2e scenario mcp-session-state.test.ts: a session-stateful MCP fixture (workspace-selection shape); asserts state set by call 1 is visible to call 2 and the fixture's request ledger saw exactly one Mcp-Session-Id. Failed before this change on selfhost and cloud; passes on both now.
  • New connection-pool.test.ts: sequential reuse (one session), concurrent leases (no serialization), transparent redial after server-side session loss, drop-on-failure.
  • elicitation.test.ts extended: per-invoke elicitation handlers ride a reused session correctly (accept then decline over one session).
  • Adjacent guards re-run green: mcp-catalog-sync (list_changed + unknown-tool healing) and policy-tool-approval e2e.

Gates: format:check, lint, typecheck, full packages/plugins/mcp suite green.

@github-actions

github-actions Bot commented Jul 20, 2026

Copy link
Copy Markdown
Contributor

Cloudflare preview

Torn down — the PR is closed.

@cloudflare-workers-and-pages

Copy link
Copy Markdown

Deploying with  Cloudflare Workers  Cloudflare Workers

The latest updates on your project. Learn more about integrating Git with Workers.

Status Name Latest Commit Preview URL Updated (UTC)
✅ Deployment successful!
View logs
executor-marketing c618b8f Commit Preview URL

Branch Preview URL
Jul 20 2026, 06:17 AM

@cloudflare-workers-and-pages

Copy link
Copy Markdown

Deploying with  Cloudflare Workers  Cloudflare Workers

The latest updates on your project. Learn more about integrating Git with Workers.

Status Name Latest Commit Updated (UTC)
✅ Deployment successful!
View logs
executor-cloud c618b8f Jul 20 2026, 06:18 AM

@pkg-pr-new

pkg-pr-new Bot commented Jul 20, 2026

Copy link
Copy Markdown

Open in StackBlitz

@executor-js/cli

npm i https://pkg.pr.new/@executor-js/cli@1435

@executor-js/config

npm i https://pkg.pr.new/@executor-js/config@1435

@executor-js/execution

npm i https://pkg.pr.new/@executor-js/execution@1435

@executor-js/sdk

npm i https://pkg.pr.new/@executor-js/sdk@1435

@executor-js/codemode-core

npm i https://pkg.pr.new/@executor-js/codemode-core@1435

@executor-js/runtime-quickjs

npm i https://pkg.pr.new/@executor-js/runtime-quickjs@1435

@executor-js/plugin-file-secrets

npm i https://pkg.pr.new/@executor-js/plugin-file-secrets@1435

@executor-js/plugin-graphql

npm i https://pkg.pr.new/@executor-js/plugin-graphql@1435

@executor-js/plugin-keychain

npm i https://pkg.pr.new/@executor-js/plugin-keychain@1435

@executor-js/plugin-mcp

npm i https://pkg.pr.new/@executor-js/plugin-mcp@1435

@executor-js/plugin-onepassword

npm i https://pkg.pr.new/@executor-js/plugin-onepassword@1435

@executor-js/plugin-openapi

npm i https://pkg.pr.new/@executor-js/plugin-openapi@1435

executor

npm i https://pkg.pr.new/executor@1435

commit: c618b8f

@RhysSullivan
RhysSullivan marked this pull request as ready for review July 20, 2026 06:30
@RhysSullivan
RhysSullivan merged commit af95edb into main Jul 20, 2026
21 checks passed
@RhysSullivan RhysSullivan mentioned this pull request Jul 20, 2026
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