102 lines · 4.5 KB · TypeScript
RawDownload
// Cloud-only (billing): every code execution run through the MCP session// Durable Object is metered to Autumn, exactly like the HTTP executor plane.// The MCP server is the PRIMARY execution surface (Claude/Cursor run code here,// not over /api), so if the DO doesn't bill, the bulk of real usage silently// never reaches the meter — the regression this pins.//// Black-box and end-to-end: drive a real @modelcontextprotocol/sdk client over// StreamableHTTP (the exact transport an MCP client uses) against the production// workerd + McpSessionDO topology, then read the usage the server ACTUALLY// tracked from the Autumn ledger. The execution's own response can't prove it// was billed — metering is fire-and-forget, decoupled from the user-facing// result — so only the meter is the source of truth.import { expect } from "@effect/vitest";import { Effect } from "effect";import { Client } from "@modelcontextprotocol/sdk/client/index.js";import { StreamableHTTPClientTransport } from "@modelcontextprotocol/sdk/client/streamableHttp.js"; import { scenario } from "../src/scenario";import { Autumn, Billing, Mcp, Target } from "../src/services";import type { Identity } from "../src/target"; const emailOf = (identity: Identity): string => identity.credentials?.email ?? identity.label; /** The org the bearer is scoped to — the Autumn customer id `trackExecution` *  meters against — read from the JWT's public claims. */const orgIdOf = (bearer: string): string => {  const claims = JSON.parse(Buffer.from(bearer.split(".")[1] ?? "", "base64url").toString()) as {    readonly org_id?: string;  };  if (!claims.org_id) throw new Error("orgIdOf: bearer carries no org_id claim");  return claims.org_id;}; 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 RUNS = 3; scenario(  "Billing · every MCP execution is metered to Autumn, one unit per run",  { timeout: 180_000 },  Effect.gen(function* () {    // Gates: billing is enforced here AND the Autumn ledger is observable    // (the suite booted the emulator). Yield before any work so a target    // missing either capability skips cleanly.    yield* Billing;    const autumn = yield* Autumn;    const target = yield* Target;    const mcp = yield* Mcp;     const identity = yield* target.newIdentity();    const bearer = yield* mcp.mintBearer(emailOf(identity));    const customerId = orgIdOf(bearer);     // A fresh org has metered nothing yet — the baseline the run is measured    // against, and a guard that we're reading this customer's ledger in    // isolation from every other scenario's executions.    const before = yield* autumn.usageEvents({ customerId, featureId: "executions" });    expect(before.length, "a brand-new org starts with zero metered executions").toBe(0);     // A real MCP client over StreamableHTTP — the production code path.    const client = new Client(      { name: "executor-e2e-metering", version: "0.0.1" },      { capabilities: {} },    );    const transport = new StreamableHTTPClientTransport(new URL(target.mcpUrl), {      requestInit: { headers: { authorization: `Bearer ${bearer}` } },    });    const connected = yield* Effect.promise(() => client.connect(transport).then(() => client));     yield* Effect.gen(function* () {      // Run a handful of executions; each is one billable unit.      for (let i = 1; i <= RUNS; i++) {        const result = yield* Effect.promise(() =>          connected.callTool({ name: "execute", arguments: { code: `return ${i} * 2;` } }),        );        expect(result.isError, `execution ${i} succeeds`).not.toBe(true);        expect(textOf(result), `execution ${i} returns its value`).toContain(String(i * 2));      }       // The meter is the source of truth. Tracking is fire-and-forget, so poll      // the ledger until all runs have landed.      const after = yield* autumn.expectUsage({        customerId,        featureId: "executions",        count: RUNS,      });       expect(        after.length,        "exactly one 'executions' usage event per run — no over- or under-counting",      ).toBe(RUNS);      expect(        after.every((event) => event.value === 1),        "each run meters a single unit",      ).toBe(true);    }).pipe(Effect.ensuring(Effect.promise(() => connected.close().catch(() => undefined))));  }),);