118 lines · 4.4 KB · TypeScript
RawDownload
// ---------------------------------------------------------------------------// MCP owner isolation (v2)//// Replaces the v1 scope-stack per-user / cross-user isolation suites. v2 has no// scope stack: an org connection and a user connection on the same integration// produce DISTINCT tool addresses (`tools.<int>.org.<conn>.<tool>` vs// `tools.<int>.user.<conn>.<tool>`) and resolve to DISTINCT credential values.// This test pins that each address dials the server with its own connection's// value applied through the integration's header auth template.// --------------------------------------------------------------------------- import { describe, expect, it } from "@effect/vitest";import { Effect } from "effect"; import {  AuthTemplateSlug,  ConnectionName,  IntegrationSlug,  ToolAddress,  createExecutor,} from "@executor-js/sdk";import { makeTestConfig, memoryCredentialsPlugin } from "@executor-js/sdk/testing"; import { mcpPlugin } from "./plugin";import { makeEchoMcpServer, serveMcpServer } from "../testing"; const INTEG = IntegrationSlug.make("iso_mcp");const TEMPLATE = AuthTemplateSlug.make("bearer"); const createAuthRecordingMcpServer = () =>  makeEchoMcpServer({    name: "iso-test",    toolName: "whoami",    toolDescription: "Echoes a marker so the test can prove the invoke reached the server",    inputName: "marker",    text: (marker) => `ok:${marker}`,  }); const serveAuthRecordingMcpServer = serveMcpServer(createAuthRecordingMcpServer); describe("MCP owner isolation", () => {  it.effect("org and user connections resolve distinct values through the header template", () =>    Effect.gen(function* () {      const server = yield* serveAuthRecordingMcpServer;      const executor = yield* createExecutor(        makeTestConfig({ plugins: [memoryCredentialsPlugin(), mcpPlugin()] as const }),      );       // One integration with a Bearer header auth template; the connection's      // value is rendered as `Authorization: Bearer <value>`.      yield* executor.mcp.addServer({        name: "Shared MCP",        endpoint: server.url,        slug: String(INTEG),        auth: { kind: "header", headerName: "Authorization", prefix: "Bearer " },      });       yield* executor.connections.create({        owner: "org",        name: ConnectionName.make("shared"),        integration: INTEG,        template: TEMPLATE,        value: "token-org",      });      yield* executor.connections.create({        owner: "user",        name: ConnectionName.make("mine"),        integration: INTEG,        template: TEMPLATE,        value: "token-user",      });       // Both connections produced their own tool rows under distinct owners.      const tools = yield* executor.tools.list();      const orgTool = tools.find((t) => String(t.address) === "tools.iso_mcp.org.shared.whoami");      const userTool = tools.find((t) => String(t.address) === "tools.iso_mcp.user.mine.whoami");      expect(orgTool).toBeDefined();      expect(userTool).toBeDefined();       // Invoking the org address reaches the server with the org token.      const beforeOrg = (yield* server.requests).length;      const orgResult = yield* executor.execute(        ToolAddress.make("tools.iso_mcp.org.shared.whoami"),        { marker: "from-org" },        { onElicitation: "accept-all" },      );      expect(orgResult).toMatchObject({        ok: true,        data: { content: [{ type: "text", text: "ok:from-org" }] },      });      expect(        (yield* server.requests)          .slice(beforeOrg)          .some((request) => request.authorization === "Bearer token-org"),      ).toBe(true);       // Invoking the user address reaches the server with the user token —      // never the org token.      const beforeUser = (yield* server.requests).length;      const userResult = yield* executor.execute(        ToolAddress.make("tools.iso_mcp.user.mine.whoami"),        { marker: "from-user" },        { onElicitation: "accept-all" },      );      expect(userResult).toMatchObject({        ok: true,        data: { content: [{ type: "text", text: "ok:from-user" }] },      });      const userRequests = (yield* server.requests).slice(beforeUser);      expect(userRequests.some((request) => request.authorization === "Bearer token-user")).toBe(        true,      );      expect(userRequests.every((request) => request.authorization !== "Bearer token-org")).toBe(        true,      );    }),  );});