Server data from the Official MCP Registry
Schedule webhook calls, pay per run in USDC on Base via x402. Ten tools, built for small models.
About
Schedule webhook calls, pay per run in USDC on Base via x402. Ten tools, built for small models.
Security Report
Valid MCP server (2 strong, 4 medium validity signals). 2 known CVEs in dependencies ⚠️ Package registry links to a different repository than scanned source. Imported from the Official MCP Registry. 1 finding(s) downgraded by scanner intelligence.
10 files analyzed · 3 issues found
Security scores are indicators to help you make informed decisions, not guarantees. Always review permissions before connecting any MCP server.
Permissions Required
This plugin requests these system permissions. Most are normal for its category.
What You'll Need
Set these up before or after installing:
Environment variable: CRON402_PRIVATE_KEY
Environment variable: CRON402_NETWORK
Environment variable: CRON402_API_URL
Environment variable: CRON402_STATE_DIR
How to Install
Add this to your MCP configuration file:
{
"mcpServers": {
"io-github-userdefault13-cron402-mcp": {
"env": {
"CRON402_API_URL": "your-cron402-api-url-here",
"CRON402_NETWORK": "your-cron402-network-here",
"CRON402_STATE_DIR": "your-cron402-state-dir-here",
"CRON402_PRIVATE_KEY": "your-cron402-private-key-here"
},
"args": [
"-y",
"cron402-mcp"
],
"command": "npx"
}
}
}Documentation
View on GitHubFrom the project's GitHub README.
cron402
Prepaid cron jobs for AI agents, paid per run in USDC over x402. An agent asks cron402 to call a webhook on a schedule, pays $0.008 per run from its own wallet, and manages the job with wallet signatures. There are no accounts or API keys: the wallet is the credential.
| Site | https://agentcron.site (docs, llms.txt, wallet dashboard) |
| API | https://cron402-api.user-defaults.workers.dev (OpenAPI) |
| Price | $0.008 USDC per run on Base mainnet (eip155:8453); 1 / 10 / 100-run top-up packs |
| Agent entry points | cron402-mcp MCP server, skills/cron402 agent skill, plain HTTP + any x402 client |
| Stack | Cloudflare Workers (Hono), Durable Objects, D1, @x402/hono, viem, SvelteKit, TypeScript, pnpm + Turborepo |
| Tests | 38 tests running in the real Workers runtime (@cloudflare/vitest-plugin), on every push and PR. See Testing. |
What it does
- Create:
POST /v1/cronswith a cron expression and a target URL. The first call returns402 Payment Required; the agent signs an EIP-3009 USDC authorization and retries. The facilitator verifies and settles, and the job starts with 1 run credit. - Run: each job lives in its own Durable Object. Its alarm fires at the next cron time, calls the webhook with retries, logs the result to D1, and spends one credit.
- Top up:
POST /v1/crons/topup/{1,10,100}adds credits (anyone can fund any job). Exhausted jobs reactivate. - Manage: pause, resume and delete require an EIP-712
ManageJob { action, jobId, timestamp }signature from the wallet that paid, valid for ±5 minutes.
Architecture
flowchart LR
agent["AI agent<br/>(cron402-mcp, skill, or any x402 client)"]
human["Human<br/>(agentcron.site dashboard)"]
subgraph cf["Cloudflare Worker: apps/api"]
hono["Hono router<br/>+ x402 paywall (@x402/hono)"]
auth["EIP-712 manage auth<br/>(auth.ts)"]
sweep["Cron Trigger sweeper<br/>(every minute)"]
do["CronJobDO<br/>one Durable Object per job<br/>alarm() fires the run"]
d1[("D1<br/>jobs · executions · payments")]
end
fac["x402 facilitator<br/>(Coinbase CDP)"]
base[("USDC on Base")]
hook["Agent's webhook"]
agent -- "HTTP + x402 payment" --> hono
human -- "wallet-signed pause/resume/delete" --> hono
hono -- "verify / settle" --> fac
fac --> base
hono --> auth
hono -- "init / resume / cancel / destroy (RPC)" --> do
hono --> d1
do -- "scheduled call (retries, 10s timeout)" --> hook
do -- "log run, spend credit, next_run_at" --> d1
sweep -- "re-arm missed alarms,<br/>recover orphaned jobs" --> do
sweep --> d1
The web app (apps/web, SvelteKit on Vercel) serves the landing page, agent docs, llms.txt and a wallet-connected job console. It talks to the same public API.
How scheduling works
- Every job gets its own Durable Object (
CronJobDO). Acron-parser-computed next-run time is set as the DO'salarm()— exact to the minute for arbitrary cron expressions. - Each fire: dispatch the target webhook (10s timeout) → up to 3 attempts with backoff → log the execution to D1 (kept: last 100 runs / 30 days per job) → consume 1 credit.
- Auto-pause rules: 3 consecutive failures →
paused; zero credits →exhausted. - A global Worker Cron Trigger (
* * * * *) sweeps for missed alarms as a safety net: it re-arms active jobs more than 90s overdue and re-initialises active jobs whose Durable Object never got set up (soft-deleting them if that fails, without touching payments). - Top-ups reactivate
paused/exhaustedjobs automatically.
How payments work
Paid endpoints are gated by @x402/hono v2 middleware:
- Agent calls the endpoint with no auth.
- Server responds
402 Payment Requiredwith instructions (price, network,payTo). - Agent signs an EIP-3009 USDC transfer from its wallet and retries.
- The facilitator verifies + settles; settlement receipts land in D1.
Because the payer isn't online when an alarm fires, billing is prepaid run credits: creation includes 1 credit; packs of 1 / 10 / 100 cost $0.008 / $0.08 / $0.80. Credits never expire and top-ups are permissionless (anyone can fund any job).
Management actions (pause/resume/delete) require an EIP-712 signature over ManageJob { action, jobId, timestamp } from the payer key (±5 min validity).
Repository layout
ai-cron-site/
├── .github/workflows/ci.yml # typecheck + tests on every push and PR
├── package.json # pnpm workspaces + turbo tasks
├── turbo.json # build/check/test/lint pipeline
├── biome.json # Biome lint/format config
├── .env.example # all env vars documented
├── packages/shared/
│ └── src/ # zod schemas, constants, types, JSON Schemas
├── packages/mcp/
│ ├── src/index.ts # cron402-mcp: MCP server (10 agent tools)
│ ├── src/schedule.ts # plain-English → cron parsing, validation, next-run preview
│ └── src/store.ts # remembers created job ids for list_crons
├── skills/cron402/SKILL.md # drop-in agent skill for using the MCP server
└── apps/
├── api/ # Cloudflare Worker
│ ├── wrangler.jsonc # DO bindings, D1 binding, cron trigger
│ ├── .dev.vars.example # local dev vars template
│ ├── migrations/ # D1 schema (0001 init, 0002 notify_url)
│ ├── vitest.config.ts # Workers-runtime test pool (D1 + DO bindings, pinned test env)
│ ├── test/ # auth, scheduler, x402 and management tests
│ └── src/
│ ├── index.ts # Hono app, routes, lazy x402 middleware
│ ├── scheduler.ts # CronJobDO (alarm loop, retries, retention, notify)
│ ├── auth.ts # EIP-712 management-signature verification
│ ├── payments.ts # payer extraction from x402 headers
│ ├── facilitator.ts # CDP hosted facilitator client
│ ├── cdp-auth.ts # CDP JWT signing (jose/WebCrypto)
│ ├── openapi.ts # OpenAPI 3.1 document builder
│ ├── moltbook.ts # token-gated, read-only Moltbook heartbeat route
│ └── types.ts # Env bindings + typed DO stub interface
└── web/ # SvelteKit 5 → adapter-vercel (MS-DOS theme)
└── src/routes/
├── +page.svelte # landing
├── docs/+page.svelte # agent integration guide
├── dashboard/+page.svelte # wallet-connected job console
├── llms.txt/+server.ts # agent-readable site summary
└── robots.txt/+server.ts # crawler policy (AI bots allowed)
API reference
| Endpoint | Auth | Price | Description |
|---|---|---|---|
POST /v1/crons | x402 | $0.008 | Create job (body: { schedule, target: { url, method?, headers?, body? }, maxRuns? }). Includes 1 run credit. |
POST /v1/crons/topup/1 | x402 | $0.008 | Add 1 credit (body: { jobId }) |
POST /v1/crons/topup/10 | x402 | $0.08 | Add 10 credits |
POST /v1/crons/topup/100 | x402 | $0.80 | Add 100 credits |
GET /v1/crons/:id | free | — | Job status, credits, last 20 executions |
POST /v1/crons/:id/pause | wallet-signed | — | Pause job |
POST /v1/crons/:id/resume | wallet-signed | — | Resume job (requires credits) |
DELETE /v1/crons/:id | wallet-signed | — | Delete job |
GET /health | free | — | Liveness probe |
POST /v1/echo | free | — | Test target for smoke tests |
Signed requests add two headers:
x-cron402-timestamp: unix ms (valid ±5 min)x-cron402-signature: EIP-712 signature, domain{ name: "cron402", version: "1" }, primaryTypeManageJob
Limits & policies (v1)
- Min interval: 1 minute · Max 1,000 active jobs per payer wallet
- Retry policy: 3× with 1s/3s/8s backoff; non-retryable 4xx (except 429) fails immediately
- Log retention: last 100 executions or 30 days per job, whichever trims first
Deployment status (live)
| Surface | URL |
|---|---|
| API (Cloudflare Workers) | https://cron402-api.user-defaults.workers.dev |
| Web (Vercel) | https://agentcron.site |
| OpenAPI spec | https://cron402-api.user-defaults.workers.dev/v1/openapi.json |
| Agent summary | https://agentcron.site/llms.txt |
| Job console | https://agentcron.site/dashboard |
Network: Base mainnet (eip155:8453) — flipped from Sepolia after a fully verified paid run. Receiving wallet secret PAY_TO_ADDRESS is set on the Worker. Facilitator: Coinbase CDP hosted (CDP_API_KEY_ID/CDP_API_KEY_SECRET secrets set; JWT auth implemented in src/cdp-auth.ts). D1 database cron402 (id b7e05d96-4737-4aa4-ab52-badcfacb3a22) holds the schema from both migrations.
Bazaar: bazaar extensions are declared via @x402/extensions/bazaar (info.input.type: "http"); listing appears after a successful CDP settle that echoes the extension — do not claim currently indexed until validated.
Execution webhooks
Pass optional notifyUrl when creating a job. After every fire, cron402 POSTs to it:
{
"type": "cron402.execution",
"jobId": "…",
"runAt": 1755000000000,
"ok": true,
"attempts": 1,
"statusCode": 200,
"error": null,
"durationMs": 42
}
Notifications are best-effort (10s timeout, never affect scheduling).
Human dashboard
/dashboard connects any injected EIP-1193 wallet (MetaMask etc.). Load a job by id to see status/credits/executions; pause/resume/delete request an eth_signTypedData_v4 signature over ManageJob — exactly what agents sign, just through a browser wallet.
MCP server (cron402-mcp)
Ten tools for any MCP client, designed so a small model can schedule a cron without burning money on mistakes:
| Tool | Cost | Purpose |
|---|---|---|
cron402_guide | free | The full procedure, prices and rules — what a model reads first |
check_wallet | free | Address, network, USDC/ETH balance, runs affordable |
preview_schedule | free | Validate a schedule, show the next 5 UTC run times |
create_cron | $0.008 | Create a job (includes 1 credit) |
topup_cron | $0.008–$0.80 | Add 1 / 10 / 100 credits |
get_cron | free | Status, credits, last 20 executions |
list_crons | free | Jobs created from this machine, with live status |
pause_cron · resume_cron · delete_cron | free | EIP-712 signed management |
What makes it usable by weak models:
- Plain-English schedules —
"every 15 minutes","every weekday at 9am","every monday at 5pm","weekly", or raw cron. Ambiguous input ("every 2 weeks","at teatime") fails loudly with a suggested fix instead of silently scheduling the wrong time. - Timezone conversion — pass an IANA
timezoneand the time-of-day is converted to the UTC cron the API stores, with the DST caveat spelled out in the response. - Free dry run before any spend —
preview_schedulecosts nothing, andcreate_cronre-validates locally so a malformed schedule never reaches a paid request. - Money guardrails — paid tools are labelled
PAIDwith the exact amount, and every response carries anext_steptelling the model not to call them twice. - Actionable errors —
{ ok, error, next_step }for insufficient funds, an unreachable API, a job owned by another wallet, or alocalhosttarget. - Job memory — the API has no list endpoint, so created ids are written to
~/.cron402/jobs.json(override withCRON402_STATE_DIR) andlist_cronsreads them back. - Read-only start — a missing
CRON402_PRIVATE_KEYno longer kills the server; free tools keep working and the paid ones explain what to configure.
Paid calls flow through x402 automatically; management actions are signed locally.
Agent skill (skills/cron402)
skills/cron402/SKILL.md teaches an agent the whole workflow — check wallet → preview → confirm → create → top up — plus the rules that keep it from double-charging. Drop it into any project:
mkdir -p .claude/skills/cron402
curl -sL https://raw.githubusercontent.com/userdefault13/ai-cron-site/main/skills/cron402/SKILL.md \
-o .claude/skills/cron402/SKILL.md
See skills/README.md for other clients.
Install from npm:
export CRON402_PRIVATE_KEY=0x... # fund with USDC on Base
npx -y cron402-mcp
MCP client config (Cursor / Claude Desktop):
{
"mcpServers": {
"cron402": {
"command": "npx",
"args": ["-y", "cron402-mcp"],
"env": { "CRON402_PRIVATE_KEY": "0x..." }
}
}
}
Local dev from repo:
pnpm --filter cron402-mcp build
packages/mcp/abra-start.sh # key from abracadabra vault (Touch ID gated)
See packages/mcp/PUBLISHING.md for release instructions.
Env (optional overrides): CRON402_NETWORK (eip155:8453 mainnet default, eip155:84532 for Sepolia), CRON402_API_URL, CRON402_ABRA_PROJECT (default ai-cron-site). If CRON402_PRIVATE_KEY is already set in the environment, the vault lookup is skipped.
Agent wallet: 0xfD4AE5BD56d9BB939B95CCb870E0c97B62E0dC30 — stored encrypted in the abracadabra vault as ai-cron-site / EVM_PRIVATE_KEY. (It replaced 0x269B9678…, which was rotated out after its key was briefly committed to this repo and swept; never hardcode the key, read it from the vault.) On mainnet it needs mainnet USDC (0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913) + a little ETH for gas. This is the payer; revenue flows to the Worker's PAY_TO_ADDRESS secret.
Local development
Prereqs: Node 22+, pnpm 9+, Wrangler (bundled).
pnpm install
# API — apply schema locally and start dev server (:8787)
pnpm --filter @cron402/api db:local
cp apps/api/.dev.vars.example apps/api/.dev.vars # SKIP_PAYMENTS=1 by default
pnpm dev:api
# Web — SvelteKit dev server (:5173)
pnpm dev:web
With SKIP_PAYMENTS=1 (local only), paid routes skip x402 verification and identify the payer via the x-payer header.
E2E smoke test
# create an every-minute job targeting the built-in echo endpoint
curl -X POST http://127.0.0.1:8787/v1/crons \
-H 'content-type: application/json' \
-H 'x-payer: 0x1111111111111111111111111111111111111111' \
-d '{"schedule":"* * * * *","target":{"url":"http://127.0.0.1:8787/v1/echo","method":"POST"}}'
# wait ~70s for the next minute boundary, then check the execution log
curl http://127.0.0.1:8787/v1/crons/<jobId>
# expect: credits decremented, executions[0].ok = 1, status flips to "exhausted" at 0 credits
# top up and watch it reactivate
curl -X POST http://127.0.0.1:8787/v1/crons/topup/10 \
-H 'content-type: application/json' \
-H 'x-payer: 0x1111111111111111111111111111111111111111' \
-d '{"jobId":"<jobId>"}'
Quality gates: pnpm check (tsc + svelte-check, including the test files), pnpm test, pnpm lint (Biome), pnpm build.
Testing
pnpm install
pnpm test # all workspaces (turbo)
pnpm --filter @cron402/api test # API only
pnpm --filter @cron402/api test:watch # watch mode
The API tests run inside workerd, the real Cloudflare Workers runtime, through @cloudflare/vitest-plugin. Each run gets a local D1 database with apps/api/migrations applied and real CronJobDO Durable Objects. Every outbound fetch (webhooks, notify URLs, the x402 facilitator) is mocked, so the suite needs no network, secrets or funds. Wallets are throwaway keys from viem's generatePrivateKey(). Test bindings are pinned in vitest.config.ts, so a local .dev.vars never leaks into a test run.
| File | Covers |
|---|---|
test/auth.test.ts | EIP-712 ManageJob verification: valid signature accepted; expired and future timestamps outside ±5 min rejected; another wallet's signature rejected; a signature replayed against a different action, job, timestamp or EIP-712 domain rejected; malformed input handled; header parsing |
test/scheduler.test.ts | CronJobDO: init arms the first alarm; an alarm fires the webhook, records the execution, spends a credit and re-arms; last credit leads to exhausted; no credits means no call; 4xx is not retried and counts as a failure; notifyUrl receives the run result. Sweeper: re-arms a past-due job with a lost alarm, respects the 90s grace window, recovers an orphaned job from its D1 row, soft-deletes and scrubs an orphan whose init fails (payments kept), ignores paused jobs |
test/x402.test.ts | Paid create flow: unpaid POST /v1/crons returns a 402 x402 v2 challenge (USDC on Base, $0.008, payTo, Bazaar extension) and creates nothing; a signed payment is verified, the handler runs, payment settles, and the job row, payment row and Durable Object alarm all exist; an invalid body is not settled; a facilitator-rejected payment returns 402 and creates nothing |
test/manage.test.ts | Signed routes end to end: pause, resume and delete with the payer's signature; another wallet gets 403; a pause signature replayed as delete gets 403; missing headers get 401; unknown job gets 404 |
CI (.github/workflows/ci.yml) runs pnpm install --frozen-lockfile, pnpm check and pnpm test on every push and pull request.
Go-live instructions
1. Provision Cloudflare resources
cd apps/api
npx wrangler login
# create production D1 database and note the returned id
npx wrangler d1 create cron402
npx wrangler d1 execute cron402 --remote --file=migrations/0001_init.sql
Edit apps/api/wrangler.jsonc: replace the placeholder database_id with the real one.
⚠️ Durable Object class
CronJobDOusesnew_sqlite_classes— if you rename it or add classes later, bump the migration tag.
2. Set secrets & vars (API)
cd apps/api
# your receiving address on the chosen network (REQUIRED)
npx wrangler secret put PAY_TO_ADDRESS
# optional: CDP-hosted facilitator for production settlement
# default is the open https://x402.org/facilitator
npx wrangler secret put FACILITATOR_URL
Vars live in wrangler.jsonc / dashboard:
| Var | Values | Notes |
|---|---|---|
X402_NETWORK | eip155:84532 (sepolia) · eip155:8453 (mainnet) | must match payTo network |
SKIP_PAYMENTS | unset in prod! | only for local testing |
FACILITATOR_URL | facilitator base URL | see below |
Facilitator options
- Default:
https://x402.org/facilitator— open, no auth, testnet only. - Production: Coinbase CDP hosted facilitator (recommended). Set secrets
CDP_API_KEY_ID+CDP_API_KEY_SECRET; the Worker then automatically switches toCdpFacilitatorClient(apps/api/src/facilitator.ts), which generates a per-request CDP JWT bound to each endpoint (/verify,/settle,/supported) via theurisclaim. JWT signing is implemented onjose/WebCrypto inapps/api/src/cdp-auth.tsand supports both Ed25519 and EC (ES256) CDP keys.- Get keys at https://portal.cdp.coinbase.com → API Keys → Secret API Key.
- Optionally override
FACILITATOR_URLif CDP exposes a different base path; default ishttps://api.cdp.coinbase.com/platform/v2/x402.
3. Deploy
# API
pnpm --filter @cron402/api deploy
curl https://cron402-api.<your-subdomain>.workers.dev/health
# Web (first time: vercel link inside apps/web)
cd apps/web && npx vercel --prod
# set PUBLIC_API_URL to the deployed Worker URL in Vercel project settings,
# then update the apiUrl constant in src/routes/docs/+page.svelte
4. Fund & verify end-to-end on Sepolia
- Get free test USDC: https://faucet.circle.com/ (Base Sepolia).
- Run the buyer snippet from
/docson the site (uses@x402/fetch+ Coinbase Agentic Wallet) pointed at your deployed Worker URL. - Confirm:
402 → sign → retrysucceeds, job appears, first fire lands within one minute, settlement logged in D1:
npx wrangler d1 execute cron402 --remote --command "SELECT * FROM payments ORDER BY created_at DESC LIMIT 5"
5. Mainnet flip checklist
-
PAY_TO_ADDRESSis a mainnet-controlled address -
X402_NETWORK=eip155:8453 - Facilitator upgraded (CDP recommended; set spend controls on the payer side too)
- Re-test a full paid cycle before announcing
- Review rate limits / pricing in
packages/shared/src/index.ts(PRICE_PER_RUN_USD, pack prices inapps/api/src/index.tsTOPUP_PACKS)
Operational notes
- Monitoring: poll
GET /v1/crons/:id; execution rows include status code, latency, error text. - Sweeper: the every-minute Cron Trigger re-kicks DOs whose alarms were missed (>90s overdue). Check Worker metrics if jobs stall.
- Changing price: update
PRICE_PER_RUN_USDin shared +TOPUP_PACKSin the API together. - Secrets hygiene:
.dev.vars,.env*are gitignored; never commit keys.
Reviews
No reviews yet
Be the first to review this server!
More Developer Tools MCP Servers
Rea
Freeby Morluto · Developer Tools
Reverse engineer anything from your terminal or agent with one CLI and MCP server.
Paperclip
Freeby Paperclipai · Developer Tools
Trending hip-hop artist momentum scores across four cultural dimensions.
Git
Freeby Modelcontextprotocol · Developer Tools
Read, search, and manipulate Git repositories programmatically
Toleno
Freeby Toleno · Developer Tools
Toleno Network MCP Server — Manage your Toleno mining account with Claude AI using natural language.
MCP Marketplace
Freeby mcp-marketplace · Developer Tools
Search and install MCP servers from inside your AI client.
mcp-creator-python
Freeby mcp-marketplace · Developer Tools
Create, build, and publish Python MCP servers to PyPI — conversationally.
