Skip to content

fix(cloud): back MCP session Durable Object with SQLite#1189

Merged
RhysSullivan merged 3 commits into
mainfrom
fix-cloud-mcp-do-sqlite
Jun 28, 2026
Merged

fix(cloud): back MCP session Durable Object with SQLite#1189
RhysSullivan merged 3 commits into
mainfrom
fix-cloud-mcp-do-sqlite

Conversation

@RhysSullivan

Copy link
Copy Markdown
Owner

Incident

After #1178 deployed, all executor.sh /mcp sessions 500'd with:

SqlError: This Durable Object is not backed by SQLite storage, so the SQL API is not available... an already-deployed class cannot be converted to SQLite.

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. But apps/cloud's McpSessionDO class was originally deployed on the key-value backend (new_classes), and Cloudflare cannot convert a live class to SQLite in place. apps/host-cloudflare was already on new_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):

  • migration v2: deleted_classes: ["McpSessionDO"] + new_sqlite_classes: ["McpSessionDOSqlite"]
  • MCP_SESSION binding repoints to McpSessionDOSqlite
  • the worker's exported DO class is renamed to match

The DO is addressed by binding + idFromName, so the rename is transparent at runtime. No durable data lost (only in-flight sessions, which reconnect).

Validation

  • typecheck clean; class export ↔ class_name ↔ migration all aligned
  • Migration shape follows Cloudflare's documented KV→SQLite replacement path
  • The PR's Workers Build deploys to the e2e account (not prod), which exercises the migration in isolation before this lands on main

Rollback 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.

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.
@RhysSullivan RhysSullivan changed the title fix(cloud): back MCP session Durable Object with SQLite (prod 500 hotfix) fix(cloud): back MCP session Durable Object with SQLite Jun 28, 2026
@cloudflare-workers-and-pages

cloudflare-workers-and-pages Bot commented Jun 28, 2026

Copy link
Copy Markdown

Deploying with  Cloudflare Workers  Cloudflare Workers

The latest updates on your project. Learn more about integrating Git with Workers.

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

@cloudflare-workers-and-pages

cloudflare-workers-and-pages Bot commented Jun 28, 2026

Copy link
Copy Markdown

Deploying with  Cloudflare Workers  Cloudflare Workers

The latest updates on your project. Learn more about integrating Git with Workers.

Status Name Latest Commit Updated (UTC)
❌ Deployment failed
View logs
executor-cloud d998f49 Jun 28 2026, 09:34 PM

@greptile-apps

greptile-apps Bot commented Jun 28, 2026

Copy link
Copy Markdown

Greptile Summary

This PR fixes a production incident where all /mcp sessions on executor.sh returned 500 errors because the McpSessionDO Durable Object class was created on the key-value backend but now extends McpAgent, which requires SQLite storage — a conversion Cloudflare cannot perform in place on a live class.

  • Wrangler migration fix: Adds v2 (deleted_classes: ["McpSessionDO"]) and v3 (new_sqlite_classes: ["McpSessionDO"]) migration entries that atomically delete the old KV-backed class and recreate it under the same name with SQLite storage, keeping the binding and export unchanged.
  • Package.json cleanup: Moves "access": "public" to the top of the publishConfig object in 20 packages — a cosmetic reordering with no functional impact.

Confidence Score: 5/5

Safe 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

Filename Overview
apps/cloud/wrangler.jsonc Adds v2 (delete KV class) and v3 (recreate as SQLite) DO migrations. The class name, binding, and server.ts export all consistently use McpSessionDO throughout, so a fresh deploy applies all three steps in sequence without any naming mismatch.
packages/core/cli/package.json Moves access: "public" to the top of publishConfig — cosmetic reordering, no functional change.
packages/core/sdk/package.json Same cosmetic publishConfig reordering as the other package.json files.

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)
Loading
%%{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)
Loading

Reviews (3): Last reviewed commit: "fix(cloud): recreate MCP session DO as S..." | Re-trigger Greptile

Comment thread apps/cloud/wrangler.jsonc
@github-actions

github-actions Bot commented Jun 28, 2026

Copy link
Copy Markdown
Contributor

Cloudflare preview

Torn down — the PR is closed.

@pkg-pr-new

pkg-pr-new Bot commented Jun 28, 2026

Copy link
Copy Markdown

Open in StackBlitz

@executor-js/cli

npm i https://pkg.pr.new/@executor-js/cli@1189

@executor-js/config

npm i https://pkg.pr.new/@executor-js/config@1189

@executor-js/execution

npm i https://pkg.pr.new/@executor-js/execution@1189

@executor-js/sdk

npm i https://pkg.pr.new/@executor-js/sdk@1189

@executor-js/codemode-core

npm i https://pkg.pr.new/@executor-js/codemode-core@1189

@executor-js/runtime-quickjs

npm i https://pkg.pr.new/@executor-js/runtime-quickjs@1189

@executor-js/plugin-file-secrets

npm i https://pkg.pr.new/@executor-js/plugin-file-secrets@1189

@executor-js/plugin-graphql

npm i https://pkg.pr.new/@executor-js/plugin-graphql@1189

@executor-js/plugin-keychain

npm i https://pkg.pr.new/@executor-js/plugin-keychain@1189

@executor-js/plugin-mcp

npm i https://pkg.pr.new/@executor-js/plugin-mcp@1189

@executor-js/plugin-onepassword

npm i https://pkg.pr.new/@executor-js/plugin-onepassword@1189

@executor-js/plugin-openapi

npm i https://pkg.pr.new/@executor-js/plugin-openapi@1189

executor

npm i https://pkg.pr.new/executor@1189

commit: d998f49

…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.
@RhysSullivan RhysSullivan merged commit 2a8b1e0 into main Jun 28, 2026
15 of 16 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant