// Cloud: MCP sessions driven by the REAL @modelcontextprotocol/sdk Client over// StreamableHTTP — exactly the code path Claude/Cursor run. The dev server is// the production wrangler topology (real workerd, real McpSessionDO), so// session continuity here is real Durable Object state surviving across// client connections, not a stub.//// Ported from apps/cloud/src/mcp-miniflare.e2e.node.test.ts (unstable_dev +// test-seam bearers) onto the e2e dev server with real OAuth bearers.// Telemetry-span assertions from that file required injecting an OTLP// receiver into the worker env and were NOT carried (not black-box). import { expect } from "@effect/vitest";import { Effect } from "effect";import { composePluginApi } from "@executor-js/api/server";import { Client } from "@modelcontextprotocol/sdk/client/index.js";import { StreamableHTTPClientTransport } from "@modelcontextprotocol/sdk/client/streamableHttp.js"; import { scenario } from "../src/scenario";import { Api, Mcp, Target } from "../src/services";import type { Identity } from "../src/target";import { configuredMcpPausedSessionIdleTimeoutMs, configuredMcpSessionTimeoutMs,} from "../setup/mcp-session-timeouts"; const coreApi = composePluginApi([] as const); const emailOf = (identity: Identity): string => identity.credentials?.email ?? identity.label; interface Connected { readonly client: Client; readonly transport: StreamableHTTPClientTransport;} /** A real SDK client over StreamableHTTP; `sessionId` resumes an existing session. */const connectClient = async ( mcpUrl: string, bearer: string, sessionId?: string,): Promise<Connected> => { const client = new Client( { name: "executor-e2e-sessions", version: "0.0.1" }, { capabilities: {} }, ); const transport = new StreamableHTTPClientTransport(new URL(mcpUrl), { requestInit: { headers: { authorization: `Bearer ${bearer}` } }, ...(sessionId === undefined ? {} : { sessionId }), }); await client.connect(transport); return { client, transport };}; const textOf = (result: { content?: unknown; toolResult?: unknown }): string => ((result.content ?? []) as Array<{ type: string; text?: string }>) .filter((part) => part.type === "text") .map((part) => part.text) .join("\n"); const closeQuietly = (connected: Connected): Effect.Effect<void> => Effect.promise(() => connected.client.close().catch(() => undefined)); scenario( "MCP sessions · a real MCP client connects, lists tools, and executes code", {}, Effect.gen(function* () { const target = yield* Target; const mcp = yield* Mcp; const identity = yield* target.newIdentity(); const bearer = yield* mcp.mintBearer(emailOf(identity)); const session = yield* Effect.promise(() => connectClient(target.mcpUrl, bearer)); yield* Effect.gen(function* () { expect( session.client.getServerVersion()?.name, "the handshake reports the product server", ).toBe("executor"); expect(session.transport.sessionId, "the transport holds a session id").toEqual( expect.any(String), ); const { tools } = yield* Effect.promise(() => session.client.listTools()); expect( tools.map((tool) => tool.name), "the execute tool is advertised", ).toContain("execute"); expect( tools.map((tool) => tool.name), "the resume tool is advertised", ).toContain("resume"); const result = yield* Effect.promise(() => session.client.callTool({ name: "execute", arguments: { code: "return 6 * 7;" } }), ); expect(result.isError, "the call succeeds").not.toBe(true); expect(textOf(result), "the sandbox returns the value").toContain("42"); }).pipe(Effect.ensuring(closeQuietly(session))); }),); scenario( "MCP sessions · a second client resuming the session id continues the session", {}, Effect.gen(function* () { const target = yield* Target; const mcp = yield* Mcp; const identity = yield* target.newIdentity(); const bearer = yield* mcp.mintBearer(emailOf(identity)); // First connection establishes the session, does real work, then goes // away — the laptop-closed / process-restarted case. const first = yield* Effect.promise(() => connectClient(target.mcpUrl, bearer)); const sessionId = first.transport.sessionId; expect(sessionId, "the first client got a session id").toEqual(expect.any(String)); const before = yield* Effect.promise(() => first.client.callTool({ name: "execute", arguments: { code: 'return "before";' } }), ).pipe(Effect.ensuring(closeQuietly(first))); expect(textOf(before), "the first client's call succeeded").toContain("before"); // A brand-new client resumes with nothing but the session id. The // session's Durable Object state persists across connections — this is // the restore guarantee. const second = yield* Effect.promise(() => connectClient(target.mcpUrl, bearer, sessionId)); yield* Effect.gen(function* () { expect(second.transport.sessionId, "the session id is preserved, not reissued").toBe( sessionId, ); const { tools } = yield* Effect.promise(() => second.client.listTools()); expect( tools.map((tool) => tool.name), "the resumed session serves requests", ).toContain("execute"); const after = yield* Effect.promise(() => second.client.callTool({ name: "execute", arguments: { code: 'return "after";' } }), ); expect(after.isError, "the resumed session executes code").not.toBe(true); expect(textOf(after), "the resumed session returns results").toContain("after"); }).pipe(Effect.ensuring(closeQuietly(second))); }),); const IDLE_RUNTIME_DISPOSE_BUFFER_MS = 2_000;const IDLE_RUNTIME_DISPOSE_GAP_MS = configuredMcpSessionTimeoutMs() + IDLE_RUNTIME_DISPOSE_BUFFER_MS;const IDLE_RUNTIME_DISPOSE_SCENARIO_TIMEOUT_MS = IDLE_RUNTIME_DISPOSE_GAP_MS + 120_000; scenario( "MCP sessions · a resumed session after idle runtime disposal restores cleanly", { timeout: IDLE_RUNTIME_DISPOSE_SCENARIO_TIMEOUT_MS }, Effect.gen(function* () { const target = yield* Target; const mcp = yield* Mcp; const identity = yield* target.newIdentity(); const bearer = yield* mcp.mintBearer(emailOf(identity)); const first = yield* Effect.promise(() => connectClient(target.mcpUrl, bearer)); const sessionId = first.transport.sessionId; expect(sessionId, "the client got a session id").toEqual(expect.any(String)); if (sessionId === undefined) return yield* Effect.die("missing session id"); const before = yield* Effect.promise(() => first.client.callTool({ name: "execute", arguments: { code: 'return "before-idle";' } }), ).pipe(Effect.ensuring(closeQuietly(first))); expect(before.isError, "the pre-idle call succeeds").not.toBe(true); expect(textOf(before), "the pre-idle call returns results").toContain("before-idle"); yield* Effect.sleep(IDLE_RUNTIME_DISPOSE_GAP_MS); const second = yield* Effect.promise(() => connectClient(target.mcpUrl, bearer, sessionId)); yield* Effect.gen(function* () { expect(second.transport.sessionId, "the session id is preserved").toBe(sessionId); const after = yield* Effect.promise(() => second.client.callTool({ name: "execute", arguments: { code: 'return "after-idle";' } }), ); expect(after.isError, "the post-idle call succeeds").not.toBe(true); expect(textOf(after), "the post-idle call returns results").toContain("after-idle"); }).pipe(Effect.ensuring(closeQuietly(second))); }),); scenario( "MCP sessions · an unknown session id fails fast with a clean error, not a hang", {}, Effect.gen(function* () { const target = yield* Target; const mcp = yield* Mcp; const identity = yield* target.newIdentity(); const bearer = yield* mcp.mintBearer(emailOf(identity)); // A session id the server never issued (right shape, never minted). const session = yield* Effect.promise(() => connectClient(target.mcpUrl, bearer, "0".repeat(64)), ); const failure = yield* Effect.flip( Effect.tryPromise({ try: () => session.client.listTools(), catch: (cause) => cause, }), ).pipe( Effect.timeoutOrElse({ duration: "15 seconds", orElse: () => Effect.die(new Error("listTools on an unknown session hung instead of failing")), }), Effect.ensuring(closeQuietly(session)), ); expect(String(failure), "the server answered with a JSON-RPC error envelope").toContain( "jsonrpc", ); }),); scenario( "MCP sessions · two concurrent clients hold isolated sessions that don't interfere", {}, Effect.gen(function* () { const target = yield* Target; const mcp = yield* Mcp; const identity = yield* target.newIdentity(); const bearer = yield* mcp.mintBearer(emailOf(identity)); const [alpha, beta] = yield* Effect.promise(() => Promise.all([connectClient(target.mcpUrl, bearer), connectClient(target.mcpUrl, bearer)]), ); yield* Effect.gen(function* () { expect(alpha.transport.sessionId, "each client gets its own session").not.toBe( beta.transport.sessionId, ); const [alphaResult, betaResult] = yield* Effect.promise(() => Promise.all([ alpha.client.callTool({ name: "execute", arguments: { code: 'await new Promise((resolve) => setTimeout(resolve, 300));\nreturn "alpha-result";', }, }), beta.client.callTool({ name: "execute", arguments: { code: 'return "beta-result";' }, }), ]), ); expect(textOf(alphaResult), "the first session got its own answer").toContain("alpha-result"); expect(textOf(alphaResult), "no cross-talk into the first session").not.toContain( "beta-result", ); expect(textOf(betaResult), "the second session got its own answer").toContain("beta-result"); expect(textOf(betaResult), "no cross-talk into the second session").not.toContain( "alpha-result", ); }).pipe(Effect.ensuring(closeQuietly(alpha)), Effect.ensuring(closeQuietly(beta))); }),); const APPROVAL_TARGET_TOOL = "executor.coreTools.policies.list"; const GATED_CODE = `const result = await tools.executor.coreTools.policies.list({});return JSON.stringify(result);`; const executionIdOf = (text: string): string | undefined => /\bexecutionId:\s*(\S+)/.exec(text)?.[1]; const IDLE_TEARDOWN_BUFFER_MS = 2_000;const IDLE_TEARDOWN_GAP_MS = configuredMcpPausedSessionIdleTimeoutMs() + IDLE_TEARDOWN_BUFFER_MS;const IDLE_TEARDOWN_SCENARIO_TIMEOUT_MS = IDLE_TEARDOWN_GAP_MS + 120_000; // The session DO tears its runtime down after the configured idle ceiling and// rebuilds it from storage on the next request. The e2e harness shortens that// ceiling when it owns the cloud dev stack. Paused approvals deliberately do// not survive this (durable pause state is out of scope); the contract is that// an expired pause fails with recovery guidance and the session keeps working. scenario( "MCP sessions · an approval paused past the idle window expires with re-run guidance, not a dead end", { timeout: IDLE_TEARDOWN_SCENARIO_TIMEOUT_MS }, Effect.gen(function* () { const target = yield* Target; const { client } = yield* Api; const mcp = yield* Mcp; const identity = yield* target.newIdentity(); const api = yield* client(coreApi, identity); const bearer = yield* mcp.mintBearer(emailOf(identity)); const policy = yield* api.policies.create({ payload: { owner: "org", pattern: APPROVAL_TARGET_TOOL, action: "require_approval" }, }); yield* Effect.gen(function* () { const first = yield* Effect.promise(() => connectClient(target.mcpUrl, bearer)); const sessionId = first.transport.sessionId; expect(sessionId, "the first client got a session id").toEqual(expect.any(String)); if (sessionId === undefined) return yield* Effect.die("missing session id"); const paused = yield* Effect.promise(() => first.client.callTool({ name: "execute", arguments: { code: GATED_CODE } }), ).pipe(Effect.ensuring(closeQuietly(first))); const pausedText = textOf(paused); expect(pausedText, "the gated call pauses instead of completing").toContain( "Execution paused", ); const executionId = executionIdOf(pausedText); expect(executionId, "the paused result carries the executionId").toEqual(expect.any(String)); if (executionId === undefined) return yield* Effect.die("missing paused execution id"); // The user thinks the approval over for longer than the session keeps // its runtime warm; the pause is gone when they come back. yield* Effect.sleep(IDLE_TEARDOWN_GAP_MS); const second = yield* Effect.promise(() => connectClient(target.mcpUrl, bearer, sessionId)); yield* Effect.gen(function* () { const resumed = yield* Effect.promise(() => second.client.callTool({ name: "resume", arguments: { executionId, action: "accept", content: "{}" }, }), ); const resumedText = textOf(resumed); expect( resumedText, "the error tells the model how to recover, not just that the pause is gone", ).toContain("run the execute tool again"); expect(resumed.isError, "the expired resume is an error, not a silent success").toBe(true); // The advertised recovery actually works on the same session: a fresh // execute pauses with a NEW id (stale ids are never reused), and // resuming that completes the gated call. const reExecuted = yield* Effect.promise(() => second.client.callTool({ name: "execute", arguments: { code: GATED_CODE } }), ); const reExecutedText = textOf(reExecuted); expect(reExecutedText, "the re-run pauses for approval again").toContain( "Execution paused", ); const freshExecutionId = executionIdOf(reExecutedText); expect(freshExecutionId, "the re-run mints a different executionId").not.toBe(executionId); if (freshExecutionId === undefined) return yield* Effect.die("missing fresh execution id"); const resumedFresh = yield* Effect.promise(() => second.client.callTool({ name: "resume", arguments: { executionId: freshExecutionId, action: "accept", content: "{}" }, }), ); expect(resumedFresh.isError, "the fresh approval resumes to completion").not.toBe(true); expect(textOf(resumedFresh), "the gated tool's result comes back after approval").toContain( APPROVAL_TARGET_TOOL, ); }).pipe(Effect.ensuring(closeQuietly(second))); }).pipe( Effect.ensuring( api.policies .remove({ params: { policyId: policy.id }, payload: { owner: "org" } }) .pipe(Effect.ignore), ), ); }),); scenario( "MCP sessions · a duplicate resume replays the outcome instead of losing the approval", {}, Effect.gen(function* () { const target = yield* Target; const { client } = yield* Api; const mcp = yield* Mcp; const identity = yield* target.newIdentity(); const api = yield* client(coreApi, identity); const bearer = yield* mcp.mintBearer(emailOf(identity)); const policy = yield* api.policies.create({ payload: { owner: "org", pattern: APPROVAL_TARGET_TOOL, action: "require_approval" }, }); yield* Effect.gen(function* () { const session = yield* Effect.promise(() => connectClient(target.mcpUrl, bearer)); yield* Effect.gen(function* () { const paused = yield* Effect.promise(() => session.client.callTool({ name: "execute", arguments: { code: GATED_CODE } }), ); const executionId = executionIdOf(textOf(paused)); expect(executionId, "the paused result carries the executionId").toEqual( expect.any(String), ); const resume = () => Effect.promise(() => session.client.callTool({ name: "resume", arguments: { executionId: executionId ?? "", action: "accept", content: "{}" }, }), ); const first = yield* resume(); expect(first.isError, "the first resume completes the execution").not.toBe(true); // MCP clients retry resume when a response is lost in transit. The // retry must replay the same completed outcome — the production // failure mode was "No paused execution" seconds after a successful // resume. const retry = yield* resume(); expect(retry.isError, "the duplicate resume is not an error").not.toBe(true); expect(textOf(retry), "the duplicate resume returns the same completed result").toContain( APPROVAL_TARGET_TOOL, ); }).pipe(Effect.ensuring(closeQuietly(session))); }).pipe( Effect.ensuring( api.policies .remove({ params: { policyId: policy.id }, payload: { owner: "org" } }) .pipe(Effect.ignore), ), ); }),); scenario( "MCP sessions · a paused approval survives the client reconnecting and resumes", {}, Effect.gen(function* () { const target = yield* Target; const { client } = yield* Api; const mcp = yield* Mcp; const identity = yield* target.newIdentity(); const api = yield* client(coreApi, identity); const bearer = yield* mcp.mintBearer(emailOf(identity)); // Gate a built-in tool behind human approval; the org is fresh, so the // gate affects no other scenario, but remove it anyway on every exit. const policy = yield* api.policies.create({ payload: { owner: "org", pattern: APPROVAL_TARGET_TOOL, action: "require_approval" }, }); yield* Effect.gen(function* () { const first = yield* Effect.promise(() => connectClient(target.mcpUrl, bearer)); const sessionId = first.transport.sessionId; const paused = yield* Effect.promise(() => first.client.callTool({ name: "execute", arguments: { code: GATED_CODE } }), ).pipe(Effect.ensuring(closeQuietly(first))); const pausedText = textOf(paused); expect(pausedText, "the gated call pauses instead of completing").toContain( "Execution paused", ); const executionId = /\bexecutionId:\s*(\S+)/.exec(pausedText)?.[1]; expect(executionId, "the paused result carries the executionId").toEqual(expect.any(String)); // The user answers from a NEW client on the same session — the paused // execution lives in the session, not the connection. const second = yield* Effect.promise(() => connectClient(target.mcpUrl, bearer, sessionId)); const resumed = yield* Effect.promise(() => second.client.callTool({ name: "resume", arguments: { executionId: executionId ?? "", action: "accept", content: "{}" }, }), ).pipe(Effect.ensuring(closeQuietly(second))); expect(resumed.isError, "the resumed execution completes").not.toBe(true); expect(textOf(resumed), "the gated tool's result comes back after approval").toContain( APPROVAL_TARGET_TOOL, ); }).pipe( Effect.ensuring( api.policies .remove({ params: { policyId: policy.id }, payload: { owner: "org" } }) .pipe(Effect.ignore), ), ); }),);
// Cloud: MCP sessions driven by the REAL @modelcontextprotocol/sdk Client over
// StreamableHTTP — exactly the code path Claude/Cursor run. The dev server is
// the production wrangler topology (real workerd, real McpSessionDO), so
// session continuity here is real Durable Object state surviving across
// client connections, not a stub.
//
// Ported from apps/cloud/src/mcp-miniflare.e2e.node.test.ts (unstable_dev +
// test-seam bearers) onto the e2e dev server with real OAuth bearers.
// Telemetry-span assertions from that file required injecting an OTLP
// receiver into the worker env and were NOT carried (not black-box).
import { expect } from "@effect/vitest";
import { Effect } from "effect";
import { composePluginApi } from "@executor-js/api/server";
import { Client } from "@modelcontextprotocol/sdk/client/index.js";
import { StreamableHTTPClientTransport } from "@modelcontextprotocol/sdk/client/streamableHttp.js";
import { scenario } from "../src/scenario";
import { Api, Mcp, Target } from "../src/services";
import type { Identity } from "../src/target";
import {
configuredMcpPausedSessionIdleTimeoutMs,
configuredMcpSessionTimeoutMs,
} from "../setup/mcp-session-timeouts";
const coreApi = composePluginApi([] as const);
const emailOf = (identity: Identity): string => identity.credentials?.email ?? identity.label;
interface Connected {
readonly client: Client;
readonly transport: StreamableHTTPClientTransport;
}
/** A real SDK client over StreamableHTTP; `sessionId` resumes an existing session. */
const connectClient = async (
mcpUrl: string,
bearer: string,
sessionId?: string,
): Promise<Connected> => {
const client = new Client(
{ name: "executor-e2e-sessions", version: "0.0.1" },
{ capabilities: {} },
);
const transport = new StreamableHTTPClientTransport(new URL(mcpUrl), {
requestInit: { headers: { authorization: `Bearer ${bearer}` } },
...(sessionId === undefined ? {} : { sessionId }),
});
await client.connect(transport);
return { client, transport };
};
const textOf = (result: { content?: unknown; toolResult?: unknown }): string =>
((result.content ?? []) as Array<{ type: string; text?: string }>)
.filter((part) => part.type === "text")
.map((part) => part.text)
.join("\n");
const closeQuietly = (connected: Connected): Effect.Effect<void> =>
Effect.promise(() => connected.client.close().catch(() => undefined));
scenario(
"MCP sessions · a real MCP client connects, lists tools, and executes code",
{},
Effect.gen(function* () {
const target = yield* Target;
const mcp = yield* Mcp;
const identity = yield* target.newIdentity();
const bearer = yield* mcp.mintBearer(emailOf(identity));
const session = yield* Effect.promise(() => connectClient(target.mcpUrl, bearer));
yield* Effect.gen(function* () {
expect(
session.client.getServerVersion()?.name,
"the handshake reports the product server",
).toBe("executor");
expect(session.transport.sessionId, "the transport holds a session id").toEqual(
expect.any(String),
);
const { tools } = yield* Effect.promise(() => session.client.listTools());
expect(
tools.map((tool) => tool.name),
"the execute tool is advertised",
).toContain("execute");
expect(
tools.map((tool) => tool.name),
"the resume tool is advertised",
).toContain("resume");
const result = yield* Effect.promise(() =>
session.client.callTool({ name: "execute", arguments: { code: "return 6 * 7;" } }),
);
expect(result.isError, "the call succeeds").not.toBe(true);
expect(textOf(result), "the sandbox returns the value").toContain("42");
}).pipe(Effect.ensuring(closeQuietly(session)));
}),
);
scenario(
"MCP sessions · a second client resuming the session id continues the session",
{},
Effect.gen(function* () {
const target = yield* Target;
const mcp = yield* Mcp;
const identity = yield* target.newIdentity();
const bearer = yield* mcp.mintBearer(emailOf(identity));
// First connection establishes the session, does real work, then goes
// away — the laptop-closed / process-restarted case.
const first = yield* Effect.promise(() => connectClient(target.mcpUrl, bearer));
const sessionId = first.transport.sessionId;
expect(sessionId, "the first client got a session id").toEqual(expect.any(String));
const before = yield* Effect.promise(() =>
first.client.callTool({ name: "execute", arguments: { code: 'return "before";' } }),
).pipe(Effect.ensuring(closeQuietly(first)));
expect(textOf(before), "the first client's call succeeded").toContain("before");
// A brand-new client resumes with nothing but the session id. The
// session's Durable Object state persists across connections — this is
// the restore guarantee.
const second = yield* Effect.promise(() => connectClient(target.mcpUrl, bearer, sessionId));
yield* Effect.gen(function* () {
expect(second.transport.sessionId, "the session id is preserved, not reissued").toBe(
sessionId,
);
const { tools } = yield* Effect.promise(() => second.client.listTools());
expect(
tools.map((tool) => tool.name),
"the resumed session serves requests",
).toContain("execute");
const after = yield* Effect.promise(() =>
second.client.callTool({ name: "execute", arguments: { code: 'return "after";' } }),
);
expect(after.isError, "the resumed session executes code").not.toBe(true);
expect(textOf(after), "the resumed session returns results").toContain("after");
}).pipe(Effect.ensuring(closeQuietly(second)));
}),
);
const IDLE_RUNTIME_DISPOSE_BUFFER_MS = 2_000;
const IDLE_RUNTIME_DISPOSE_GAP_MS =
configuredMcpSessionTimeoutMs() + IDLE_RUNTIME_DISPOSE_BUFFER_MS;
const IDLE_RUNTIME_DISPOSE_SCENARIO_TIMEOUT_MS = IDLE_RUNTIME_DISPOSE_GAP_MS + 120_000;
scenario(
"MCP sessions · a resumed session after idle runtime disposal restores cleanly",
{ timeout: IDLE_RUNTIME_DISPOSE_SCENARIO_TIMEOUT_MS },
Effect.gen(function* () {
const target = yield* Target;
const mcp = yield* Mcp;
const identity = yield* target.newIdentity();
const bearer = yield* mcp.mintBearer(emailOf(identity));
const first = yield* Effect.promise(() => connectClient(target.mcpUrl, bearer));
const sessionId = first.transport.sessionId;
expect(sessionId, "the client got a session id").toEqual(expect.any(String));
if (sessionId === undefined) return yield* Effect.die("missing session id");
const before = yield* Effect.promise(() =>
first.client.callTool({ name: "execute", arguments: { code: 'return "before-idle";' } }),
).pipe(Effect.ensuring(closeQuietly(first)));
expect(before.isError, "the pre-idle call succeeds").not.toBe(true);
expect(textOf(before), "the pre-idle call returns results").toContain("before-idle");
yield* Effect.sleep(IDLE_RUNTIME_DISPOSE_GAP_MS);
const second = yield* Effect.promise(() => connectClient(target.mcpUrl, bearer, sessionId));
yield* Effect.gen(function* () {
expect(second.transport.sessionId, "the session id is preserved").toBe(sessionId);
const after = yield* Effect.promise(() =>
second.client.callTool({ name: "execute", arguments: { code: 'return "after-idle";' } }),
);
expect(after.isError, "the post-idle call succeeds").not.toBe(true);
expect(textOf(after), "the post-idle call returns results").toContain("after-idle");
}).pipe(Effect.ensuring(closeQuietly(second)));
}),
);
scenario(
"MCP sessions · an unknown session id fails fast with a clean error, not a hang",
{},
Effect.gen(function* () {
const target = yield* Target;
const mcp = yield* Mcp;
const identity = yield* target.newIdentity();
const bearer = yield* mcp.mintBearer(emailOf(identity));
// A session id the server never issued (right shape, never minted).
const session = yield* Effect.promise(() =>
connectClient(target.mcpUrl, bearer, "0".repeat(64)),
);
const failure = yield* Effect.flip(
Effect.tryPromise({
try: () => session.client.listTools(),
catch: (cause) => cause,
}),
).pipe(
Effect.timeoutOrElse({
duration: "15 seconds",
orElse: () =>
Effect.die(new Error("listTools on an unknown session hung instead of failing")),
}),
Effect.ensuring(closeQuietly(session)),
);
expect(String(failure), "the server answered with a JSON-RPC error envelope").toContain(
"jsonrpc",
);
}),
);
scenario(
"MCP sessions · two concurrent clients hold isolated sessions that don't interfere",
{},
Effect.gen(function* () {
const target = yield* Target;
const mcp = yield* Mcp;
const identity = yield* target.newIdentity();
const bearer = yield* mcp.mintBearer(emailOf(identity));
const [alpha, beta] = yield* Effect.promise(() =>
Promise.all([connectClient(target.mcpUrl, bearer), connectClient(target.mcpUrl, bearer)]),
);
yield* Effect.gen(function* () {
expect(alpha.transport.sessionId, "each client gets its own session").not.toBe(
beta.transport.sessionId,
);
const [alphaResult, betaResult] = yield* Effect.promise(() =>
Promise.all([
alpha.client.callTool({
name: "execute",
arguments: {
code: 'await new Promise((resolve) => setTimeout(resolve, 300));\nreturn "alpha-result";',
},
}),
beta.client.callTool({
name: "execute",
arguments: { code: 'return "beta-result";' },
}),
]),
);
expect(textOf(alphaResult), "the first session got its own answer").toContain("alpha-result");
expect(textOf(alphaResult), "no cross-talk into the first session").not.toContain(
"beta-result",
);
expect(textOf(betaResult), "the second session got its own answer").toContain("beta-result");
expect(textOf(betaResult), "no cross-talk into the second session").not.toContain(
"alpha-result",
);
}).pipe(Effect.ensuring(closeQuietly(alpha)), Effect.ensuring(closeQuietly(beta)));
}),
);
const APPROVAL_TARGET_TOOL = "executor.coreTools.policies.list";
const GATED_CODE = `
const result = await tools.executor.coreTools.policies.list({});
return JSON.stringify(result);
`;
const executionIdOf = (text: string): string | undefined =>
/\bexecutionId:\s*(\S+)/.exec(text)?.[1];
const IDLE_TEARDOWN_BUFFER_MS = 2_000;
const IDLE_TEARDOWN_GAP_MS = configuredMcpPausedSessionIdleTimeoutMs() + IDLE_TEARDOWN_BUFFER_MS;
const IDLE_TEARDOWN_SCENARIO_TIMEOUT_MS = IDLE_TEARDOWN_GAP_MS + 120_000;
// The session DO tears its runtime down after the configured idle ceiling and
// rebuilds it from storage on the next request. The e2e harness shortens that
// ceiling when it owns the cloud dev stack. Paused approvals deliberately do
// not survive this (durable pause state is out of scope); the contract is that
// an expired pause fails with recovery guidance and the session keeps working.
scenario(
"MCP sessions · an approval paused past the idle window expires with re-run guidance, not a dead end",
{ timeout: IDLE_TEARDOWN_SCENARIO_TIMEOUT_MS },
Effect.gen(function* () {
const target = yield* Target;
const { client } = yield* Api;
const mcp = yield* Mcp;
const identity = yield* target.newIdentity();
const api = yield* client(coreApi, identity);
const bearer = yield* mcp.mintBearer(emailOf(identity));
const policy = yield* api.policies.create({
payload: { owner: "org", pattern: APPROVAL_TARGET_TOOL, action: "require_approval" },
});
yield* Effect.gen(function* () {
const first = yield* Effect.promise(() => connectClient(target.mcpUrl, bearer));
const sessionId = first.transport.sessionId;
expect(sessionId, "the first client got a session id").toEqual(expect.any(String));
if (sessionId === undefined) return yield* Effect.die("missing session id");
const paused = yield* Effect.promise(() =>
first.client.callTool({ name: "execute", arguments: { code: GATED_CODE } }),
).pipe(Effect.ensuring(closeQuietly(first)));
const pausedText = textOf(paused);
expect(pausedText, "the gated call pauses instead of completing").toContain(
"Execution paused",
);
const executionId = executionIdOf(pausedText);
expect(executionId, "the paused result carries the executionId").toEqual(expect.any(String));
if (executionId === undefined) return yield* Effect.die("missing paused execution id");
// The user thinks the approval over for longer than the session keeps
// its runtime warm; the pause is gone when they come back.
yield* Effect.sleep(IDLE_TEARDOWN_GAP_MS);
const second = yield* Effect.promise(() => connectClient(target.mcpUrl, bearer, sessionId));
yield* Effect.gen(function* () {
const resumed = yield* Effect.promise(() =>
second.client.callTool({
name: "resume",
arguments: { executionId, action: "accept", content: "{}" },
}),
);
const resumedText = textOf(resumed);
expect(
resumedText,
"the error tells the model how to recover, not just that the pause is gone",
).toContain("run the execute tool again");
expect(resumed.isError, "the expired resume is an error, not a silent success").toBe(true);
// The advertised recovery actually works on the same session: a fresh
// execute pauses with a NEW id (stale ids are never reused), and
// resuming that completes the gated call.
const reExecuted = yield* Effect.promise(() =>
second.client.callTool({ name: "execute", arguments: { code: GATED_CODE } }),
);
const reExecutedText = textOf(reExecuted);
expect(reExecutedText, "the re-run pauses for approval again").toContain(
"Execution paused",
);
const freshExecutionId = executionIdOf(reExecutedText);
expect(freshExecutionId, "the re-run mints a different executionId").not.toBe(executionId);
if (freshExecutionId === undefined) return yield* Effect.die("missing fresh execution id");
const resumedFresh = yield* Effect.promise(() =>
second.client.callTool({
name: "resume",
arguments: { executionId: freshExecutionId, action: "accept", content: "{}" },
}),
);
expect(resumedFresh.isError, "the fresh approval resumes to completion").not.toBe(true);
expect(textOf(resumedFresh), "the gated tool's result comes back after approval").toContain(
APPROVAL_TARGET_TOOL,
);
}).pipe(Effect.ensuring(closeQuietly(second)));
}).pipe(
Effect.ensuring(
api.policies
.remove({ params: { policyId: policy.id }, payload: { owner: "org" } })
.pipe(Effect.ignore),
),
);
}),
);
scenario(
"MCP sessions · a duplicate resume replays the outcome instead of losing the approval",
{},
Effect.gen(function* () {
const target = yield* Target;
const { client } = yield* Api;
const mcp = yield* Mcp;
const identity = yield* target.newIdentity();
const api = yield* client(coreApi, identity);
const bearer = yield* mcp.mintBearer(emailOf(identity));
const policy = yield* api.policies.create({
payload: { owner: "org", pattern: APPROVAL_TARGET_TOOL, action: "require_approval" },
});
yield* Effect.gen(function* () {
const session = yield* Effect.promise(() => connectClient(target.mcpUrl, bearer));
yield* Effect.gen(function* () {
const paused = yield* Effect.promise(() =>
session.client.callTool({ name: "execute", arguments: { code: GATED_CODE } }),
);
const executionId = executionIdOf(textOf(paused));
expect(executionId, "the paused result carries the executionId").toEqual(
expect.any(String),
);
const resume = () =>
Effect.promise(() =>
session.client.callTool({
name: "resume",
arguments: { executionId: executionId ?? "", action: "accept", content: "{}" },
}),
);
const first = yield* resume();
expect(first.isError, "the first resume completes the execution").not.toBe(true);
// MCP clients retry resume when a response is lost in transit. The
// retry must replay the same completed outcome — the production
// failure mode was "No paused execution" seconds after a successful
// resume.
const retry = yield* resume();
expect(retry.isError, "the duplicate resume is not an error").not.toBe(true);
expect(textOf(retry), "the duplicate resume returns the same completed result").toContain(
APPROVAL_TARGET_TOOL,
);
}).pipe(Effect.ensuring(closeQuietly(session)));
}).pipe(
Effect.ensuring(
api.policies
.remove({ params: { policyId: policy.id }, payload: { owner: "org" } })
.pipe(Effect.ignore),
),
);
}),
);
scenario(
"MCP sessions · a paused approval survives the client reconnecting and resumes",
{},
Effect.gen(function* () {
const target = yield* Target;
const { client } = yield* Api;
const mcp = yield* Mcp;
const identity = yield* target.newIdentity();
const api = yield* client(coreApi, identity);
const bearer = yield* mcp.mintBearer(emailOf(identity));
// Gate a built-in tool behind human approval; the org is fresh, so the
// gate affects no other scenario, but remove it anyway on every exit.
const policy = yield* api.policies.create({
payload: { owner: "org", pattern: APPROVAL_TARGET_TOOL, action: "require_approval" },
});
yield* Effect.gen(function* () {
const first = yield* Effect.promise(() => connectClient(target.mcpUrl, bearer));
const sessionId = first.transport.sessionId;
const paused = yield* Effect.promise(() =>
first.client.callTool({ name: "execute", arguments: { code: GATED_CODE } }),
).pipe(Effect.ensuring(closeQuietly(first)));
const pausedText = textOf(paused);
expect(pausedText, "the gated call pauses instead of completing").toContain(
"Execution paused",
);
const executionId = /\bexecutionId:\s*(\S+)/.exec(pausedText)?.[1];
expect(executionId, "the paused result carries the executionId").toEqual(expect.any(String));
// The user answers from a NEW client on the same session — the paused
// execution lives in the session, not the connection.
const second = yield* Effect.promise(() => connectClient(target.mcpUrl, bearer, sessionId));
const resumed = yield* Effect.promise(() =>
second.client.callTool({
name: "resume",
arguments: { executionId: executionId ?? "", action: "accept", content: "{}" },
}),
).pipe(Effect.ensuring(closeQuietly(second)));
expect(resumed.isError, "the resumed execution completes").not.toBe(true);
expect(textOf(resumed), "the gated tool's result comes back after approval").toContain(
APPROVAL_TARGET_TOOL,
);
}).pipe(
Effect.ensuring(
api.policies
.remove({ params: { policyId: policy.id }, payload: { owner: "org" } })
.pipe(Effect.ignore),
),
);
}),
);