◉CLAUDE LABJP
●2.1.309 — Sandbox and permission checks are tighter, and hooks and PowerShell on Windows are fixed (10/22)●2.1.307 — Fixes MCP failing to reconnect after sleep or a keychain lock (10/21). 2.1.306 also fixed the false claude -p login message●SONNET 4.5 — Retires on the Claude API on 11/30, 37 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.309 — Sandbox and permission checks are tighter, and hooks and PowerShell on Windows are fixed (10/22)●2.1.307 — Fixes MCP failing to reconnect after sleep or a keychain lock (10/21). 2.1.306 also fixed the false claude -p login message●SONNET 4.5 — Retires on the Claude API on 11/30, 37 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-04-10Advanced

The Boundaries That Trip You Up Moving Claude Managed Agents to Production — Agent / Environment / Session, and Keeping Keys Out of the Sandbox

A production design walkthrough for Claude Managed Agents, checked against the SDK's actual type definitions. Covers which object owns sandbox control, persistent memory, credentials, delegation, and cost measurement.

managed-agents6production111architecture10api39enterprise5security20

✦ Premium Article

I Assumed I Could Just Add a Config Block

When I started moving a Managed Agents prototype toward something production-shaped, my mental model was simple: keep adding blocks to agents.create() — a sandbox block, a memory block, an audit block — and eventually the shape would be right. Configuration lives in one place, surely.

None of those blocks exist.

Rather than keep guessing, I installed the SDK and read the types directly. In @anthropic-ai/sdk 0.120.0, the top level of AgentCreateParams holds exactly ten fields:

FieldWhat it carries
name / descriptionHuman-readable identity
modelModel ID — a bare string, or an object carrying id and speed
systemSystem prompt
toolsBuilt-in toolset / MCP tools / custom tools
mcp_serversMCP server connections
skillsSkill references
multiagentDelegation roster
metadata / betasArbitrary key-values / beta headers

sandbox, memory, observability, optimization, audit, permissions, checkpoint, lifecycle — zero matches for every one of them. There is no instructions either; the field is system.

More fundamentally, client.agents is undefined. The surface lives under client.beta.agents. And client.beta.agents.batch, .pipelines, .secrets, and client.beta.sessions.run all return undefined as well.

So the blueprint I had in my head — "declare everything on the agent" — was never a real shape to begin with. Carry that misreading into a design review and you end up redrawing the skeleton during implementation. What follows is a walk through each boundary I misread, in the order the mistakes cost me time.

If you're new to the platform, the concepts and setup path are covered in the complete guide to Claude Managed Agents — that's the smoother entry point.

The Skeleton Splits Three Ways

Configuration doesn't gather in one place because responsibility is split across three objects.

ObjectOwnsLifetime
AgentModel, system prompt, tools, MCP servers, skills, delegation rosterPersistent, versioned
EnvironmentHow the container is provisioned (runtime, network policy)Persistent, reused
SessionOne interaction. References an agent and environment; carries resources, vaults, budgetDisposable

The agent loop itself runs on Anthropic's orchestration layer. The container is where tools execute. Once that division clicks, the absence of sandbox settings on the agent stops feeling like an omission: the agent's persona and the workshop it operates in are defined separately and composed at session time.

The minimal shape looks like this:

import Anthropic from "@anthropic-ai/sdk";
 
const client = new Anthropic();
 
// 1. The workshop template (reused across sessions)
const env = await client.beta.environments.create({
  name: "report-workspace",
  config: {
    type: "cloud",
    networking: { type: "unrestricted" },
  },
});
 
// 2. The agent — persistent and versioned. Never create this in the request path.
const agent = await client.beta.agents.create({
  name: "sales-report-agent",
  model: "claude-sonnet-4-6",
  system: "You produce monthly sales reports. Always source figures from attached data.",
  tools: [{ type: "agent_toolset_20260401" }],
});
 
// 3. The session — created per run, referencing the agent by ID
const session = await client.beta.sessions.create({
  agent: agent.id,
  environment_id: env.id,
  title: "April 2026 monthly report",
});

One operational note that matters more than it looks: don't call agents.create() inside your request path. Agents are persistent objects, so creating one per request quietly accumulates near-duplicate configs. Store the returned ID and reference it from then on.

Environment names are unique, too. Recreating one with an existing name returns a 409. If you want CI to run this idempotently, write the create-then-fall-back-to-lookup branch up front rather than discovering it on a Friday deploy.

✦

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
✦Catch the wrong-object mistake early — knowing which of Agent / Environment / Session owns each setting, grounded in the SDK's own types rather than guesswork
✦Draw a precise line around what the Vault boundary protects, so MCP credentials never enter the sandbox and you can say exactly what that does and does not buy you
✦Measure runtime spend instead of estimating it, using the active_seconds and list_cost the session reports about itself
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-04-26
Building a Scalable Real-Time AI Chat Server with Claude API × WebSocket × Redis Pub/Sub — Node.js Production Architecture, Multi-User Management, and Cost Control
Running a real-time AI chat server on Claude API, WebSocket, and Redis Pub/Sub in production. SSE trade-offs, multi-instance routing, and how stop latency quietly corrupts per-user budgets — with the instrumentation code to measure it.
⬡ API & SDK2026-04-23
Using tool_choice to Cut Wasted Inference: Four Modes and Cost Patterns for Production
tool_choice is one of the most underused parameters in the Claude API. The four modes — auto, any, tool, and none — each change both behavior and token cost. Here are the patterns I reach for in production, with runnable code.
⬡ API & SDK2026-03-27
Claude API Production Resilience Patterns — Model Routing, Circuit Breakers, and Fallback Strategies for Indie Teams
Production resilience patterns for Claude API: circuit breakers, intelligent model routing, fallback chains, exponential backoff with jitter, and disaster recovery — with TypeScript implementations and operational notes from an indie developer.