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.
  • ManagedAgentsAgent maps 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_projection with structured numbers.
  • useRenderTool matches that streamed tool call by name and mounts the React chart in the transcript.
Architecture of the Claude Managed Agents recipe: a request flows from CopilotKit chat through CopilotSseRuntime and ManagedAgentsAgent to a Claude Managed Agent in an Anthropic-hosted workspace; the agent calls show_growth_projection, which streams back as an AG-UI tool event and is rendered by useRenderTool as an interactive React chart.

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-5 model 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 .env

Add your key to .env:

.env
ANTHROPIC_API_KEY=sk-ant-your_key
# Optional; defaults to claude-fable-5
ANTHROPIC_MODEL=claude-haiku-4-5

Provision the persistent environment and managed agent once, then start the server and frontend:

npm run setup
npm run dev

Open 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:

server/src/setup.ts
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:

server/src/index.ts
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:

server/src/financialAssistantTools.ts
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:

web/src/viz/renderers.tsx
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 start

Set 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.com

ANTHROPIC_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 useRenderTool component; 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.