A thin orchestration layer that schedules, governs, and observes a fleet of Anthropic Managed Agents — controllable from inside Claude itself, via a unified MCP tool surface.
axon-gateway started with a simple promise: bring internal business intel to the people who need it, without making them ask me. The pattern was tidy — scheduled tasks scrape raw data, enrich it, persist to SQLite. MCP tools expose the result. Anyone in the workforce sits in Claude and asks a question; the data answers. That part works.
What didn't scale is the gap between fresh data arrived and an intelligent answer in front of a stakeholder. Three gaps surfaced once the basic ETL-to-MCP pattern was in place.
axon's tools are reactive — they answer questions when asked. But most of the value of fresh intel is unprompted: a finding worth flagging, a number that moved, a recommendation the data implies. axon has no agent loop running over new data to produce insights and write them back. Every insight today requires a human to think to ask for it.
Today I am the proxy. Users ask me questions; I forward them to one of my Claude Code sessions; that session does the analysis; I relay the answer back. Those sessions are functioning as pseudo-agents — they have context, tools, the ability to reason — but they're tethered to my keyboard. The pattern only scales if I'm at it.
Claude is a capable Q&A interface, but it isn't a persistent dashboard and it isn't an upload surface. End users don't type questions to find out what's happening — they need to land on a dashboard that says "here's what changed", AND they need to drop next month's Viant xlsx into a UI that feeds the pipeline. Reading and writing are both first-class operator needs. Anthropic's cowork features are early and don't reach either side. The intel needs a real product surface with both directions covered — structured reporting for the eyes, structured ingestion for the hands.
These three gaps point to a different architecture. axon stays — the unified MCP surface is right, and the workforce-asks-Claude pattern is working. But the agent loop, the persistent UI, and the push channel all live outside axon's scope. The blueprint below is what fills those gaps without ripping out what already works.
Anthropic owns the runtime. Each agent is defined declaratively in Anthropic's console — system prompt, tool list, knowledge config. The catalog, versioning, and the per-run reasoning loop all live there. They expose an API to invoke, list, observe.
Cloudflare owns the ops. A small set of Workers handle what Anthropic doesn't ship natively: cron-driven scheduling, push delivery, run history, and a thin REST surface for control. Per-app D1 holds operational data and the schedule/run/subscription tables.
axon-gateway owns the unified MCP surface for analysts. Every
tool — domain tools for each agent project, plus a new agents_*
control-plane family — ships through one server. Analysts sit in Claude and say
"pause syndication recommendations for the week" or "show me what
the recommendations agent ran yesterday."
Per-app Next.js UIs own the end-user surface. Each agent project ships its own dashboard hosted on Cloudflare. End users land on the dashboard to see the state, and use the same UI to upload the next vendor xlsx into the pipeline. The web UI is a first-class plane, not a footnote — it's the read AND write surface for the people who don't live in Claude.
Read the diagram from the bottom up. The operator (in Claude) is the entry point. Their request flows up through axon's MCP layer, which fans out to the two underlying control surfaces: the Cloudflare scheduler Worker for state mutations (schedules, triggers, pauses), and Anthropic's Managed Agents API for catalog and run information.
Each agent project ships its own Next.js dashboard hosted on Cloudflare Workers via OpenNext. The UI does two things end users need that Claude cannot — it presents structured intel passively (KPIs, charts, drill-throughs, alerts) and it accepts structured data from operators (drag-drop xlsx and csv uploads that feed the pipeline). Read surface and write surface, on one durable URL, behind Cloudflare Access.
KPIs, time-series, top-N tables, drill pages, alerts, agent-written findings/recommendations/opportunities. Every number on every page reads directly from D1 via Drizzle. The dashboard is the surface where the agent loop's output becomes legible — a stakeholder lands here and sees the state without typing anything.
Vendor reports the agent loop can't fetch autonomously — monthly revenue xlsx, daily csv exports, occasional one-off enrichment files. The operator drags a file in; the UI PUTs the bytes to R2 with a content-hash key; an upload-router worker sniffs the vendor, dispatches the right loader, and the loader writes parsed rows back to D1. Within seconds the same dashboard reflects the new month. Idempotent on sha256 — uploading the same file twice is a no-op.
The UI isn't separate from the agent loop — it's the same D1, the same schema. When the agent writes a recommendation, the dashboard surfaces it on the next render. When the operator uploads a vendor file, the loader writes rows the agent will reason over on its next scheduled tick. The UI, the agent, and the MCP tools all converge on one state store. The dashboard is what the agent thinks. The upload is what the operator tells it.
The agents_* tool family in axon does two things: it lets operators
control agent scheduling (update cron, pause, trigger now), and it
pulls a full information surface from Anthropic's Managed Agents API
(catalog, agent definitions, run history, latest output). Read tools fan to
Anthropic + D1; write tools fan to the Cloudflare scheduler Worker.
GET /v1/agentsGET /v1/agents/{id}GET /schedules → D1GET /v1/agents/{id}/runs + D1 cross-refGET /v1/agents/{id}/runs/{run_id}PATCH /schedules/{id}POST /schedules/{id}/pausePOST /trigger/{id}POST /subscriptions
All write tools are wrapped in a two-step diff-then-confirm pattern. The first call
returns the proposed mutation as JSON; only a second call with a returned
confirmation_token applies it. The agent never mutates its own schedule
autonomously without an operator-confirmed second call. This avoids the 2am recursion bug
where an agent's failure mode is to disable itself.
The push-driven path. No operator involvement — the system surfaces intelligence proactively on schedule.
POST /v1/agents/syn-daily-digest/invoke with today's context window.top_advertisers, top_segments, recommendations_list) to gather data.runs table. Emits a webhook to the push Worker.syndication_daily_digest. Posts to each channel via Slack webhook / email API.The recursive control move. The operator never visits a dashboard or a CLI — they just describe the desired state to Claude and the MCP layer handles it.
agents_list_catalog() to identify the agent by name. Resolves to syn-recommendations.agents_pause(id="syn-recommendations", until="2026-06-01") — first call, dry-run.confirmation_token.agents_pause(..., confirmation_token=...) — second call, apply.The write path. An end user drops a vendor's monthly report into the UI; seconds later the dashboards reflect the new month and the next scheduled agent run will reason over it. No engineering involvement.
2026_05_Resonate_Usage.xlsx onto the /upload drop zone.PUT bytes to R2 with the sha as the object key.ingest_inbox row in D1 marked pending. Dispatches the right loader.batch() INSERT into D1 — listing rows, revenue rows, segment-advertiser rows.ingest_inbox row as ingested. Invalidates UI caches. Optionally enqueues an alert message ("new month ingested, $X across N advertisers").The point worth stating plainly: the end user supplying data is what the agent reasons over next. The UI's upload widget isn't a side feature — it's how fresh ground truth enters the system. Without it, the agent only sees what the scrapers can fetch autonomously.
The choice between Anthropic's Claude Agent SDK and Anthropic's Managed Agents platform was the load-bearing question. For 3 projects with 5 users on scheduled-runs-plus-push, Managed wins on time-to-ship and observability. The Agent SDK is the right answer for fully bespoke runtime behavior — sub-agent orchestration, custom retry, in-process state machines — but you'd be building ops you don't need.
The mitigation for lock-in: the scheduler Worker's "invoke agent" should be one function — today it calls Anthropic Managed Agents; tomorrow it could call the Agent SDK running on a Worker, or a third-party. Don't let Anthropic's API shape leak into your scheduler's domain model.
The framing is "control plane," not "platform." The distinction protects scope. A platform is something you build features on; a control plane is a thin layer that governs something owned elsewhere. Resist these temptations:
agents_* MCP tools are the control surface; Claude is the UI for them./mcp endpoints. The unified axon surface is the whole value proposition — splitting it loses cross-tool composability.packages/ui; shared business logic doesn't.If you start adding business logic into the scheduler — domain rules, output formatting, vendor-specific code paths — you've stopped building a control plane and started building a platform. That's the line.
Before any platform investment, prove the architecture hangs together with the smallest possible loop. The syndication recommendations agent is the right pilot — smallest scope, most legible output, lowest blast radius.
https://axon.../mcp. System prompt: "summarize today's syndication recommendations."/invoke, records the run to D1.agents_get_runs(limit=10) reading the D1 run table, and agents_list_catalog() reading Anthropic's /v1/agents.If all five pieces wire up cleanly, the rest of the build is execution. If the Anthropic API doesn't let agents call axon, or push delivery is fiddly, or the run-recording shape feels wrong — the spike surfaces it before any platform investment is committed.