// 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)))); }), );