Skip to main content

Claude Code's Dynamic Workflows Cap Concurrency at 16, Not Hundreds: What a Real Subagent Burst Does to a Deploy MCP Server

11 min readDora NodaDora Noda
Share
On this page

Anthropic's June 2026 Dynamic Workflows update let a Claude Code session write its own orchestration script and fan a task out across "tens to hundreds of parallel subagents." That framing has been enough to make more than one platform team nervous about what happens when a workflow points its subagents at an infrastructure MCP server — a hundred simultaneous rollback calls sounds like exactly the kind of burst a deploy API wasn't built to survive. The real number, published in Claude Code's own docs, is smaller and more specific: up to 16 agents run concurrently, with a hard ceiling of 1,000 agents total per run. That's not "hundreds at once." It's sixteen at once, as many as a thousand times over the course of a run. The gap between those two shapes is the difference between designing for an unbounded flood and designing for a bounded, repeatable burst — and only one of those is actually a tractable engineering problem.

The Concurrency Shape, Exactly

Claude Code's workflow runtime documentation is specific about the bounds, and they're worth reading as constraints rather than marketing copy:

ConstraintValueWhy it exists
Concurrent agentsUp to 16 (fewer on CPU-limited machines)Bounds local resource use
Total agents per run1,000Prevents runaway loops
"Large workflow" warning>25 scheduled agents or >1.5M projected tokensFlags a run worth a second look before it starts

Two details matter more than the headline numbers. First, agents inside a workflow call tools — including MCP tools — directly, not through a lead agent acting as a proxy: the docs note that "shell commands, web fetches, and MCP tools that aren't in your allowlist can still prompt you mid-run," which only makes sense if each of the up-to-16 concurrent agents is opening its own tool calls against your server, not funneling them through a single connection. Second, those agents run in acceptEdits mode and inherit the session's tool allowlist — so once a deploy/rollback MCP server is on that allowlist, every subagent in the workflow can call it without a human confirming each individual call.

Put together: a workflow auditing 200 services for health and rolling back the unhealthy ones doesn't send your MCP server 200 simultaneous rollback calls. It sends at most 16 at a time, sequenced through the runtime's pool, until all 200 are processed. That's a materially easier server to build than one rated for unbounded concurrency — but "easier than unbounded" is not the same as "easy," and 16 concurrent write calls against a control plane is still enough to break a server that was only ever tested one call at a time.

What 16 Concurrent Rollbacks Actually Breaks

Walk through the audit-and-rollback workflow concretely. Sixteen subagents each pick up one unhealthy app and call your MCP server's rollback tool. Three things go wrong in a server that wasn't built for this, none of them exotic:

Timeout-triggered double execution. A rollback call that takes 12 seconds to actually flip traffic back to the prior release looks, from a subagent's perspective, indistinguishable from a call that's hung. Claude Code's own retry behavior — and every well-behaved MCP client's — is to retry a tool call that times out. If your server has no way to recognize "this is the same rollback request I already started," the retry doesn't check status, it re-executes: a second rollback fires while the first is still finishing, and depending on how your rollback logic reads "current release" (before or after the first call's write completes), the app can end up on a third revision nobody asked for.

Same-app races from different subagents. A workflow auditing services for staleness rather than health might independently decide two different apps sharing a database migration both need a rollback — or, more commonly, a flaky health check flaps and two separate audit passes both flag the same app in the same run. Two subagents now hold two rollback calls for one app, arriving within milliseconds of each other with no ordering guarantee. Whichever one's write lands second wins, silently discarding the first — and neither subagent's transcript shows an error, because from each one's point of view its own call succeeded.

Connection-pool exhaustion, not rate-limit rejection. Sixteen concurrent calls is a small number by web-traffic standards, but a deploy/rollback tool call typically isn't one HTTP request — it's a chain (validate token, read current release, write new release pointer, trigger health check, poll until healthy) that can hold a database connection or a lock for seconds at a time. A control plane sized for "one operator clicking rollback in a dashboard" can have a connection pool small enough that 16 of those chains running at once queues the 17th request behind whichever of the first 16 finishes last, turning a bounded burst into a latency cliff the workflow's own timeout then interprets as a hang — which loops back into the first failure mode.

None of these are new failure modes MCP invented. They're the same idempotency and locking bugs any concurrent API has always had to solve. What's new is that a workflow makes triggering all three in the same run a one-sentence prompt away, on infrastructure many teams still operate as if only a human, clicking one button at a time, would ever call it.

The Idempotency Key That Actually Closes the Retry Gap

The fix for the first failure mode is standard, and it has to live in the tool's contract, not just the implementation. A write tool — deploy, rollback, configure — needs to accept a caller-supplied (or deterministically derivable) idempotency key, and the server needs to treat a repeated call with the same key as "return what already happened," not "do it again":

text
rollback(app: "api", release: "previous", idempotency_key: "wf-8f2a1e-rollback-api")

The key doesn't need to be sophisticated. A workflow script can derive one deterministically from the run ID plus the tool name plus the target app (${workflowRunId}-rollback-${app}), so a retry from the same logical call — whether it's Claude Code's client retrying a timeout or a subagent restarted by the runtime's own r resume-agent control — naturally reuses the same key instead of minting a new one. Server-side, that key maps to a stored result (revision rolled back to, timestamp, status) with a TTL generous enough to outlast a workflow's total runtime — 24 hours is the pattern most production MCP write tools converge on. A second call with a seen key returns the cached result immediately, without touching the control plane's actual rollback logic a second time. Critically, this has to be enforced server-side: documenting "calls are idempotent" in a tool description is a suggestion an agent can't verify, while a server that actually deduplicates by key is a guarantee it doesn't need to.

Read-only tools — status, logs — don't need this; they're naturally idempotent by virtue of not writing anything. The discipline only has to apply to the tools capable of changing infrastructure state, which for a Render-compatible deploy API is a short, known list.

Serializing Writes Per App, Not Per Server

The idempotency key closes the retry gap for one logical call repeated. It doesn't stop two different logical calls — two genuinely separate rollback decisions, from two different subagents — from racing on the same app. That needs a second, independent mechanism: a lock keyed by app ID, held for the duration of a write operation, so a rollback and a deploy (or two rollbacks) targeting the same app serialize instead of interleaving.

This is where the 16-concurrent ceiling actually pays off in server design. A lock scoped per-app means the server's effective concurrency requirement per app is 1, not 16 — the 16 concurrent workflow agents are, in the overwhelmingly common case, each targeting a different app, so a per-app lock adds essentially zero latency to the happy path and only serializes the rare case where two subagents genuinely collide on the same target. That's a cheap lock to hold (a row-level lock in whatever database already tracks each app's current release, or a short-TTL key in a cache layer already in front of the control plane) — cheap enough that there's no excuse for a deploy/rollback MCP server to skip it, because the alternative is a silent lost-write bug that only shows up under exactly the concurrent-agent load Dynamic Workflows now makes routine.

Server-level concurrency is the coarser knob, and it's mostly a sizing question rather than a rate-limiting one: a token bucket sized to comfortably absorb 16 concurrent write calls (burst capacity of 16-32, refill matched to how fast your control plane can actually process a deploy or rollback end to end) means the server spends its effort on correctness — idempotency, per-app locking — rather than on rejecting calls a well-behaved client was never going to send more than 16 of at once. Reserve the harder 429-with-a-machine-readable-reason response for genuinely pathological cases: a misconfigured workflow script, a runaway loop that slipped past the runtime's own 1,000-agent cap, or a tenant running multiple workflows against the same fleet simultaneously — not for the ordinary case of 16 concurrent subagents doing exactly what they were asked to.

The Stateless MCP Core Doesn't Touch Any of This

It's worth being explicit about what the 2026-07-28 MCP specification release candidate's move to a stateless protocol core does and doesn't solve here, since the two changes land in the same conversation. Stateless MCP removes the session handshake and sticky-routing requirement, so a deploy MCP server can scale horizontally behind a plain round-robin load balancer — genuinely useful once a platform is fielding concurrent tool calls from many workflow agents across many tenants at once. But idempotency keys and per-app locks are a data concern, not a protocol concern: they have to live in the control plane's own database, keyed by the app or the idempotency key itself, reachable by whichever stateless server instance happens to handle a given retry. A server that solved horizontal scaling by going stateless but kept its idempotency cache as an in-process map defeats the purpose immediately — the retry that lands on a different instance won't see it, and you're back to double-executing rollbacks, just now with extra steps.

What to Check Before a Workflow Points at Your Deploy Tools

The honest scope of this problem is smaller than "hundreds of concurrent agents hammering your API" made it sound, and that's good news: a server correctly handling 16 concurrent write calls with per-app serialization and idempotent retries is a server that handles the actual Dynamic Workflows load pattern, not a hypothetical one. Before exposing deploy/rollback/configure to an agent that might run inside a workflow:

  1. Every write tool accepts and honors an idempotency key, with server-side deduplication against a store (not just documentation asking the agent to be careful) and a TTL that outlasts a realistic workflow run.
  2. Every write tool acquires a per-app lock before mutating that app's state, so concurrent calls targeting the same app serialize instead of racing.
  3. Timeouts are tuned to the operation, not copy-pasted from a read-tool default — a rollback chain that legitimately takes 12 seconds needs a timeout longer than that, or every legitimate call looks like a hang to the retrying client.
  4. The idempotency cache and the locks live in shared, persistent state (the control plane's database, a cache layer in front of it) — not in a single server process's memory, especially once the server scales horizontally on the strength of the new stateless protocol core.
  5. Rate limiting is sized to the known burst (16, with headroom), not tuned as if the load were unbounded — save aggressive rejection for the pathological cases, not the ordinary ones.

Bex.co is the open-source, AI-native Render alternative — push a git repo, get a running HTTPS service on machines you own. Its deploy/rollback/configure MCP tools are built idempotent and per-app-locked by default, because "an agent fans out 16 subagents at your fleet" stopped being a hypothetical the day Dynamic Workflows shipped. Star the repo on GitHub or deploy your first app today.


Sources:

Related articles

Give your agents a chain backend

Autonomous agents hit RPC endpoints very differently than people do. See what bex router handles on their behalf.

Read the agents guide