Claude Managed Agents are agents that Anthropic runs for you, with server-side sessions, sandboxed tools, and an event stream your app consumes.
Vercel's Chat SDK gives that agent a chat surface through a single type-safe handler, with adapters for the web, Slack, Teams, Discord, Telegram, and WhatsApp.
The new Anthropic quickstart pairs Claude Managed Agents with Chat SDK into a research analyst in a browser chat. Each conversation maps to a single persistent session; replies stream token by token, and a live feed shows the agent's activity.
Copy link to headingOverview
In this guide, you'll learn how to:
- Provision a Managed Agents research analyst and its sandbox environment
- Run the Chat SDK web chat locally and watch the agent research in real time
- Update the agent's model and system prompt without creating duplicates
- Deploy the chat to Vercel using Vercel Services
Copy link to headingPrerequisites
Before you begin, make sure you have:
- Node.js 22.9 or later
- An Anthropic API key from platform.claude.com, or run
ant auth login - An Anthropic organization with Managed Agents access
Copy link to headingHow it works
Copy link to headingSessions are the conversation store
The quickstart holds no state of its own. Every conversation in the browser maps to exactly one Managed Agents session, and the sessions API backs each part:
| In the browser | What powers it |
|---|---|
| Starting a new chat | POST /api/sessions creates a session, and useChat uses the returned session ID as its thread ID |
| Conversation sidebar | sessions.list() |
| Replaying a transcript | sessions.events.list() |
| Follow-up questions | The session holds the research context server-side |
Copy link to headingThe web adapter needs no platform registration
The demo uses the Chat SDK's web adapter, so there is no chat platform to register with. You don't need a Slack app, a webhook to verify, or a tunnel. The only credential is your Anthropic auth.
Copy link to headingReplies and activity stream on separate channels
When a message arrives, the adapter holds the HTTP response open while the bridge holds an Anthropic event stream open for the same turn, forwarding each agent.message event to the response. The acknowledgment appears within seconds, and the finished brief arrives with the same response minutes later.
Tool calls, model requests, and thinking indicators stream separately. The page receives them as a live feed over GET /api/activity while the turn runs.
Copy link to headingThe agent runs entirely on Anthropic's side
The tool loop, sandboxed web research, session state, compaction, and prompt caching all occur within the session.
The agent's shell tool is disabled by design. The analyst reads arbitrary web pages, and prompt-injected content combined with an auto-approved shell and open egress would create an exfiltration path. The brief relies on web search, web fetch, and the file tools instead.
Copy link to headingSteps
Copy link to heading1. Clone the quickstart
Clone the repository and move into the project:
Copy link to heading2. Install dependencies
This installs:
- Chat SDK packages:
chat: the core SDK with the type-safe handler and message primitives@chat-adapter/web: the web adapter that serves the browser chat surface@chat-adapter/state-memory: in-memory adapter state
- Anthropic SDK:
@anthropic-ai/sdk0.109.0 or later - Server and UI: Hono and the React chat page's dependencies
Copy link to heading3. Configure authentication
Copy the environment template:
Then add Anthropic auth in one of two ways:
- Uncomment
ANTHROPIC_API_KEYin.envand paste a key from platform.claude.com. - Run
ant auth loginonce and leave the variable out, since the SDK discovers CLI credentials on its own.
Copy link to heading4. Provision the agent and environment
This one-time step creates two persistent resources:
- A cloud sandbox environment where the agent's tools run
- The analyst agent, configured with a model (Claude Opus by default, overridable with
QUICKSTART_MODEL), a system prompt tuned for chat-length research briefs, and a toolset with bash disabled
The script prints both IDs:
Paste the two IDs into .env. Don't re-run npm run setup later, because it would create a duplicate agent. To change the agent, use npm run update-agent.
Copy link to heading5. Run the chat locally
Start the development server:
Open http://localhost:3000, select New chat, and ask for a brief on any topic. Here's what you should see:
- An acknowledgment message within seconds
- The activity feed filling with searches for one to three minutes
- The brief streaming in token by token
- A collapsed tool-call trace and a "Brief ready" card linking to the session's trace in the Anthropic Console
To confirm that sessions hold all the state, restart the development server and reload the page. The sidebar and every transcript come back intact from the sessions API, and follow-up questions in a replayed chat still work because the session keeps the research context server-side.
Copy link to heading6. Customize the agent
The agent's entire identity (name, model, and system prompt) lives in setup/agent-config.ts. After editing it, push the change as a new agent version:
Running sessions keep their pinned version and new chats use the latest, so an edit never disrupts a conversation in flight.
Copy link to headingDeploying to Vercel
Deploying to Vercel comes down to a Vite build for the page, a Node entry for the API, environment variables, and a longer function duration.
Copy link to heading1. Serve the page from the CDN and the API from a function
The local Node server serves both the chat page and the API. On Vercel, use Vercel Services to deploy them as two services in the same project:
- A Vite service builds the existing
web/directory and serves the generated frontend assets from Vercel’s CDN. - A Node service runs the Hono application and handles the four
/apiroutes throughdeployedApi().
First, create the API entry point for the Hono server by adding a new server.ts file at the project root:
This adapts the Fetch-native Hono application to a Node HTTP server. Vercel imports the exported server in production, while the local development command starts it normally.
Now, install Vite and its React plugin as development dependencies:
Add these scripts alongside the existing ones in package.json:
Then, configure Vite to read the frontend from web/ and write the production bundle to dist/. For this, create a new file called vite.config.ts at the project root:
Finally, create a vercel.json file at the project root to configure Vercel Services and the rewrites:
With these changes in place, test the setup by running:
This will start both services locally and give you a single URL to test the project. Once you are ready, you can deploy the project to Vercel by running:
Vercel builds the frontend into dist/ and serves it through the web service. Requests for the page and its generated assets are handled by the CDN-backed Vite deployment, while /api/* requests are routed to the Node service running the Hono application on Vercel Functions.
Copy link to heading2. Add production credentials
CLI credentials from ant auth login only exist on your machine, and the Anthropic client reads process.env at module scope, so the deployment needs its configuration set as environment variables before imports run. In your project settings, open Environment Variables and add:
| Variable | Value |
|---|---|
ANTHROPIC_API_KEY | The key from platform.claude.com. The SDK also accepts ANTHROPIC_AUTH_TOKEN as the bearer-token form |
CLAUDE_AGENT_ID | The agent_... ID printed by npm run setup |
CLAUDE_ENVIRONMENT_ID | The env_... ID printed by npm run setup |
deployedApi() checks these on every request and names the missing variable in its 500 response. Don't run npm run setup as part of the build: the agent and environment are persistent resources, and re-running setup creates duplicates. Provision once from your machine and reuse the IDs.
Copy link to heading3. Learn what stays per instance
The Managed Agents session holds every conversation, so createMemoryState() in src/bot.ts carries only Chat SDK internals, and the quickstart keeps memory state in production on a single instance.
Fluid compute runs many requests on one instance but scales to more instances under load, and three behaviors in the quickstart are process-local:
| Behavior | Where it lives | On multiple instances |
|---|---|---|
| Message dedup | createMemoryState() in src/bot.ts | Duplicate deliveries of the same message ID could each start a turn. useChat sends each message once, so this matters when you add retries or a second surface. The fix is createRedisState() from @chat-adapter/state-redis, which auto-detects REDIS_URL |
| Turn ordering | enqueueTurn in src/managed-agents.ts | Two instances streaming the same session would each post every reply. The browser serializes sends per conversation, so this appears when something else writes into the same session concurrently |
| Activity feed fan-out | src/activity.ts | When GET /api/activity lands on a different instance than the turn's /api/chat, the feed shows empty for that turn. This is cosmetic: the chat lane is unaffected |
For a demo or low-traffic deployment, ship as-is.
Before scaling past one instance, make the Redis swap and treat the other two rows as known behavior rather than bugs to chase.
Copy link to heading4. Raise the max duration for the chat route
The research turns hold the /api/chat response open for one to three minutes while the brief streams. Vercel Functions on fluid compute default to a 300-second maximum duration, which covers a typical turn, and Pro and Enterprise teams can raise the limit to 800 seconds for longer research runs.
To raise the project default, open the Functions page in your project settings. Under Advanced Settings, set a new value for Function Max Duration.
You can also set maxDuration per function in vercel.json if only the chat route needs the longer window. For runs beyond 800 seconds, extended max duration supports per-function limits up to 30 minutes on supported Node.js and Python runtimes. Project defaults stay capped at 800 seconds.
Fluid compute fits this workload well. Active CPU billing only applies while your code executes and pauses while the function waits on I/O, and this function spends nearly the entire turn waiting on Anthropic's event stream.
Copy link to heading5. Lock down authentication first
Deploying makes every route public, so complete the getUser replacement described in best practices before your first production deploy. The demo authentication treats every caller as the same user, which is safe on 127.0.0.1 and nowhere else: anyone who reaches the URL can run research turns on your bill, list your sessions, and replay every transcript.
Vercel's Deployment Protection can gate the URL while getUser is still the demo version, but treat it as a stopgap rather than a fix.
Copy link to headingBest practices
Copy link to headingReplace the demo authentication before exposing the server
The getUser function in src/bot.ts is the security boundary for every route: /api/chat, /api/sessions, /api/history, and /api/activity. The demo version accepts every caller as the same local user, which is why the server binds to 127.0.0.1 by default.
Before setting HOST=0.0.0.0 or deploying:
- Replace
getUserwith your real session lookup (e.g., with Better Auth) - Scope sessions per user by writing the resolved user ID into session metadata
Copy link to headingNever trust a browser-supplied conversation ID
The browser sends session IDs, and the server's Anthropic credentials can see every session in the workspace. The quickstart's ownedSession() check in src/managed-agents.ts verifies that an incoming ID resolves, belongs to this agent, and isn't archived before any route touches it. If you fork the project, keep that function in the path of anything that accepts a conversation ID from a client.
Copy link to headingRaise idle timeouts for long-lived responses
The /api/chat response stays open for the whole research turn, often minutes at a time. A reverse proxy or load balancer with a default idle timeout (often 60s) will close the response right when the brief is due.
On Vercel, this limit is the function's max duration. If you host the app elsewhere or put your own proxy in front of it, raise the proxy's idle timeout for the /api/chat route and verify that your platform allows long streaming responses.
Copy link to headingTroubleshooting
| Symptom | Likely cause | Fix |
|---|---|---|
| Replies arrive whole but never stream | The organization doesn't have session streaming enabled, or the installed @anthropic-ai/sdk is older than 0.109.0 | Upgrade the SDK to 0.109.0 or later and confirm streaming is enabled for your organization |
| Every request fails with a 404 | The organization has no Managed Agents access. The SDK sends the managed-agents-2026-04-01 beta header on every call, so an org without access rejects the first request | Use an organization that has Managed Agents access |
| 400 Invalid agent ID | .env still contains the agent_... placeholder | Re-paste the IDs printed by npm run setup |
| Acknowledgment arrives, then a network error replaces the brief | Something between the browser and the server closed the long-lived response. The turn still finished server-side | Reopen the chat from the sidebar to see the reply, then raise the function's max duration or the proxy's idle timeout |
Copy link to headingResources and next steps
- Move the same handler to Slack, Teams, Discord, Telegram, or WhatsApp by adding an adapter in
src/bot.ts. See the Chat SDK adapters directory and the porting notes in the quickstart'sskill.md - Learn how Vercel builds and runs Hono apps in the Hono documentation
- Learn more about the Chat SDK's core patterns in the documentation
- Explore sessions, environments, memory stores, and multi-agent rosters in the Claude Managed Agents documentation
- Learn more about Vercel Services