Claude Managed Agents
Connect CopilotKit to a hosted Claude Managed Agent over AG-UI, then render a backend tool call as interactive generative UI.
Claude Managed Agents run Claude agents in Anthropic-hosted environments. Anthropic manages the model runtime and workspace; your app starts sessions, streams their events, and supplies any application-specific tools. This recipe connects one of those hosted agents to a compact CopilotKit chat over AG-UI.
The example is a focused finance assistant. Ask what a recurring investment could grow to and the managed agent calculates in its hosted workspace, calls one backend tool, and CopilotKit renders an interactive growth projection inline. It is adapted from Anthropic's Claude Managed Agents × CopilotKit quickstart, reduced to one interaction that fits a Cookbook pane.
How it works#
- A one-time setup script creates a restricted hosted environment and a versioned managed agent.
ManagedAgentsAgentmaps each CopilotKit thread to one Claude Managed Agents session.- The CopilotKit runtime exposes that agent through an AG-UI-compatible SSE endpoint.
- The managed agent calls
show_growth_projectionwith structured numbers. useRenderToolmatches that streamed tool call by name and mounts the React chart in the transcript.
Try it live#
Use the suggested prompt, then adjust the contribution and return sliders in the resulting chart.
Prerequisites#
- Node.js 22 or newer.
- An Anthropic Console account and API key with Claude Managed Agents access.
- An Anthropic organization with 30-day data retention. The default
claude-fable-5model is unavailable under zero data retention.
Run the example#
Clone CopilotKit and enter the standalone showcase:
git clone https://github.com/CopilotKit/CopilotKit.git
cd CopilotKit/examples/showcases/claude-managed-agents
npm install
cp .env.example .envAdd your key to .env:
ANTHROPIC_API_KEY=sk-ant-your_key
# Optional; defaults to claude-fable-5
ANTHROPIC_MODEL=claude-haiku-4-5Provision the persistent environment and managed agent once, then start the server and frontend:
npm run setup
npm run devOpen http://localhost:5173. npm run setup writes the generated resource IDs to the
gitignored agent-ids.json; rerunning it reuses those IDs instead of creating more billable resources. Use
npm run setup -- --force only when you intentionally want a replacement environment and agent.
Try it#
Select the suggested prompt or ask:
If I invest $500/month at a 7% annual return, what will I have in 20 years?Claude passes the assumptions to show_growth_projection and keeps its prose short while the chart
carries the result. Move either slider to explore the assumptions without starting another agent run. A
follow-up in the same CopilotKit thread resumes the same managed session and workspace.
The key pieces, in code#
The setup script creates long-lived managed resources. The environment blocks outbound hosts, package managers, and MCP servers, while the agent disables its complete built-in toolset. The runtime adds only the focused financial visualization tool when it creates a session:
const environment = await client.beta.environments.create({
name: `financial-assistant-demo-${Date.now().toString(36)}`,
config: {
type: "cloud",
networking: {
type: "limited",
allowed_hosts: [],
allow_package_managers: false,
allow_mcp_servers: false,
},
},
});
const model = process.env.ANTHROPIC_MODEL ?? "claude-fable-5";
const agent = await client.beta.agents.create({
name: "financial-assistant",
model,
system: ASSISTANT_SYSTEM,
tools: [{
type: "agent_toolset_20260401",
default_config: { enabled: false },
}],
});The model is fixed when the managed agent is provisioned. Changing ANTHROPIC_MODEL later does not update that
agent. Run npm run setup -- --force with the new model, then replace the generated environment and agent IDs
wherever the demo is deployed.
At runtime, the AG-UI adapter owns managed-session creation and maps it to the CopilotKit thread. The visual tool
is supplied as a session override, so changing its schema does not require a new managed-agent version.
CopilotSseRuntime is imported under that exact name; it is CopilotKit's V2 runtime for direct SSE connections.
The adapter requires only the managed agent and environment IDs, while this recipe adds backendTools for the
interactive projection:
import { CopilotSseRuntime } from "@copilotkit/runtime/v2";
import { ManagedAgentsAgent } from "@ag-ui/claude-managed-agents";
import {
createCopilotRequestBodyParser,
createFinancialAssistantAgentConfig,
} from "./runtimeLimits";
import { configureDemoRunLimits } from "./requestLimits";
const runtime = new CopilotSseRuntime({
agents: {
"financial-assistant": new ManagedAgentsAgent(
createFinancialAssistantAgentConfig(ids),
),
},
});
configureDemoRunLimits(app, createCopilotRequestBodyParser());The runtime helper keeps the public demo bounded: it leaves turnTimeoutMs at 90 seconds and rejects request bodies
over 256 KB. The adapter already serializes runs per thread.
Only the canonical POST /api/copilotkit/agent/financial-assistant/run path, with one optional trailing slash, can
start the provider-backed agent. Run aliases, unknown agents, and suggestion routes are rejected before the runtime.
Provider-like attempts are limited to 20 per client IP per minute before body parsing, and the process accepts
2,000 successful run requests per 24-hour window. Other CopilotKit routes do not use these allowances.
The single backend tool declares the exact data the UI needs. Its handler only acknowledges the render because the streamed tool call itself is the user-visible result:
export const financialAssistantTools: BackendCustomTool[] = [{
name: "show_growth_projection",
description: "Render an interactive compound-growth chart in the chat.",
parameters: {
type: "object",
properties: {
title: { type: "string" },
initialAmount: { type: "number", minimum: 0 },
monthlyContribution: { type: "number", minimum: 0 },
annualReturnPercent: { type: "number", minimum: 0, maximum: 30 },
years: { type: "integer", minimum: 1, maximum: 50 },
},
required: ["title", "initialAmount", "monthlyContribution", "annualReturnPercent", "years"],
},
handler: () => "Rendered the projection to the user.",
}];CopilotKit matches the AG-UI tool-call events by name with
useRenderTool. The runnable example validates and coerces the streamed
arguments with Zod before mounting the chart:
useRenderTool(
{
name: "show_growth_projection",
parameters: growthSchema,
render: vizRender(
growthSchema,
"Building growth projection…",
GrowthProjection,
),
},
[],
);Deploy it#
The example supports a single-process deployment: build the Vite frontend, then let the Express server serve
both web/dist and /api/copilotkit.
npm install && npm run build && npm startSet ANTHROPIC_API_KEY and the two IDs printed by npm run setup:
ANTHROPIC_ENVIRONMENT_ID=env_...
ANTHROPIC_AGENT_ID=agent_...
ALLOWED_ORIGINS=https://your-app.example.comANTHROPIC_MODEL is used by setup, not by the running server. To change models on Railway, reprovision first and
then update ANTHROPIC_ENVIRONMENT_ID and ANTHROPIC_AGENT_ID with the replacement IDs.
On Railway, the per-IP limiter uses Railway's X-Real-IP header and normalizes IPv6 client addresses with
express-rate-limit. It does not enable Express proxy trust or use X-Forwarded-For. Missing or malformed
X-Real-IP values share one conservative bucket; local and direct deployments use the socket-derived
request.ip instead.
Protect the runtime before going public
The example endpoint has no user authentication, and every message spends your Anthropic API credits.
When ALLOWED_ORIGINS is set, the server rejects requests without the exact matching Origin header, including
when the frontend and runtime are hosted separately. That browser check is not authentication because custom
clients can forge the header. The public demo therefore adds route-specific per-IP and process-wide in-memory
limits. The counters reset on restart and are not shared across replicas, and the 2,000-start allowance is not a
fixed dollar ceiling. Scope the API key to a dedicated, non-default
Anthropic workspace with the desired monthly spend
limit as the durable cost backstop. Put the runtime behind your application session check before using this
pattern in production.
The adapter's default session store is in memory. A server restart starts fresh managed sessions, while the
provisioned environment and agent remain reusable. Replace it with a durable SessionStore if thread continuity
must survive deploys or multiple server replicas.
Going further#
- Add another focused backend tool and matching
useRenderToolcomponent; the AG-UI transport does not change. - Persist the thread-to-session mapping in your database for multi-instance deployments.
- Add user authentication, quotas, and audit logging before exposing a credit-spending runtime.
- Add Rich Threads through CopilotKit Intelligence so each savings scenario becomes a named, persistent conversation whose messages and generated chart reopen across devices.
Get the code#
Full source: examples/showcases/claude-managed-agents. The example includes the runtime and frontend.