CF rejects deleting McpSessionDO while MCP_SESSION still binds it (code 10061), and won't reuse a deleted class name. Rename the concrete class + all refs to McpSessionDOSqlite, repoint the binding, and have v2 delete the old KV McpSessionDO + create the new SQLite class. Verified the built worker exports McpSessionDOSqlite and nothing named McpSessionDO.
Unified · Split
| @@ -13,7 +13,7 @@ import { ApiKeyService } from "./auth/api-keys"; · 12 unmodified lines | ||
| 13 | 13 | import { cloudIdentityFailureStrategy, workosIdentityLayer } from "./auth/workos-auth-provider"; |
| 14 | 14 | import { DbService } from "./db/db"; |
| 15 | 15 | import { cloudMcpAuth } from "./mcp"; |
| 16 | −import { McpSessionDO } from "./mcp/session-durable-object"; | |
| 16 | +import { McpSessionDOSqlite } from "./mcp/session-durable-object"; | |
| 17 | 17 | import { ErrorCaptureLive } from "./observability"; |
| 18 | 18 | import { AutumnService } from "./extensions/billing/service"; |
| 19 | 19 | import { |
| @@ -112,7 +112,7 @@ const { appLayer, toWebHandler, mcpExport } = ExecutorApp.make({ · 92 unmodified lines | ||
| 112 | 112 | failure: cloudIdentityFailureStrategy, |
| 113 | 113 | // The MCP session Durable Object class — a top-level Workers export a Layer |
| 114 | 114 | // can't return; surfaced so `server.ts` can re-export it. |
| 115 | − mcpExport: McpSessionDO, | |
| 115 | + mcpExport: McpSessionDOSqlite, | |
| 116 | 116 | }, |
| 117 | 117 | // The long-lived (boot-scoped) context provideMerge'd under everything: the |
| 118 | 118 | // WorkOS control plane (the raw `WorkOSClient` + `ApiKeyService` the per-request |
| @@ -135,7 +135,7 @@ const { appLayer, toWebHandler, mcpExport } = ExecutorApp.make({ · 16 unmodified lines | ||
| 135 | 135 | requestScoped: RequestScopedServicesLive, |
| 136 | 136 | }); |
| 137 | 137 | |
| 138 | −export { McpSessionDO }; | |
| 138 | +export { McpSessionDOSqlite }; | |
| 139 | 139 | |
| 140 | 140 | export const CloudAppLayer = appLayer; |
| 141 | 141 | export const cloudMcpExport = mcpExport; |
| @@ -15,7 +15,7 @@ import { · 14 unmodified lines | ||
| 15 | 15 | import type { McpSessionProps } from "@executor-js/cloudflare/mcp/agent-durable-object"; |
| 16 | 16 | |
| 17 | 17 | import { cloudMcpAuth } from "./auth-provider"; |
| 18 | −import { McpSessionDO } from "./session-durable-object"; | |
| 18 | +import { McpSessionDOSqlite } from "./session-durable-object"; | |
| 19 | 19 | |
| 20 | 20 | interface McpAgentSessionStub { |
| 21 | 21 | readonly validateMcpSessionOwner: (identity: { |
| @@ -111,7 +111,7 @@ const propsForPrincipal = ( · 89 unmodified lines | ||
| 111 | 111 | }); |
| 112 | 112 | |
| 113 | 113 | export const makeCloudMcpAgentHandler = () => { |
| 114 | − const serve = McpSessionDO.serve("/mcp", { | |
| 114 | + const serve = McpSessionDOSqlite.serve("/mcp", { | |
| 115 | 115 | binding: "MCP_SESSION", |
| 116 | 116 | transport: "streamable-http", |
| 117 | 117 | }); |
| @@ -157,7 +157,7 @@ const makeSessionServices = (dbHandle: CloudSessionDbHandle) => { · 156 unmodified lines | ||
| 157 | 157 | // Durable Object |
| 158 | 158 | // --------------------------------------------------------------------------- |
| 159 | 159 | |
| 160 | −export class McpSessionDO extends McpAgentSessionDOBase<Env, CloudSessionDbHandle> { | |
| 160 | +export class McpSessionDOSqlite extends McpAgentSessionDOBase<Env, CloudSessionDbHandle> { | |
| 161 | 161 | protected override openSessionDb(): CloudSessionDbHandle { |
| 162 | 162 | return makeDbHandle({ |
| 163 | 163 | idleTimeout: LONG_LIVED_DB_IDLE_TIMEOUT_SECONDS, |
| @@ -181,7 +181,7 @@ export class McpSessionDO extends McpAgentSessionDOBase<Env, CloudSessionDbHandl · 17 unmodified lines | ||
| 181 | 181 | elicitationMode: token.elicitationMode, |
| 182 | 182 | } satisfies SessionMeta; |
| 183 | 183 | }).pipe( |
| 184 | − Effect.withSpan("McpSessionDO.resolveSessionMeta"), | |
| 184 | + Effect.withSpan("McpSessionDOSqlite.resolveSessionMeta"), | |
| 185 | 185 | Effect.provide(makeSessionServices(dbHandle)), |
| 186 | 186 | Effect.ensuring(Effect.promise(() => dbHandle.end())), |
| 187 | 187 | // oxlint-disable-next-line executor/no-effect-escape-hatch -- boundary: a vanished org is a defect; the worker already verified the bearer |
| @@ -208,7 +208,7 @@ export class McpSessionDO extends McpAgentSessionDOBase<Env, CloudSessionDbHandl · 20 unmodified lines | ||
| 208 | 208 | // billing service degrades to a no-op tracker, so this stays inert in |
| 209 | 209 | // cloud dev/preview environments that run without a billing backend. |
| 210 | 210 | Effect.provide(CloudMeteredExecutionStackLayer.pipe(Layer.provide(AutumnService.Default))), |
| 211 | − Effect.withSpan("McpSessionDO.makeExecutionStack"), | |
| 211 | + Effect.withSpan("McpSessionDOSqlite.makeExecutionStack"), | |
| 212 | 212 | ); |
| 213 | 213 | // Build the description here so `executor.connections.list()` stays under |
| 214 | 214 | // the DO startup span and the MCP SDK receives a concrete string instead |
| @@ -235,10 +235,10 @@ export class McpSessionDO extends McpAgentSessionDOBase<Env, CloudSessionDbHandl · 20 unmodified lines | ||
| 235 | 235 | }), |
| 236 | 236 | } |
| 237 | 237 | : { mode: sessionElicitationMode }, |
| 238 | − }).pipe(Effect.withSpan("McpSessionDO.createExecutorMcpServer")); | |
| 238 | + }).pipe(Effect.withSpan("McpSessionDOSqlite.createExecutorMcpServer")); | |
| 239 | 239 | return { mcpServer, engine } satisfies BuiltMcpServer; |
| 240 | 240 | }).pipe( |
| 241 | − Effect.withSpan("McpSessionDO.buildMcpServer"), | |
| 241 | + Effect.withSpan("McpSessionDOSqlite.buildMcpServer"), | |
| 242 | 242 | Effect.provide(makeSessionServices(dbHandle)), |
| 243 | 243 | // oxlint-disable-next-line executor/no-effect-escape-hatch -- boundary: runtime-build failures surface as the base's tapCause/cleanup defect |
| 244 | 244 | Effect.orDie, |
| @@ -12,7 +12,7 @@ import handler from "@tanstack/react-start/server-entry"; · 11 unmodified lines | ||
| 12 | 12 | import { isAppOwnedPath } from "./app-paths"; |
| 13 | 13 | import { makeCloudMcpAgentHandler } from "./mcp/agent-handler"; |
| 14 | 14 | import { classifyMcpPath, prepareMcpOrgScope } from "./mcp/mount"; |
| 15 | −import { McpSessionDO as McpSessionDOBase } from "./mcp/session-durable-object"; | |
| 15 | +import { McpSessionDOSqlite as McpSessionDOBase } from "./mcp/session-durable-object"; | |
| 16 | 16 | import { browserTracesResponse } from "./observability/browser-traces"; |
| 17 | 17 | import { flushTracerProvider, installTracerProvider } from "./observability/telemetry"; |
| 18 | 18 | |
| @@ -41,7 +41,7 @@ const sentryOptions = (env: Env) => ({ · 22 unmodified lines | ||
| 41 | 41 | // not a global fetch wrapper. |
| 42 | 42 | // --------------------------------------------------------------------------- |
| 43 | 43 | |
| 44 | −export const McpSessionDO = Sentry.instrumentDurableObjectWithSentry( | |
| 44 | +export const McpSessionDOSqlite = Sentry.instrumentDurableObjectWithSentry( | |
| 45 | 45 | sentryOptions, |
| 46 | 46 | McpSessionDOBase, |
| 47 | 47 | ); |
| @@ -20,16 +20,17 @@ · 19 unmodified lines | ||
| 20 | 20 | "bindings": [ |
| 21 | 21 | { |
| 22 | 22 | "name": "MCP_SESSION", |
| 23 | − "class_name": "McpSessionDO", | |
| 23 | + "class_name": "McpSessionDOSqlite", | |
| 24 | 24 | }, |
| 25 | 25 | ], |
| 26 | 26 | }, |
| 27 | 27 | // The MCP session DO moved to the Cloudflare Agents (`McpAgent`) base, which |
| 28 | − // stores state in SQLite. `McpSessionDO` was originally created with the | |
| 28 | + // stores state in SQLite. The original `McpSessionDO` was created with the | |
| 29 | 29 | // key-value backend (`new_classes`), and Cloudflare cannot convert a live class |
| 30 | − // to SQLite in place. Session state is ephemeral, so v2 deletes the KV class and | |
| 31 | − // v3 recreates the same name on the SQLite backend. Keeping the name avoids any | |
| 32 | − // binding/export rename. | |
| 30 | + // to SQLite in place, nor delete a class while a binding still references it. | |
| 31 | + // Session state is ephemeral, so v2 repoints the `MCP_SESSION` binding to a new | |
| 32 | + // SQLite-backed class (`McpSessionDOSqlite`) and deletes the old KV `McpSessionDO` | |
| 33 | + // (now unreferenced). The worker exports `McpSessionDOSqlite`, not `McpSessionDO`. | |
| 33 | 34 | "migrations": [ |
| 34 | 35 | { |
| 35 | 36 | "tag": "v1", |
| @@ -38,10 +39,7 @@ · 2 unmodified lines | ||
| 38 | 39 | { |
| 39 | 40 | "tag": "v2", |
| 40 | 41 | "deleted_classes": ["McpSessionDO"], |
| 41 | − }, | |
| 42 | − { | |
| 43 | − "tag": "v3", | |
| 44 | − "new_sqlite_classes": ["McpSessionDO"], | |
| 42 | + "new_sqlite_classes": ["McpSessionDOSqlite"], | |
| 45 | 43 | }, |
| 46 | 44 | ], |
| 47 | 45 | "services": [ |