157 lines · 5.7 KB · TypeScript
RawDownload
// Cross-target: downstream MCP session continuity — the "an agent can use a// stateful MCP server" promise. Servers like Render's MCP key state by// Mcp-Session-Id (select_workspace, then every later call reads the// selection). If executor dials a fresh downstream connection per tool call,// every call lands in a brand-new session: select_workspace succeeds, and the// very next call reports no workspace selected. This scenario drives that// journey against a session-stateful MCP fixture and asserts state set by one// tool call is visible to the next call in the same execution.import { randomBytes } from "node:crypto"; import { expect } from "@effect/vitest";import { Effect, Predicate } from "effect";import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";import { composePluginApi } from "@executor-js/api/server";import { mcpHttpPlugin } from "@executor-js/plugin-mcp/api";import { serveMcpServer } from "@executor-js/plugin-mcp/testing";import { AuthTemplateSlug, ConnectionName, IntegrationSlug } from "@executor-js/sdk/shared"; import { scenario } from "../src/scenario";import { Api, Target } from "../src/services"; const api = composePluginApi([mcpHttpPlugin()] as const); const freshSlug = (prefix: string): string => `${prefix}_${randomBytes(4).toString("hex")}`; // A session-stateful MCP server, shaped like Render's workspace selection:// `select_workspace` stores the choice in the session (each MCP session gets// its own server instance from the factory, exactly like state keyed by// Mcp-Session-Id), and `get_workspace` answers from that same session state.const makeSessionStatefulMcpServer = () => () => {  const server = new McpServer(    { name: "session-stateful-test-server", version: "1.0.0" },    { capabilities: {} },  );  // Per-session: lives in this server instance, created per MCP session.  let selected: string | null = null;   server.registerTool(    "select_workspace",    {      description: "Selects the workspace for this MCP session",      inputSchema: {},    },    async () => {      selected = "ws-e2e";      return { content: [{ type: "text" as const, text: "selected:ws-e2e" }] };    },  );   server.registerTool(    "get_workspace",    {      description: "Returns the workspace selected earlier in this MCP session",      inputSchema: {},    },    async () => ({      content: [        {          type: "text" as const,          text: selected === null ? "no workspace selected" : `workspace:${selected}`,        },      ],    }),  );   return server;}; // One sandbox execution, two dependent tool calls — the smallest agent journey// that relies on downstream session state.const selectThenReadCode = (slug: string) => `const selected = await tools.${slug}.org.main.select_workspace({});const read = await tools.${slug}.org.main.get_workspace({});return JSON.stringify({ selected, read });`; scenario(  "MCP · session state set by one tool call is visible to the next call",  {},  Effect.scoped(    Effect.gen(function* () {      const target = yield* Target;      const { client: makeApiClient } = yield* Api;      const identity = yield* target.newIdentity();      const client = yield* makeApiClient(api, identity);      const slug = freshSlug("mcp_state");       const server = yield* serveMcpServer(makeSessionStatefulMcpServer());       yield* client.mcp.addServer({        payload: {          transport: "remote",          name: "Session-stateful MCP",          endpoint: server.url,          slug,          remoteTransport: "streamable-http",        },      });       yield* Effect.gen(function* () {        yield* client.connections.create({          payload: {            owner: "org",            name: ConnectionName.make("main"),            integration: IntegrationSlug.make(slug),            template: AuthTemplateSlug.make("none"),            value: "",          },        });         // Only observe the execution's own traffic, not discovery's.        yield* server.clearRequests;         const executed = yield* client.executions.execute({          payload: { code: selectThenReadCode(slug), autoApprove: true },        });        expect(executed.status, "the two-call execution completed").toBe("completed");         // THE promise: the selection made by the first call is what the second        // call reads. A per-call connection lands the second call in a fresh        // downstream session, which answers "no workspace selected".        expect(executed.text, "the first call selected the workspace").toContain("selected:ws-e2e");        expect(executed.text, "the second call sees the same session's selection").toContain(          "workspace:ws-e2e",        );         // Wire-level corroboration from the fixture's request ledger: every        // session-bound request of this execution carried ONE Mcp-Session-Id.        const sessionIds = new Set(          (yield* server.requests)            .map((request) => request.sessionId)            .filter(Predicate.isNotUndefined),        );        expect([...sessionIds].length, "both tool calls rode a single downstream MCP session").toBe(          1,        );      }).pipe(        Effect.ensuring(          Effect.gen(function* () {            yield* client.connections              .remove({                params: {                  owner: "org",                  integration: IntegrationSlug.make(slug),                  name: ConnectionName.make("main"),                },              })              .pipe(Effect.ignore);            yield* client.mcp              .removeServer({ params: { slug: IntegrationSlug.make(slug) } })              .pipe(Effect.ignore);          }),        ),      );    }),  ),);

Keyboard shortcuts

General

Show keyboard shortcuts
?
Focus the current search
/

Issues and pull requests

Open filters
f

Issues, pull requests and Inbox

Select next item
j↓
Select previous item
k↑
Open selected item
Enter
Dismiss filters or clear selection
Esc

Repository code

Go to file
t