fix(cloud): back MCP session Durable Object with SQLite#1189
Conversation
The MCP session DO now extends the Cloudflare Agents `McpAgent` base, which stores its state in SQLite. apps/cloud still had the original `McpSessionDO` class on the key-value backend (`new_classes`), and Cloudflare cannot convert a live class to SQLite in place, so every session 500'd with "This Durable Object is not backed by SQLite storage". Session state is ephemeral, so delete the old KV class and create a new SQLite-backed class (`McpSessionDOSqlite`) in migration v2, repointing the `MCP_SESSION` binding. The DO is addressed by binding + idFromName, so the rename is transparent at runtime. host-cloudflare was already on `new_sqlite_classes`, so only cloud was affected.
Deploying with
|
| Status | Name | Latest Commit | Preview URL | Updated (UTC) |
|---|---|---|---|---|
| ✅ Deployment successful! View logs |
executor-marketing | d998f49 | Commit Preview URL Branch Preview URL |
Jun 28 2026, 09:34 PM |
Deploying with
|
| Status | Name | Latest Commit | Updated (UTC) |
|---|---|---|---|
| ❌ Deployment failed View logs |
executor-cloud | d998f49 | Jun 28 2026, 09:34 PM |
Greptile SummaryThis PR fixes a production incident where all
Confidence Score: 5/5Safe to merge. The migration sequence is well-reasoned, the class name is consistent across the binding, export, and all three migration entries, and session state is explicitly ephemeral so the delete-and-recreate path loses nothing durable. The wrangler.jsonc migration follows the documented Cloudflare pattern for KV-to-SQLite replacement: delete the old class in one step, recreate under the same name with the new backend in the next. Both steps are applied in the same wrangler deploy, so there is no window where the binding points to a missing class. The server.ts export (McpSessionDO), the durable_objects binding (class_name: McpSessionDO), and v3's new_sqlite_classes all match. The 20 package.json changes are a cosmetic reordering with no semantic effect. No files require special attention. The key file is apps/cloud/wrangler.jsonc, and the migration shape looks correct. Important Files Changed
Sequence Diagram%%{init: {'theme': 'neutral'}}%%
sequenceDiagram
participant D as wrangler deploy
participant CF as Cloudflare Workers
participant B as MCP_SESSION binding
Note over CF: Before this PR (prod state)
CF->>CF: v1 applied: McpSessionDO (KV backend)
B->>CF: "class_name = McpSessionDO (KV)"
Note over D,CF: This PR deploys
D->>CF: Apply v2: deleted_classes ["McpSessionDO"]
CF->>CF: KV-backed class removed
D->>CF: Apply v3: new_sqlite_classes ["McpSessionDO"]
CF->>CF: SQLite-backed class registered
B->>CF: "class_name = McpSessionDO (SQLite)"
Note over CF: After deploy (target state)
CF->>CF: McpSessionDO runs on SQLite (McpAgent compatible)
%%{init: {'theme': 'base', 'themeVariables': {"darkMode": true, "background": "#0d1117", "primaryColor": "#21262d", "primaryTextColor": "#e6edf3", "primaryBorderColor": "#8b949e", "lineColor": "#8b949e", "textColor": "#e6edf3", "edgeLabelBackground": "#161b22", "actorBkg": "#21262d", "actorBorder": "#8b949e", "actorTextColor": "#e6edf3", "actorLineColor": "#8b949e", "signalColor": "#8b949e", "signalTextColor": "#e6edf3", "noteBkgColor": "#373320", "noteBorderColor": "#d4a72c", "noteTextColor": "#f0e6c0", "labelBoxBkgColor": "#21262d", "labelBoxBorderColor": "#8b949e", "labelTextColor": "#e6edf3", "loopTextColor": "#e6edf3", "activationBkgColor": "#30363d", "activationBorderColor": "#8b949e"}}}%%
sequenceDiagram
participant D as wrangler deploy
participant CF as Cloudflare Workers
participant B as MCP_SESSION binding
Note over CF: Before this PR (prod state)
CF->>CF: v1 applied: McpSessionDO (KV backend)
B->>CF: "class_name = McpSessionDO (KV)"
Note over D,CF: This PR deploys
D->>CF: Apply v2: deleted_classes ["McpSessionDO"]
CF->>CF: KV-backed class removed
D->>CF: Apply v3: new_sqlite_classes ["McpSessionDO"]
CF->>CF: SQLite-backed class registered
B->>CF: "class_name = McpSessionDO (SQLite)"
Note over CF: After deploy (target state)
CF->>CF: McpSessionDO runs on SQLite (McpAgent compatible)
Reviews (3): Last reviewed commit: "fix(cloud): recreate MCP session DO as S..." | Re-trigger Greptile |
Cloudflare previewTorn down — the PR is closed. |
@executor-js/cli
@executor-js/config
@executor-js/execution
@executor-js/sdk
@executor-js/codemode-core
@executor-js/runtime-quickjs
@executor-js/plugin-file-secrets
@executor-js/plugin-graphql
@executor-js/plugin-keychain
@executor-js/plugin-mcp
@executor-js/plugin-onepassword
@executor-js/plugin-openapi
executor
commit: |
…lass rename) Same-name delete+recreate (v2 delete, v3 new_sqlite) keeps the McpSessionDO binding and worker export unchanged, avoiding the rename that left the deleted KV class still exported. Reverts the server.ts/app.ts rename from the prior attempt; the only code-level change is the wrangler migration.
Incident
After #1178 deployed, all
executor.sh/mcpsessions 500'd with:Rolled back via
wrangler rollback. This is the fix-forward.Root cause
The MCP session DO now extends Cloudflare Agents'
McpAgent, which stores state in SQLite. Butapps/cloud'sMcpSessionDOclass was originally deployed on the key-value backend (new_classes), and Cloudflare cannot convert a live class to SQLite in place.apps/host-cloudflarewas already onnew_sqlite_classes, so only cloud was affected — and neither CI nor the preview caught it because their classes were SQLite from first creation; this only manifests against prod's pre-existing KV class.Fix
DO migrations are forward-only and KV→SQLite conversion isn't supported, so (session state is ephemeral):
v2:deleted_classes: ["McpSessionDO"]+new_sqlite_classes: ["McpSessionDOSqlite"]MCP_SESSIONbinding repoints toMcpSessionDOSqliteThe DO is addressed by binding +
idFromName, so the rename is transparent at runtime. No durable data lost (only in-flight sessions, which reconnect).Validation
class_name↔ migration all alignedRollback note
Once v2 deploys to prod it deletes the old KV class — rolling back to the pre-#1178 KV code is no longer clean after this. Forward-only from here. The new SQLite DO is the same code already validated under load on the preview.