◉CLAUDE LABJP
●2.1.302 — Fork subagents are now on by default in claude -p, the Agent SDK and the desktop app. Turn them off with CLAUDE_CODE_FORK_SUBAGENT=0 (10/16)●2.1.301 — Unattended retries now stop on their own when a usage limit is hit instead of waiting (10/15). Latest is 2.1.304 (10/19)●SONNET 4.5 — Retires on the Claude API on 11/30, 40 days left. Migrate to Sonnet 5.5●WINDOWS — Reports say hooks break silently only on Japanese-locale Windows; check the character encoding first●FAST MODE — Opus 5.5 fast mode is out of research preview and no longer needs the beta header (10/15)●NEW — I Put a Ten-Line Promise File at the To…●2.1.302 — Fork subagents are now on by default in claude -p, the Agent SDK and the desktop app. Turn them off with CLAUDE_CODE_FORK_SUBAGENT=0 (10/16)●2.1.301 — Unattended retries now stop on their own when a usage limit is hit instead of waiting (10/15). Latest is 2.1.304 (10/19)●SONNET 4.5 — Retires on the Claude API on 11/30, 40 days left. Migrate to Sonnet 5.5●WINDOWS — Reports say hooks break silently only on Japanese-locale Windows; check the character encoding first●FAST MODE — Opus 5.5 fast mode is out of research preview and no longer needs the beta header (10/15)●NEW — I Put a Ten-Line Promise File at the To…
Articles/API & SDK
⬡ API & SDK/2026-07-24Advanced

One Shared Managed Agent Definition, Overridden Per Session

A design for collapsing four near-identical Managed Agents into one base definition, using agent version pinning and session-local overrides — with a validated factory and the traps I hit along the way.

Managed Agents4Agent SDK5MCP57Operations22Multi-tenant

✦ Premium Article

As an indie developer, I run several agents that behave almost identically.

The only differences are part of the system prompt, which MCP connectors they reach, and which skills they load. And yet, for a while, I kept a completely separate agent definition for each site.

Every time I fixed something shared, I had to sync four places by hand. Fix one and forget another, and a single night's batch would run on a stale prompt. Watching near-identical definitions drift apart, little by little, quietly wears you down.

Reading the Managed Agents model properly is what finally let me collapse this. One base agent definition, kept whole. Site-specific color then lives in two separate layers: which agent version a session points at, and a session-local override applied after the session exists.

Here is how I shaped that into something that doesn't go wrong at 3 a.m., along with the validation code I actually use.

Why one base definition instead of four

I had three real options.

ApproachSyncing shared partsVisibility of differencesPermission-accident risk
A separate definition per siteManual, drifts easilyGood (independent)Low, but definitions keep multiplying
One definition with branching inside the promptNot neededPoor (prompt bloat)Medium (fuzzy boundaries)
One definition, differentiated by version and sessionNot neededGood (differences are explicit)Can be kept low by design

What decided it for me was that the differences end up in one readable place in code.

"For this site, these MCP servers and these tools" sits right where the session starts, in plain sight. That's far easier to reason about than burying conditionals deep in a prompt.

There is one thing that trips you up first, though.

The counterintuitive part: you cannot put model or system on a session

The first version of my code tried to hand the session its own model, its own system prompt, and its own tools, all at creation time.

Those arguments don't exist.

The agent field on sessions.create() accepts exactly two things: an agent ID string, or a pointer object of the form { type: "agent", id, version }. model, system, tools, mcp_servers, and skills are all top-level fields on agents.create() — on the agent object itself. A session only points at that definition.

What I realized, after reading the session parameter table for the third time, was that the idea of coloring each session wasn't wrong. The layer where color can be applied simply sat lower than I had assumed.

Here's how the two layers divide up:

What you want to varyHow you actually do itWhen it applies
model / system / skillsAgent versions — each update appends a new immutable versionDecided by which version the session points at
tools / mcp_servers / vault_idsA session-local override via sessions.update()While the session is idle
The container setupenvironment_id, given at session creationSession creation only

A session-local override creates no new agent version and doesn't propagate back to the agent object. It applies inside that one session and disappears with it. That property is exactly why the bookkeeping section below exists.

✦

Thank you for reading this far.

Continue Reading

What follows includes implementation code, benchmarks, and practical content we hope you'll find useful. This site runs without ads — server and development costs are supported entirely by members like you. If it's been helpful, we'd be truly grateful for your support.

WHAT YOU'LL LEARN
✦A validated startup factory that separates what belongs on the agent from what belongs on the session
✦Why session-local arrays replace rather than merge, and the trap that silently dropped my tools
✦How to decide swap granularity so one base agent can serve several similar jobs
Secure payment via Stripe · Cancel anytime
✦

Unlock This Article

Get full access to the rest of this article. Buy once, read anytime. This site is ad-free — your support goes directly toward keeping it running.

or
Unlock all articles with Membership →
Share

Thank You for Reading

Claude Lab is ad-free, supported entirely by members like you. We publish practical guides daily with implementation code, benchmarks, and production-ready patterns. If you've found it useful, we'd love to have you on board.

  • ✦Copy-paste ready implementation code
  • ✦New advanced guides published daily
  • ✦$5/mo or $10 for lifetime access
View Membership →

Related Articles

⬡ API & SDK2026-07-31
The 400-Character Preview That Was Still Holding On to a Megabyte
Truncating tool output in your own MCP server does not free the original. Node's sliced strings keep the parent alive. Here is the measured 13-character boundary, which flattening tricks actually work, and a heap-snapshot audit script that counts the retained parents for you.
⬡ API & SDK2026-09-21
The morning a deprecation notice appeared, I checked versions before editing config
Claude Desktop and Cowork stop accepting older managed-configuration spellings on October 7. Here is how I sorted mine into what to fix now, what to fix after upgrading, and what changes meaning on its own.
⬡ API & SDK2026-09-09
The agent I built in the Console could not be adopted by ant apply
ant apply refuses some drift and silently duplicates other resources. Here is where that line sits, why claude-lock.json belongs in your commits, and the five doors a second copy walks through.