feat(cloudflare): hibernate MCP Durable Object sessions#1095
Closed
aryasaatvik wants to merge 9 commits into
Closed
feat(cloudflare): hibernate MCP Durable Object sessions#1095aryasaatvik wants to merge 9 commits into
aryasaatvik wants to merge 9 commits into
Conversation
Expose pausedExecutionCount and hasPausedExecutions on ExecutionEngine so Cloudflare MCP session DOs can distinguish idle cleanup from paused continuations.
… exist Add shared alarm policy so idle sessions still destroy after 5 minutes, but sessions with in-memory paused executions reschedule a 10-minute lease instead of tearing down the runtime. Preserves the legacy unaddressable-alarm guard.
…ptile) The extend_paused_lease branch rescheduled the alarm unconditionally, so a session whose paused execution was never resumed (browser tab closed, client disconnect) would extend the lease forever and keep the Durable Object alive and billable indefinitely. Bound the lease to a single approval window: once idle exceeds SESSION_TIMEOUT_MS + PAUSED_EXECUTION_LEASE_MS (15 min), the session is torn down regardless of outstanding paused work. By that point the browser approval wait (tool-server, 10 min) has already timed out, so the paused execution is no longer resumable. idleMs grows across extensions because activity is not marked during the wait, so decideSessionAlarm stays pure. Also add the missing coverage: idle_within_timeout wins regardless of paused work, and the new cap forces destroy_idle_session.
Add host-owned paused execution hooks so Cloudflare MCP sessions can hold a bounded pending-approval lease, keep active POST requests alive until their response drains, and release hibernation blockers on resume or timeout. Also bound the approval wait window and resolve OpenAPI-backed annotations with point lookups so large specs do not reset the Durable Object before returning a pause envelope. This is the upstream port of the core runtime pieces from #56. The execution-history payload compaction from that PR is omitted because this upstream branch does not contain that plugin.
Remove MCP host console logging wrappers and rely on existing debug logging plus span annotations for pause lifecycle state. Keep Cloudflare Durable Object logs focused on exceptional boundary events by dropping happy-path active POST and pending approval lease messages.
Handle malformed active POST metadata without failing the Agent connection. Avoid overwriting a stored browser approval on timeout, and only resume timed-out executions directly when no MCP waiter is already registered.
Contributor
Author
|
Additional downstream context from my fork: this PR is the foundation I am using for the deployed Cloudflare self-hosted Executor instance. High-level shape: AST outline of the downstream host binding: McpSessionDO extends McpAgentSessionDOBase
-> openSessionDb()
-> resolveSessionMeta()
-> buildMcpServer()Fork permalinks:
Call stack for approval UI: The reason I see this as foundational is that it keeps the MCP transport/housing concern separate from the host-specific app. Once merged, more Cloudflare host features can build on the same hibernatable DO path without changing the MCP tool contract. |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Adds a hibernatable MCP Durable Object path for Cloudflare Workers by serving MCP transport requests through Cloudflare Agents while preserving Executor's existing MCP server construction and approval behavior.
The key lifecycle change is that browser approval pauses are treated as bounded Durable Object work: active MCP POST responses drain before hibernation, approval waits keep the object alive only for the approval window, and abandoned approvals are declined automatically.
Implementation Shape
Cloudflare Agent Durable Object
packages/hosts/cloudflare/src/mcp/agent-session-durable-object.tsThe Agent-backed Durable Object owns transport residency. It does not rebuild the MCP tool server contract. It gives Cloudflare the hibernation-aware runtime surface while keeping Executor's MCP server implementation in
@executor-js/host-mcp.MCP Host Pause Hooks
packages/hosts/mcp/src/tool-server.tsThe MCP host emits these hooks at the pause lifecycle boundaries that the Cloudflare Durable Object needs to make residency decisions. The hooks are host-facing lifecycle signals, not plugin APIs.
Execution Introspection
packages/core/execution/src/engine.tsCloudflare session alarm policy uses this read-only introspection to distinguish an execution that is still waiting on approval from one that can be allowed to hibernate.
Cloud App Wiring
Request Flow
Approval Pause Flow
Lifecycle Diagram
flowchart TD A["Worker /mcp request"] --> B["Cloudflare Agent bridge"] B --> C["McpAgentSessionDOBase"] C --> D["MCP Streamable HTTP transport"] D --> E["MCP tool server"] E --> F["Executor engine"] F --> G{"Approval required?"} G -->|No| H["Drain active POST response"] G -->|Yes| I["paused hook"] I --> J["Pending approval lease"] J --> K{"Decision before timeout?"} K -->|Approve or deny| L["resume execution"] K -->|Timeout| M["auto-deny"] L --> N["resuming and settled hooks"] M --> N N --> H H --> O["No active request ids"] O --> P["Durable Object can hibernate"]Scope Notes
Validation
bunx oxfmt --check apps/host-cloudflare/src/mcp/session-durable-object.ts apps/cloud/src/mcp/session-durable-object.ts packages/hosts/mcp/src/tool-server.ts packages/hosts/mcp/src/tool-server.test.ts packages/hosts/cloudflare/src/mcp/agent-session-durable-object.ts packages/hosts/cloudflare/src/mcp/session-alarm-policy.ts packages/plugins/openapi/src/sdk/backing.tsbunx oxlint --deny-warnings apps/host-cloudflare/src/mcp/session-durable-object.ts apps/cloud/src/mcp/session-durable-object.ts packages/hosts/mcp/src/tool-server.ts packages/hosts/mcp/src/tool-server.test.ts packages/hosts/cloudflare/src/mcp/agent-session-durable-object.ts packages/hosts/cloudflare/src/mcp/session-alarm-policy.ts packages/plugins/openapi/src/sdk/backing.tsbun run --cwd packages/hosts/mcp test src/tool-server.test.tsbun run --cwd packages/hosts/cloudflare test src/mcp/session-alarm-policy.test.tsbun run --cwd packages/plugins/openapi test src/sdk/store.test.ts src/sdk/plugin.test.tsbun run --cwd packages/hosts/mcp typecheckbun run --cwd packages/hosts/cloudflare typecheckbun run --cwd packages/plugins/openapi typecheckbun run --cwd apps/host-cloudflare typecheckbun run --cwd apps/cloud typecheckgit diff --check