Run Claude Managed Agents (CMA) on Cloudflare.
This repo provides a customizable control plane that allows you to: - Run CMA sandboxes on both full containers and light-weight isolates - Customize the container size and image your agents use - Get better observability into agent sandboxes - Apply custom egress controls for zero-trust credential injection and connectivity to private services over Workers VPC - Extend your Agents with Browser Run, Email, and any other custom tools using the Cloudflare Developer Platform your needs.
Follow the Onboarding Guide to get started. Two
deployment styles are supported: a one-click Deploy to Cloudflare
button (git-based, runs in Workers Builds) and a terminal-based
npm run deploy flow from your laptop.
[!IMPORTANT] You need a Paid or Enterprise Cloudflare account to run Managed Agents. Cloudflare Containers (used by the MicroVM sandbox) and Worker Loader bindings (used by the Isolate sandbox's code-execution and egress proxy tools) are Workers Paid plan and above.
This repository deploys a Workers-based control plane for running Claude Managed Agents on Cloudflare.
Claude Managed Agents will send a webhook to your Worker when an agent session begin or ends. The control plane spins up a sandbox for each session, syncs state across session sleeps, and shuts down the sandbox when the session ends.
Sandboxes can be Cloudflare Containers or Isolate sandboxes using the AgentsSDK. This allows you to get either the full functionality of a Linux VM or a lightweight isolate that can hit massive scales without the resource overhead or cost of a microVM.
You can configure policies to restrict agent access to specific domains, inject credentials into outgoing requests without the agent ever accessing secrets, or write arbitrary proxy code to modify, reroute, or log egress traffic.
Additionally, you can connect Workers VPC and Mesh resources to your sandboxes. This allows you to easily access private resources, on any cloud or on-prem, without them ever being exposed to the public internet.
Lastly, this codebase can be easily extended to add custom tools to your agent's sandbox. Tools are picked up automatically by both backends and run with direct access to Worker bindings so they can take advantage of the whole Cloudflare Developer Platform.
The repo provides built-in tools for: - Email Sending - Private Service Access - Browser Automation - Image Generation
All of this is accessible via a UI and API that gets deployed to your Cloudflare account.
With the repository you should be able to get a Cloudflare-based self-managed environment up and running quickly. Then you can fork it, customize it to suit your needs, and redeploy. This repository is meant as a starting point.
[!IMPORTANT] You should consider this repository alpha software. It is not yet stable and may contain bugs.
Order matters — work through the steps top to bottom.
[!IMPORTANT] Step 8 is vital to securing your workload in production. Until you add auth, anybody with access to your Workers URL can start sessions via the Claude Platform.
There are two ways to deploy. Pick one and stick with it for the whole onboarding:
npm run deploy from your laptop. Requires
Docker (for the container image build) and a wrangler login
session.Steps 1 and 3 differ between the two tracks — each has separate sub-sections below. Steps 2 and 4 through 9 are identical regardless of how you deployed.
The Deploy to Cloudflare button forks this repo into your GitHub account, provisions the D1 database, KV namespaces, R2 bucket, and Durable Objects automatically, prompts you for the required secrets (see Step 2), and deploys the Worker via Workers Builds.
After the deploy finishes you'll have a https://<your-worker>.workers.dev
URL you'll need in step 2.
⚠️ You must rename one of the two KV namespaces in the deploy form. The "Configure resources" step pre-fills both
SECRETSandEGRESS_POLICIESwith the same default name, and the deploy will fail withCannot provision a KV Namespace ... because it already existsif you don't change one. Suggested names:<worker-name>-secretsand<worker-name>-egress-policies.
The worker won't function until you finish the remaining steps.
npm run deploy
npm run deploy builds your base sandbox container image (Docker
required), deploys the Worker, and applies D1 migrations via the
postdeploy hook. The committed wrangler.jsonc deliberately leaves
KV id fields and the D1 database_id empty — scripts/ensure-kv.mjs
and scripts/ensure-d1.mjs run on prebuild and patch the real IDs
in (adopting any namespaces / databases that already exist by name,
creating fresh ones otherwise). R2 buckets are bound by name (no ID
lookup needed) and are auto-created by wrangler.
If you're on a Cloudflare login that has access to more than one
account, set CLOUDFLARE_ACCOUNT_ID before running so the ensure
scripts know which account to talk to:
export CLOUDFLARE_ACCOUNT_ID=<your-account-id>
npm run deploy
The worker won't function until you finish the remaining steps, but
the deploy gives you the https://<your-worker>.workers.dev URL
you'll need for the webhook.
Create a "Self-managed" environment in the
Claude Platform Console
and save the environment secret key. Then, in the
Webhooks settings,
set the webhook URL to https://<your-worker>.workers.dev/webhooks
and save the generated Webhook Secret.
You will need the following values:
ENVIRONMENT_ID — the ID of your new self-managed environmentANTHROPIC_ENVIRONMENT_KEY — environment secret key used by the
control plane to authenticate calls from the specified Claude agent
environmentANTHROPIC_API_KEY — used by the Worker to make calls to AnthropicWEBHOOK_SECRET — Standard Webhooks signing secret. Anthropic
posts events to your Worker; we verify the signature before doing
anything with them.The Deploy to Cloudflare form prompted for the core secrets when you
deployed, so ENVIRONMENT_ID, ANTHROPIC_ENVIRONMENT_KEY, and
ANTHROPIC_API_KEY are already on your Worker. If not,
add them with npx wrangler secret put as well.
Two follow-ups:
WEBHOOK_SECRET. The webhook didn't exist yet when
you deployed (you created it in step 2), so whatever placeholder
you supplied in the form is wrong. Push the real value now:sh
npx wrangler secret put WEBHOOK_SECRET
.dev.vars for local development.
Workers Builds doesn't sync your form inputs back to the repo, so
wrangler dev won't see them otherwise.sh
cp .dev.vars.example .dev.vars
# Edit .dev.vars and paste the values you supplied in the form,
# plus the real WEBHOOK_SECRET from step 2.
Set up local dev:
cp .dev.vars.example .dev.vars
# Edit with your real values
In production, every entry in .dev.vars needs a matching
wrangler secret put NAME. The four core secrets are:
npx wrangler secret put ENVIRONMENT_ID
npx wrangler secret put ANTHROPIC_ENVIRONMENT_KEY
npx wrangler secret put ANTHROPIC_API_KEY
npx wrangler secret put WEBHOOK_SECRET
The postdeploy hook runs wrangler d1 migrations apply DB --remote
automatically after every npm run deploy (including the Deploy to
Cloudflare button's deploy), so there's nothing to do here for
production. For local dev:
npm run db:migrate # local
The R2 bucket itself was auto-created in step 1. The presigned-URL
path used in production still needs an access key. If you provided
R2_ACCESS_KEY_ID / R2_SECRET_ACCESS_KEY / BACKUP_BUCKET_NAME /
CLOUDFLARE_ACCOUNT_ID during the Deploy to Cloudflare form, you
can skip ahead — they're already set.
This is required unless you're exclusively using Isolate sandboxes
or running stateless agent sessions. Isolate sessions persist through
Durable Object SQLite storage and don't need R2 — if you're certain
you'll never run a MicroVM agent, you can skip this step (and drop
the r2_buckets block from wrangler.jsonc).
Anyone running the MicroVM backend should treat this as core onboarding.
Mint an R2 access key
with read+write on the claude-managed-agents-snapshots bucket
(Cloudflare dashboard → R2 → Manage R2 API Tokens → Create token),
copy the Access Key ID and Secret Access Key, then push the
following secrets:
npx wrangler secret put R2_ACCESS_KEY_ID
npx wrangler secret put R2_SECRET_ACCESS_KEY
npx wrangler secret put BACKUP_BUCKET_NAME # same as bucket_name in wrangler.jsonc (IE: claude-managed-agents-snapshots)
npx wrangler secret put CLOUDFLARE_ACCOUNT_ID # found in your dashboard URL
See Snapshots & state persistence for more information.
Browser Rendering, Workers AI, and Email send are wired up out of the
box — disable in wrangler.jsonc if you don't want them. VPC bindings
are opt-in.
| Capability | Default | Bindings | Secrets to add | Setup steps |
|---|---|---|---|---|
| Browser Rendering tools | on | browser in wrangler.jsonc |
CLOUDFLARE_API_TOKEN (for REST tools) |
none |
| Workers AI image gen | on | ai in wrangler.jsonc |
none | none |
| Email send + inbox | on | send_email in wrangler.jsonc; set EMAIL_DOMAIN + EMAIL_FROM (Deploy to Cloudflare prompts for these, or vars in wrangler.jsonc locally) |
none | Email docs |
| VPC private services | off | add vpc_services blocks in wrangler.jsonc |
none | Workers VPC docs |
https://<your-worker>.workers.dev/. Create an agent (the form lets
you pick MicroVM or Isolate backend), kick off a session, watch the
logs.
Your control plane is not secured by default!
Once you have set things up, you will want to secure the dashboard by setting up Cloudflare Access.
See Cloudflare Access docs for more information.
Fork this repo and modify it to suit your needs or add custom tools.
Once the control plane is up and a first session has run end-to-end, you can go futher:
src/tools/custom-tools.ts.Dockerfile and instance_type knobs for the MicroVM backend.Required secrets & vars:
| Name | Description |
|---|---|
ENVIRONMENT_ID |
Environment to poll for work |
ANTHROPIC_ENVIRONMENT_KEY |
Anthropic environment key (sk-ant-oat01-...). The single credential the control plane uses for poll, ack, heartbeat, force-stop, and the session event stream. Renamed from ANTHROPIC_ENV_KEY in the 0.96 SDK / ant 1.8 CLI. |
ANTHROPIC_API_KEY |
Anthropic API key (sk-ant-...) |
WEBHOOK_SECRET |
HMAC secret for verifying webhook signatures |
CLOUDFLARE_ACCOUNT_ID |
R2 snapshots (pr |
$ claude mcp add claude-managed-agents \
-- python -m otcore.mcp_server <graph>