Skip to content

cloudflare self-host: browser approval + a Cloudflare e2e target - #1016

Merged
RhysSullivan merged 1 commit into
mainfrom
claude/mcp-browser-approval-cloudflare
Jun 14, 2026
Merged

cloudflare self-host: browser approval + a Cloudflare e2e target#1016
RhysSullivan merged 1 commit into
mainfrom
claude/mcp-browser-approval-cloudflare

Conversation

@RhysSullivan

@RhysSullivan RhysSullivan commented Jun 14, 2026

Copy link
Copy Markdown
Collaborator

What

The Cloudflare self-host Durable Object base already owned the approval store and the paused/resume RPCs, but its buildMcpServer ignored the session's elicitation mode (model-mode only) and the worker served no approval endpoints — so a gated MCP action could never be approved in the browser.

  • host-cloudflare: buildMcpServer wires browser elicitation mode + approval URL (origin from the create request, captured by the DO base) + the base's approval store, mirroring the hosted app. The worker mounts a gated /api/mcp-sessions/* handler that routes the paused (GET) + resume (POST) calls to the owning session's Durable Object RPCs.
  • e2e: a new cloudflare target — the real worker on workerd via wrangler dev (Miniflare) with a local D1 + R2 and dev-auth, the console SPA built and served as static assets. The cross-target browser-approval scenario (approve + decline) now runs green here too, driving the rendered resume page against the worker.

Browser approval is now covered end-to-end on all three deployment shapes.

Notes

  • The cloudflare e2e project is scoped to the browser-approval scenario for now — the rest of the cross-target suite is not yet validated against the worker.

Testing

  • typecheck / format:check / lint clean.
  • Full host-cloudflare suite green, including the wrangler dev worker integration test.
  • Cloudflare browser-approval e2e (approve + decline) green against the real worker.

Third of a three-PR stack.

Stack

  1. host-mcp: shared browser-approval primitives + cloud browser e2e #1014
  2. self-host: browser approval of gated MCP actions #1015
  3. cloudflare self-host: browser approval + a Cloudflare e2e target #1016 👈 current

@RhysSullivan
RhysSullivan force-pushed the claude/mcp-browser-approval-selfhost branch from a595fe9 to 69e930c Compare June 14, 2026 03:12
@cloudflare-workers-and-pages

cloudflare-workers-and-pages Bot commented Jun 14, 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 successful!
View logs
executor-cloud d4f3e6f Jun 14 2026, 06:10 AM

@cloudflare-workers-and-pages

cloudflare-workers-and-pages Bot commented Jun 14, 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 d4f3e6f Commit Preview URL

Branch Preview URL
Jun 14 2026, 06:09 AM

@github-actions

github-actions Bot commented Jun 14, 2026

Copy link
Copy Markdown
Contributor

Cloudflare preview

Torn down — the PR is closed.

@greptile-apps

greptile-apps Bot commented Jun 14, 2026

Copy link
Copy Markdown

Greptile Summary

This PR wires browser-approval support into the Cloudflare self-host worker and adds a new cloudflare e2e target that validates the full approve/decline flow against a real wrangler dev instance.

  • host-cloudflare/mcp: buildMcpServer now forwards elicitationMode and browserApprovalStore from the DO base to createExecutorMcpServer; a new makeCloudflareApprovalHandler builds an Access-gated web handler that routes paused-execution reads (GET) and resume decisions (POST) to the owning session's DO RPCs, mounted at /api/mcp-sessions/*.
  • e2e infrastructure: A new cloudflare vitest project, global-setup, boot recipe, and target factory mirror the existing selfhost shape — wrangler dev on workerd with local D1/R2 and dev-auth, scoped to the browser-approval scenario for now.

Confidence Score: 5/5

Safe to merge — the new approval handler and e2e target are additive, self-contained, and mirror well-tested existing patterns in the codebase.

The base-class resolveAndStoreSessionMeta correctly merges token.webOrigin after the host's resolveSessionMeta returns, so the approval URL origin is populated on cold restores even though the host subclass doesn't forward it. The globalSetup for the cloudflare project is auto-generated by the project() helper, so the wrangler boot is wired without any extra configuration. The only pre-existing concern (unhandled throw from idFromString) was already flagged in a prior review thread; no new unresolved defects were introduced here.

apps/host-cloudflare/src/mcp/index.ts — the approval handler's async body has no top-level try/catch, which was called out in a previous review comment; the decodeURIComponent calls before the stubFor invocation share the same unguarded surface.

Important Files Changed

Filename Overview
apps/host-cloudflare/src/mcp/index.ts Adds the approval handler: Access verification, DO stub routing, JSON serialization. No try/catch around the async body — a malformed session ID (idFromString) was already flagged in a prior thread; the decodeURIComponent calls share the same unhandled-throw surface.
apps/host-cloudflare/src/mcp/session-durable-object.ts Wires elicitationMode and browserApprovalStore into createExecutorMcpServer. webOrigin fallback chain is correct: the base class merges token.webOrigin into sessionMeta after resolveSessionMeta returns, so sessionMeta.webOrigin is populated for cold restores.
apps/host-cloudflare/src/app.ts Mounts the approval handler at /api/mcp-sessions/* via HttpRouter extension — clean one-liner addition.
e2e/setup/cloudflare.boot.ts New boot recipe: builds the SPA (optional skip), starts wrangler dev with dev-auth vars, waits for health check. Follows the same pattern as other boot scripts; logFile and skipBuild are both wired correctly.
e2e/setup/cloudflare.globalsetup.ts Standard port-claim + boot/teardown globalsetup, mirrors the selfhost pattern correctly. Supports E2E_CLOUDFLARE_URL attach mode.
e2e/targets/cloudflare.ts New target factory with correct capability set (api, browser, mcp-oauth) and single fixed dev-admin identity — matches the single-tenant model described in the PR.
e2e/vitest.config.ts Adds cloudflare project scoped to browser-approval.test.ts + cloudflare/**. The project() helper auto-generates globalSetup from the project name, so setup/cloudflare.globalsetup.ts is wired correctly.

Sequence Diagram

sequenceDiagram
    participant Browser as Console Browser
    participant Worker as Cloudflare Worker
    participant Access as Access Verifier
    participant DO as McpSessionDO

    Note over Worker: /api/mcp-sessions/* mounted via HttpRouter

    Browser->>Worker: "GET /api/mcp-sessions/{sessionId}/executions/{execId}"
    Worker->>Access: verify(request)
    Access-->>Worker: "principal | null"
    alt not authenticated
        Worker-->>Browser: 401 Unauthorized
    end
    Worker->>DO: idFromString(sessionId) stub
    Worker->>DO: getPausedExecutionForApproval(execId, owner)
    DO-->>Worker: status text structured
    Worker-->>Browser: 200 text structured

    Browser->>Worker: "POST /api/mcp-sessions/{sessionId}/executions/{execId}/resume"
    Worker->>Access: verify(request)
    Access-->>Worker: principal
    Worker->>Worker: decodeResumeResponse(body)
    Worker->>DO: resumeExecutionForApproval(execId, owner, response)
    DO->>DO: store response, signal waiter Deferred
    DO-->>Worker: status ok executionStatus text structured
    Worker-->>Browser: 200 status text structured isError
Loading

Reviews (4): Last reviewed commit: "cloudflare self-host: browser approval +..." | Re-trigger Greptile

Comment thread apps/host-cloudflare/src/mcp/index.ts
@pkg-pr-new

pkg-pr-new Bot commented Jun 14, 2026

Copy link
Copy Markdown

Open in StackBlitz

@executor-js/cli

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

@executor-js/config

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

@executor-js/execution

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

@executor-js/sdk

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

@executor-js/codemode-core

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

@executor-js/runtime-quickjs

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

@executor-js/plugin-file-secrets

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

@executor-js/plugin-graphql

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

@executor-js/plugin-keychain

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

@executor-js/plugin-mcp

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

@executor-js/plugin-onepassword

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

@executor-js/plugin-openapi

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

executor

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

commit: d4f3e6f

@RhysSullivan
RhysSullivan force-pushed the claude/mcp-browser-approval-cloudflare branch from c34b4f5 to 8156239 Compare June 14, 2026 03:27
@RhysSullivan
RhysSullivan force-pushed the claude/mcp-browser-approval-selfhost branch from 69e930c to 14eb2c5 Compare June 14, 2026 05:47
@RhysSullivan
RhysSullivan force-pushed the claude/mcp-browser-approval-cloudflare branch from 8156239 to 9763604 Compare June 14, 2026 05:47
@RhysSullivan
RhysSullivan force-pushed the claude/mcp-browser-approval-selfhost branch from 14eb2c5 to 11c1a17 Compare June 14, 2026 05:51
@RhysSullivan
RhysSullivan force-pushed the claude/mcp-browser-approval-cloudflare branch from 9763604 to 8ff9ad6 Compare June 14, 2026 05:51
@RhysSullivan
RhysSullivan force-pushed the claude/mcp-browser-approval-selfhost branch from 11c1a17 to e3c90bb Compare June 14, 2026 06:00
The host-cloudflare Durable Object base already owned the approval store
and the paused/resume RPCs, but its buildMcpServer ignored the session's
elicitation mode (model-mode only) and the worker served no approval
endpoints — so a gated MCP action could never be approved in the browser.

- host-cloudflare: buildMcpServer wires browser elicitation mode +
  approvalUrl (origin from the create request, captured by the DO base) +
  the base's browserApprovalStore, mirroring cloud. The worker mounts an
  Access-gated /api/mcp-sessions/* handler that routes the paused (GET) +
  resume (POST) calls to the owning session's DO RPCs.
- e2e: a new 'cloudflare' target — the real worker on workerd via wrangler
  dev (Miniflare) with local D1 + R2 and dev-auth, the console SPA built
  and served as static assets. The cross-target browser-approval scenario
  (approve + decline) now runs green here too, driving the rendered resume
  page against the worker.

Browser approval is now covered end-to-end on all three deployment
shapes: cloud, self-host, and Cloudflare self-host.
@RhysSullivan
RhysSullivan force-pushed the claude/mcp-browser-approval-cloudflare branch from 8ff9ad6 to d4f3e6f Compare June 14, 2026 06:07
@RhysSullivan
RhysSullivan changed the base branch from claude/mcp-browser-approval-selfhost to main June 14, 2026 06:07
@RhysSullivan
RhysSullivan merged commit 73f2c2b into main Jun 14, 2026
18 of 25 checks passed
@greptile-apps

greptile-apps Bot commented Jun 14, 2026

Copy link
Copy Markdown

Want your agent to iterate on Greptile's feedback? Try greploops.

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