Claude Agent Server: Build a WebSocket Wrapper for Claude in Bun E2B
Claude Agent Server: Build a WebSocket Wrapper for Claude in Bun E2B
If you’ve been hunting for a clean, battle‑tested way to expose the Claude Agent SDK through a WebSocket interface, the claude‑agent‑server repository is exactly what you need. This repository brings together the best of three worlds:
- The Claude Agent SDK – a powerful API for interacting with Claude models.
- Bun – a JavaScript runtime that compiles to machine‑code for lightning performance.
- E2B sandboxes – a secure, isolated environment that spins up on demand.
In this article, we’ll walk through installing the code, building an E2B sandbox, connecting a client, and tweaking server settings. By the end, you’ll have a fully functional ChatGPT‑style assistant you can run wherever you need.
1. What Does the Repo Do?
The claude‑agent‑server repo is a monorepo with two primary sub‑packages:
| Directory | Purpose |
|---|---|
packages/server/ |
A minimal Bun server that routes WebSocket traffic to the Claude Agent SDK. It exposes two HTTP endpoints: /config for setting query options, and /ws for real‑time communication. |
packages/client/ |
A reusable TypeScript library (@dzhng/claude‑agent) that abstracts the WebSocket handshake, sandbox creation, and message serialization. |
Key features:
- E2B deployment – The
build:e2bscript packages the repo into a reusable sandbox template. - One‑to‑one WebSocket – Only a single client can connect at a time, simplifying concurrency.
- Custom tooling – Choose allowed tools, system prompts, and models via
/configbefore establishing the connection. - Local test client – Run
bun run test:localto run the bundled example againstlocalhost:3000. - Extensible – Edit
packages/server/index.tsormessage‑handler.tsto change agent behavior or add new commands.
2. Prerequisites
| Item | Minimum Version |
|---|---|
| Git | 2.30+ |
| Bun | 1.3+ |
| Node.js (optional, for npm usage) | 18+ |
| E2B account | Free tier will do |
| Anthropic API key | Valid key for the Claude model you’ll use |
If you’re new to Bun, install it with:
curl fnm.vercel.app | bash -s -- -b "$HOME/.bun"
source "$HOME/.bun/completion.bash"
3. Clone the Repo and Install Dependencies
git clone https://github.com/dzhng/claude-agent-server.git
cd claude-agent-server
bun install
The repository ships a .env.example file that defines the required keys:
cp .env.example .env
Edit the file to point to your API keys:
ANTHROPIC_API_KEY=sk-ant-·····
E2B_API_KEY=e2b_your-key-here
Tip: Keep your keys secret; never commit the real
.envfile.
4. Building an E2B Sandbox Template
E2B provides on‑demand, isolated sandboxes. The server is packaged into a ready‑to‑deploy template named claude‑agent‑server.
bun run build:e2b
What happens under the hood?
- A Base Bun‑1.3 image is created.
- The repo is cloned into the sandbox.
bun installinstalls all runtime dependencies.- The server starts automatically on port 3000.
- The resulting template is published to your E2B workspace.
Once the build completes, you’ll see an E2B template list with an entry named claude‑agent‑server. It’s now ready to be spun up whenever you need a fresh sandbox.
5. Using the Client Library
The @dzhng/claude‑agent package is a thin wrapper that handles sandbox creation, WebSocket connection, and message handling. Install it in your project:
# In your own project directory
npm i @dzhng/claude-agent
# or, with Bun
bun add @dzhng/claude-agent
5.1. Quick‑Start Example
import { ClaudeAgentClient } from '@dzhng/claude-agent';
const client = new ClaudeAgentClient({
e2bApiKey: process.env.E2B_API_KEY!,
anthropicApiKey: process.env.ANTHROPIC_API_KEY!,
template: 'claude-agent-server', // Use the E2B template name
debug: true,
});
await client.start();
client.onMessage((msg) => {
if (msg.type === 'sdk_message') {
console.log('Claude:', msg.data);
}
});
await client.send({
type: 'user_message',
data: {
type: 'user',
session_id: 'my-session',
message: {
role: 'user',
content: 'Hello, Claude!',
},
},
});
// When you’re done:
await client.stop();
Sandbox lifecycle: The client creates a new sandbox, keeps it alive until you call
stop(), and then automatically tears it down, freeing resources.
5.2. Connecting to a Local Server
During development you may want to test against your local server. The same client supports a connectionUrl:
const client = new ClaudeAgentClient({
connectionUrl: 'http://localhost:3000',
anthropicApiKey: process.env.ANTHROPIC_API_KEY!,
});
Now you can use bun run start:server to launch the local Bun server and see real‑time output in the console.
6. Customizing the Server
The server is deliberately light‑weight so you can easily extend it.
6.1. Edit the Core
Main entry point: packages/server/index.ts. It sets up the HTTP and WebSocket routes, handles configuration, and forwards incoming messages to the Claude SDK.
6.2. Modify Message Handling
packages/server/message-handler.ts contains the logic for translating SDK responses to JSON messages. Add or change handlers as needed.
6.3. Add New Tools
The allowedTools array in the /config call limits which tools the agent can use (e.g., read_file, write_file). Add custom tool definitions and expose them via the SDK.
7. Testing Locally
Run a local instance and test with the bundled client:
# In the repo root
bun run start:server # Start on http://localhost:3000
Open another terminal and invoke the test client:
bun run test:local
This client connects to localhost:3000, sends a few test messages, and prints the SDK responses. It’s a great way to verify changes before rebuilding the E2B image.
8. Building a Custom E2B Template Name
The default template name is claude-agent-server, but you can change it via the client constructor or by editing the build script:
const client = new ClaudeAgentClient({
template: 'my-custom-claude-template',
...
});
or modify packages/e2b-build/build.prod.ts’s Template() instantiation.
9. Security & Clean‑Up
- The sandbox is only alive while your client connection is open. When
stop()is called, E2B automatically kills the sandbox. - API keys are passed either via the environment or the
/configendpoint. The SDK respects the key you provide – it does not leak your key to the client. - The server only allows a single WebSocket connection at a time, preventing accidental over‑loading.
10. Wrap‑Up & Next Steps
You now have a reusable Claude Agent WebSocket wrapper, deployable in a sandboxed, isolated environment and easily integrated into any TypeScript or JavaScript application.
- Deploy a bot – Use the client library in a Telegram, Discord, or Slack bot.
- Create a custom agent – Extend the server to add domain‑specific tools.
- Publish a starter kit – Share your sandbox template with others.
For more details, always refer to the fully documented README and the inline code comments in the repo. Happy coding!