From cc3c8da8129dd3bfeec458171129bb1f92d77fed Mon Sep 17 00:00:00 2001 From: Rhys Sullivan <39114868+RhysSullivan@users.noreply.github.com> Date: Sun, 25 Oct 2026 11:30:42 -0700 Subject: [PATCH] Forward passthrough MCP requests of moved organizations to v2 --- .changeset/v2-forward-passthrough.md | 5 +++++ apps/cloud/src/mcp/v2-forward.ts | 15 ++++++--------- 2 files changed, 11 insertions(+), 9 deletions(-) create mode 100644 .changeset/v2-forward-passthrough.md diff --git a/.changeset/v2-forward-passthrough.md b/.changeset/v2-forward-passthrough.md new file mode 100644 index 0000000000..a3ec8b2fa6 --- /dev/null +++ b/.changeset/v2-forward-passthrough.md @@ -0,0 +1,5 @@ +--- +"@executor-js/cloud": patch +--- + +Executor Cloud forwards MCP requests that use `mode=passthrough` for a workspace that has moved to Executor v2, instead of refusing them. v2 serves its own tools for them. diff --git a/apps/cloud/src/mcp/v2-forward.ts b/apps/cloud/src/mcp/v2-forward.ts index 9ed1061e0a..57997d1fe1 100644 --- a/apps/cloud/src/mcp/v2-forward.ts +++ b/apps/cloud/src/mcp/v2-forward.ts @@ -20,7 +20,7 @@ // --------------------------------------------------------------------------- import { jsonRpcErrorBody, UNAVAILABLE_RETRY_AFTER_SECONDS } from "@executor-js/host-mcp"; -import { readElicitationMode, readToolMode } from "@executor-js/cloudflare/mcp/do-headers"; +import { readElicitationMode } from "@executor-js/cloudflare/mcp/do-headers"; import { EXECUTOR_ORG_SELECTOR_HEADER } from "@executor-js/sdk/shared"; import type { MovedToV2 } from "./moved-to-v2"; @@ -105,7 +105,11 @@ export const toolkitConnectionId = async ( return `v1-${hex(digest).slice(0, 40)}`; }; -/** v2's MCP endpoint for a request: only the elicitation mode and toolkit connection carry over. */ +/** + * v2's MCP endpoint for a request: only the elicitation mode and toolkit + * connection carry over. v1's tool mode (`mode=passthrough`) does not: v2 + * serves its own tools for every request. + */ export const buildV2Url = (origin: string, request: Request, connection?: string): URL => { const url = new URL("/mcp", origin); const mode = readElicitationMode(request); @@ -169,9 +173,6 @@ export const relayV2Response = ( }); }; -export const PASSTHROUGH_MOVED_MESSAGE = - "This workspace has moved to Executor v2, which has no passthrough mode. Remove mode=passthrough from the MCP URL."; - export const TOOLKIT_NOT_FOUND_MESSAGE = "Toolkit not found"; /** The bindings forwarding needs; absent in workers that do not forward. */ @@ -217,10 +218,6 @@ export const forwardToV2 = async ( ): Promise => { const log = deps.log ?? defaultLog; - if (readToolMode(request) === "passthrough") { - return jsonRpcErrorBody(400, -32600, PASSTHROUGH_MOVED_MESSAGE); - } - const { V2_MCP: binding, V2_MCP_ORIGIN: origin, V2_MCP_FORWARD_SECRET: secret } = env; if (!binding || !origin || !secret) { log({ event: "mcp_v2_forward_unconfigured", hasBinding: !!binding, hasOrigin: !!origin });