@iyulab/claude-bridge
v0.5.1
Published
OpenAI-compatible API server for Claude Code
Downloads
902
Readme
claude-bridge
Use Claude Code through any OpenAI-compatible client.
claude-bridge exposes a locally authenticated Claude Code CLI as an OpenAI-compatible HTTP endpoint (/v1/chat/completions). Any tool that speaks the OpenAI protocol — OpenAI SDK, LangChain, Open WebUI, curl — can talk to Claude Code through this bridge.
Client (OpenAI SDK / curl / Open WebUI / LangChain)
↓ HTTP
claude-bridge (Express, localhost:6190)
├── POST /v1/chat/completions (streaming + non-streaming)
├── GET /v1/models
└── POST /v1/chat/completions/:sessionId/cancel
↓
@anthropic-ai/claude-agent-sdk → query()
↓
Claude Code CLI (locally authenticated)Models
Built-in Models
| Model | Description | Tools | Use Case |
|-------|-------------|-------|----------|
| claude-code | Agentic coding assistant | Read, Write, Edit, Bash, Glob, Grep | Code analysis, file operations, git tasks |
| claude-text | Pure text generation | None | Summarization, translation, structured output, creative writing |
| claude-reader | Read-only code analysis | Read, Glob, Grep | Safe code review, codebase Q&A (no file modifications) |
claude-code is the default — Claude operates as a coding agent with full tool access. claude-text disables all agent tools, so Claude responds as a pure LLM that faithfully follows system prompt instructions. claude-reader is a safe middle ground — Claude can read and search code but cannot modify files or run commands.
Model Passthrough
Any model name not listed above is passed directly to the Claude Agent SDK as a model override, running in text mode (no tools):
# Use a specific Claude model
curl http://localhost:6190/v1/chat/completions \
-H "Content-Type: application/json" \
-d '{
"model": "claude-sonnet-4-5-20250514",
"messages": [{"role": "user", "content": "Explain quantum computing"}]
}'This lets you target specific model versions (e.g., claude-sonnet-4-5-20250514, claude-haiku-4-5-20251001) for cost or speed optimization.
Quick Start
Prerequisites
- Node.js 18+ — Download
- Claude Code CLI — installed and authenticated
npm install -g @anthropic-ai/claude-code
claude loginRun instantly with npx
npx @iyulab/claude-bridgeOr install globally
npm install -g @iyulab/claude-bridge
claude-bridgeConfigure with environment variables
PORT=8080 AUTH_TOKEN=mytoken npx @iyulab/claude-bridgeThe server starts on http://localhost:6190 by default.
Configuration
| Variable | Default | Description |
|----------|---------|-------------|
| PORT | 6190 | Server port |
| WORKDIR | (empty) | Working directory for Claude Code. If set, all sessions share this directory. If empty, each session gets an isolated directory under <cwd>/sessions/<session-id>/ |
| AUTH_TOKEN | (empty) | Set to enable Bearer token authentication. Empty = no auth (local-only mode, similar to Ollama) |
| LOG | true | Enable request/response logging. Set to false to disable. Logs are written to logs/sessions/<session-id>.jsonl |
Usage
With curl
# Agentic coding (claude-code)
curl http://localhost:6190/v1/chat/completions \
-H "Content-Type: application/json" \
-d '{
"model": "claude-code",
"messages": [{"role": "user", "content": "Read src/index.ts and explain it"}]
}'
# Pure text generation (claude-text)
curl http://localhost:6190/v1/chat/completions \
-H "Content-Type: application/json" \
-d '{
"model": "claude-text",
"messages": [
{"role": "system", "content": "Summarize the following text in 3 bullet points."},
{"role": "user", "content": "..."}
]
}'
# Streaming
curl http://localhost:6190/v1/chat/completions \
-H "Content-Type: application/json" \
-d '{
"model": "claude-code",
"messages": [{"role": "user", "content": "Explain this project structure"}],
"stream": true
}'With OpenAI SDK (Python)
from openai import OpenAI
client = OpenAI(
base_url="http://localhost:6190/v1",
api_key="any-value", # ignored if AUTH_TOKEN is not set
)
# Agentic coding
response = client.chat.completions.create(
model="claude-code",
messages=[{"role": "user", "content": "Read src/index.ts and explain it"}],
)
print(response.choices[0].message.content)
# Pure text generation
response = client.chat.completions.create(
model="claude-text",
messages=[
{"role": "system", "content": "You are a helpful assistant."},
{"role": "user", "content": "Explain quantum computing in simple terms."},
],
)
print(response.choices[0].message.content)With OpenAI SDK (Node.js)
import OpenAI from "openai";
const client = new OpenAI({
baseURL: "http://localhost:6190/v1",
apiKey: "any-value",
});
const response = await client.chat.completions.create({
model: "claude-code",
messages: [{ role: "user", content: "List all TODO comments in this project" }],
});
console.log(response.choices[0].message.content);Session Continuity
Responses include a session_id field. Pass it back to continue the conversation in the same context:
# First request — new session
curl http://localhost:6190/v1/chat/completions \
-d '{"model":"claude-code","messages":[{"role":"user","content":"Read package.json"}]}'
# Response includes "session_id": "abc-123"
# Follow-up — same session
curl http://localhost:6190/v1/chat/completions \
-d '{"model":"claude-code","messages":[{"role":"user","content":"Now add a test script"}],"session_id":"abc-123"}'Cancel a Running Request
curl -X POST http://localhost:6190/v1/chat/completions/abc-123/cancelLogging
Request/response logging is enabled by default. Each session's logs are written to logs/sessions/<session-id>.jsonl in JSONL format.
Console output uses a human-friendly format:
[10:00:01] → POST /v1/chat/completions session=abc-123 stream=true msgs=3
[10:00:02] ⚡ init claude_session=sdk-xyz
[10:00:05] 🔧 tool_use Read file_path=/foo.ts
[10:00:09] ← 200 session=abc-123 8000msSet LOG=false to disable all logging.
API Reference
POST /v1/chat/completions
| Field | Type | Required | Description |
|-------|------|----------|-------------|
| model | string | No | claude-code (default), claude-text, claude-reader, or any Claude model ID for passthrough |
| messages | array | Yes | Array of {role, content} messages |
| stream | boolean | No | Enable SSE streaming (default: false) |
| session_id | string | No | Session ID for conversation continuity |
GET /v1/models
Returns available models: claude-code, claude-text, and claude-reader.
POST /v1/chat/completions/:sessionId/cancel
Aborts an in-progress query for the given session.
Error Codes
All errors follow the OpenAI error format { error: { message, type, code } }:
| Situation | HTTP | code |
|-----------|------|------|
| Session busy (concurrent request) | 409 | session_busy |
| Cancel target not found | 404 | session_not_active |
| Claude execution error | 500 | claude_execution_error |
| Auth failure | 401 | invalid_api_key |
| Bad request | 400 | invalid_request_error |
Architecture
Key Design Decisions
- Thin translation layer — only converts between OpenAI API format and Claude Agent SDK calls
- Multiple modes —
claude-codefor agentic coding,claude-readerfor safe read-only analysis,claude-textfor pure text, or any Claude model ID for passthrough - Session isolation — each session gets its own working directory under
<cwd>/sessions/<session-id>/ - Busy lock — one request per session at a time (concurrent requests return HTTP 409)
- Structured logging — JSONL per session + human-friendly console output
Project Structure
src/
├── cli.ts # CLI entrypoint (preflight checks + server start)
├── index.ts # Express app factory (createApp, startServer)
├── config.ts # Environment variable loading
├── middleware/
│ ├── auth.ts # Optional Bearer token auth
│ └── logger.ts # Request/response logging middleware
├── routes/
│ ├── chat.ts # /v1/chat/completions + cancel
│ └── models.ts # /v1/models
├── services/
│ ├── chat.ts # Claude Agent SDK integration
│ ├── session.ts # In-memory session management
│ ├── logger.ts # Logging service (console + JSONL)
│ └── formatter.ts # SDK → OpenAI response formatting
└── types/
└── openai.ts # OpenAI-compatible type definitionsTech Stack
- Runtime: Node.js + TypeScript (ESM)
- Framework: Express 5
- Claude Integration:
@anthropic-ai/claude-agent-sdk—query()function - Session Storage: In-memory Map (resets on server restart)
Development
git clone https://github.com/iyulab/claude-bridge.git
cd claude-bridge
npm install
npm run devTo build for production:
npm run build
npm startCurrent Limitations
- Sessions are in-memory only (lost on restart)
usagetoken counts are estimated (based on SDK-reported values)- OpenAI parameters accepted but ignored:
max_tokens,max_completion_tokens,temperature,top_p,stop,presence_penalty,frequency_penalty— the Claude Agent SDK does not expose these controls - No function calling / tool_use mapping
License
MIT
