@koda-sl/baker-bridge
v0.28.0
Published
HTTP server wrapping the Claude Agent SDK with SSE streaming, WebSocket, and async endpoints
Maintainers
Readme
@koda-sl/baker-bridge
HTTP server wrapping the Claude Agent SDK with WebSocket, SSE streaming, and async endpoints. Designed for running Claude Code as a headless agent with real-time bidirectional communication and Convex callback relay.
Installation
npm install @koda-sl/baker-bridge
# or
pnpm add @koda-sl/baker-bridgeQuick Start
CLI
npx @koda-sl/baker-bridge honoProgrammatic
import { createServer } from "@koda-sl/baker-bridge/hono";
const server = createServer();
await server.start();
// Server listening on http://0.0.0.0:3000Environment Variables
| Variable | Description |
|---|---|
| AUTH_TOKEN | Bearer token for authenticating requests |
| BAKER_CONVEX_SITE_URL | Convex site URL for callback relay (e.g. https://your-deployment.convex.site) |
| BAKER_API_KEY | API key for Convex callbacks (bk_...) |
| ANTHROPIC_API_KEY | Anthropic API key (required by the Agent SDK) |
All variables are validated at startup via @t3-oss/env-core + zod.
Endpoints
| Method | Path | Auth | Description |
|--------|------|------|-------------|
| GET | /health | None | Health check — returns { status, projectDir } |
| GET | /ws | Query param ?token= | WebSocket — persistent bidirectional chat |
| POST | /message/async | Bearer token | Fire-and-forget with Convex callback |
| POST | /answer-question | Bearer token | Resolve a pending AskUserQuestion tool call |
WebSocket Protocol
Connect to /ws?token=<AUTH_TOKEN> for real-time bidirectional communication.
Client to Server
Chat message:
{
"type": "chat",
"threadId": "thread_abc123",
"content": "Build a landing page for our new product",
"attachments": [
{ "url": "https://...", "contentType": "image/png", "filename": "mockup.png" }
]
}Answer a pending question:
{
"type": "answer",
"threadId": "thread_abc123",
"toolUseId": "tool_xyz",
"answers": { "question_key": "user response" }
}Server to Client
The server sends Claude Agent SDK events as they stream:
{ type: "assistant", data: ... }— assistant message chunks{ type: "tool_use", data: ... }— tool invocations{ type: "input_request", toolUseId, questions }— agent needs user input{ type: "result", costUsd, isError, errors? }— turn complete
All non-streaming events are also relayed to Convex via POST /api/chat/event.
Async Endpoint
For server-to-server dispatch where you don't need real-time streaming. Returns 202 Accepted immediately and processes in the background. Results are delivered via Convex callbacks.
curl -X POST http://localhost:3000/message/async \
-H "Authorization: Bearer $AUTH_TOKEN" \
-H "Content-Type: application/json" \
-d '{"threadId": "thread_abc123", "prompt": "Analyze the campaign performance"}'On completion, the bridge calls:
POST /api/chat/event— each SDK event (excluding stream events)POST /api/chat/complete— final callback with{ threadId, isError, costUsd, errors? }
Architecture
- One session per thread —
getOrCreateSession(threadId)ensures all messages on the same thread share anAgentSession, whether they arrive via WebSocket or the async endpoint. - Multi-turn via resume — the first message creates a fresh
query(). Subsequent messages resume with thesessionIdso conversation context carries over. - Attachment handling — file URLs are downloaded to
/tmp/attachmentsand appended to the prompt so the agent can use itsReadtool to view them. - Convex relay — all SDK events (except
stream_event) are posted to the Convex backend with exponential backoff retry (3 attempts).
Development
# From monorepo root
pnpm --filter @koda-sl/baker-bridge dev # Watch mode with tsx
pnpm --filter @koda-sl/baker-bridge build # Compile to dist/Local linking
pnpm --filter @koda-sl/baker-bridge link:local # Build + link for local testing
pnpm --filter @koda-sl/baker-bridge unlink:local # Remove link, restore registry versionPublishing
Auto-publish (CI)
Pushing to main with changes in packages/bridge/ triggers the GitHub Actions workflow, which publishes to npm with the latest tag by default. To publish a pre-release, use the workflow dispatch with npm_tag: next.
Manual publish
From the monorepo root:
# Publish as @latest (production)
./scripts/publish-package.sh bridge
# Publish as @next (pre-release)
./scripts/publish-package.sh bridge nextThe script checks for duplicate versions, builds, and publishes. Bump the version in package.json before publishing.
Testing a pre-release in sandboxes
When you publish a @next version, sandboxes won't use it automatically — they use the version baked into the template. To override:
# Point sandboxes to the pre-release version
npx convex env set BAKER_BRIDGE_VERSION <version>The sandbox startup (nodeActions.ts) checks this env var and runs npm install -g @koda-sl/baker-bridge@<version> before starting. This is optional — when unset, the default template version is used.
After testing, always clean up to avoid running pre-release in production:
npx convex env remove BAKER_BRIDGE_VERSIONRequirements
- Node.js 18+
ANTHROPIC_API_KEYwith access to the Claude Agent SDK
