Cloud Tasks

A task backend for coding agents. Deploys to your Cloudflare account in one prompt.

Paste this into opencode, Claude Code, or Codex — your agent installs it for you.

Already deployed? Connect your agent in one line →

agent prompt
Install and deploy the open-source "Cloud Tasks" project into my own Cloudflare account, then tell me how to connect this agent to it. Do every step for me and pause only when you need my input (for example, to approve a login).

1. Clone the repo and work inside it:
   git clone https://github.com/onurkerem/cloud-tasks.git
   The Worker source lives in packages/worker/ — run every command from there.

2. Run: npm install

3. Authenticate Wrangler. Run: npx wrangler login
   Tell me to complete the browser approval. If I can't use a browser, ask me for a CLOUDFLARE_API_TOKEN instead.

4. Create the D1 database and write its id into wrangler.jsonc:
   npm run deploy:prepare

5. Apply the schema to the remote database:
   npm run db:migrate:remote

6. Generate API key material and store it as a Worker secret for REST API use.
   Create one or more "name:secret" pairs (32 random hex bytes each; the name
   identifies the channel in task records, e.g. cli, pipeline, worker):
   npx wrangler secret put API_KEYS
   Enter a value like: cli:9f2c...,pipeline:77ab...
   Remember these - each key is a channel identity.

7. Configure Cloudflare Access for the MCP endpoint. Create a Self-hosted Access application for <worker-url>/mcp, then store:
   npx wrangler secret put CF_ACCESS_TEAM_DOMAIN
   npx wrangler secret put CF_ACCESS_POLICY_AUD
   If I do not have the Access application yet, pause and tell me the exact dashboard steps.

8. Optional - outbound webhooks:
   npx wrangler secret put WEBHOOK_SECRET
   npx wrangler secret put QUEUE_WEBHOOKS
   QUEUE_WEBHOOKS is a comma-separated queue=url list, e.g.
   builds=https://your-tunnel-host/hook - the worker pings that URL whenever a
   task is created in the queue, and POSTs {event, task} to a task's
   callback_url when it finishes. Webhooks are best-effort wake-ups; claiming
   never depends on them. Skip both to disable.

9. Deploy the Worker:
   npm run deploy
   The cron trigger (lease reaper) is enabled automatically by the repo's wrangler config.

10. Capture the deployed URL (https://cloud-tasks.<my-subdomain>.workers.dev, or your custom domain if you added one) and run the REST production smoke test:
    WORKER_URL=<that url> API_KEY=<the key> npm run smoke:prod
    If anything fails, read the error, fix it, and retry.

11. Do NOT modify this agent's own config. When done, report back:
    - the Worker URL,
    - the API_KEYS values,
    - the REST API index (<url>/api),
    - the OAuth MCP endpoint (<url>/mcp), protected by Cloudflare Access,
    - the API-key MCP endpoint (<url>/api/mcp), authenticated with an API key (no OAuth — ideal for persistent local MCP clients).

Built for agents, ready in minutes

database

D1-backed task store

Every task is a durable record in Cloudflare D1 — description, tags, queue, JSON payload and result, assignee, and status (todo / in_progress / done / failed) — with generation-fenced leases, bounded retries, and completion callbacks built in.

hub

Two APIs, one backend

A REST CRUD API at /api/tasks and a Model Context Protocol endpoint — /mcp (Cloudflare Access) or /api/mcp (API key) — that exposes the same data as agent tools.

shield

Secure by default, free to run

REST and /api/mcp are gated by an API key (Authorization: Bearer or x-api-key); the /mcp endpoint is protected by Cloudflare Access. Runs entirely on Cloudflare's free-tier Workers + D1.

How it works

Your coding agent talks to a single MCP endpoint. The Worker turns each tool call into a D1 query and returns the result.

smart_toy

Your agent

opencode · Claude Code · Codex

arrow_downward
cloud

Cloud Tasks Worker

POST /mcp · Cloudflare Access

arrow_downward
database

Cloudflare D1

Persistent task records

MCP tools exposed at /mcp

create_task list_tasks get_task update_task delete_task claim_next_task heartbeat_task complete_task fail_task

Usage

Talk to Cloud Tasks however you like — raw REST for scripts and dashboards, or the MCP endpoint for agents. REST and /api/mcp use your API key; /mcp uses Cloudflare Access and Managed OAuth.

check_circle

Create tasks with tags and an assignee; filter by status, assignee, tag, or free text.

check_circle

Atomically claim the oldest todo task — perfect for multi-agent work queues.

check_circle

Leases are fenced by a claimGeneration token: every claim returns one, heartbeat / complete / fail must present it, and a stale attempt can never mutate a newer lease — even from the same worker alias.

check_circle

Point any MCP-compatible agent at /mcp and it gets nine task tools out of the box.

REST
# Create a task
curl -X POST https://tasks.keremorenli.com/api/tasks
-H "Authorization: Bearer $API_KEY" -H "content-type: application/json"
-d '{"description":"Build the api package","queue":"builds","payload":{"repo":"acme/api"}}'
# Filter the backlog
curl -H "Authorization: Bearer $API_KEY"
"https://tasks.keremorenli.com/api/tasks?status=todo&tag=backend"
# Claim the next task (atomic)
curl -X POST https://tasks.keremorenli.com/api/tasks/claim
-d '{"queue":"builds","worker":"worker@macbook"}'
# Heartbeat / complete must send the claim's
# claimGeneration (1 here) as a fence token
curl -X POST https://tasks.keremorenli.com/api/tasks/<id>/heartbeat
-d '{"generation":1}'
curl -X POST https://tasks.keremorenli.com/api/tasks/<id>/complete
-d '{"generation":1,"result":{"ok":true}}'
MCP
# OAuth MCP (Cloudflare Access)
# Connect OAuth-capable MCP clients to
https://tasks.keremorenli.com/mcp
# API-key MCP — no OAuth, no session expiry
# Point persistent local clients (Codex, Claude Code) at
https://tasks.keremorenli.com/api/mcp · "Authorization: Bearer $API_KEY"

Already deployed?

Connect your agent in one line

Hosted MCP uses Cloudflare Access. The legacy installer remains for older API-key MCP deployments and backs up every file before it touches a thing.

  • check_circle For legacy API-key MCP deployments only; hosted MCP uses Cloudflare Access
  • check_circle Pick any of: opencode, Claude Code, Codex, Antigravity
  • check_circle Merges only the cloud-tasks entry — your other config stays untouched
  • check_circle Backs up each file first; sets 600 perms on any file holding your key
  • check_circle Optional connectivity test against your endpoint before writing
  • check_circle Works on macOS and Linux; needs bash + python3

Supported agents

opencode Claude Code Codex Antigravity
one-line install
curl -fsSL https://raw.githubusercontent.com/onurkerem/cloud-tasks/main/packages/website/public/install.sh | bash

Works on macOS and Linux. Requires bash + python3. Review it first on GitHub.

Prefer the manual path?

Clone the repo and run the deploy script — wrangler.jsonc already ships with a placeholder D1 id. Everything lives in packages/worker/.

packages/worker/wrangler.jsonc

{
  "name": "cloud-tasks",
  "main": "src/index.ts",
  "compatibility_date": "2026-06-18",
  "compatibility_flags": ["nodejs_compat"],
  "d1_databases": [
    {
      "binding": "DB",
      "database_name": "cloud-tasks",
      "database_id": "00000000-0000-0000-0000-000000000000",
      "migrations_dir": "migrations"
    }
  ]
}

packages/worker/.dev.vars

API_KEYS=channel-name:secret,another-name:another-secret
CF_ACCESS_TEAM_DOMAIN=https://your-team.cloudflareaccess.com
CF_ACCESS_POLICY_AUD=your-access-aud-tag
# Optional outbound webhooks
# WEBHOOK_SECRET=shared-secret-for-x-webhook-secret
# QUEUE_WEBHOOKS=builds=https://your-tunnel-host/hook

key Set the production secret with wrangler secret put API_KEYS.

npx wrangler login
npm run deploy:prepare
npm run db:migrate:remote
npx wrangler secret put API_KEYS
npx wrangler secret put CF_ACCESS_TEAM_DOMAIN
npx wrangler secret put CF_ACCESS_POLICY_AUD
# Optional webhooks (best-effort, never block API calls):
# npx wrangler secret put WEBHOOK_SECRET
# npx wrangler secret put QUEUE_WEBHOOKS
npm run deploy

Need an API token or your account ID? Grab them from the Cloudflare dashboard open_in_new.