Skip to content

Latest commit

 

History

History
104 lines (81 loc) · 4.45 KB

File metadata and controls

104 lines (81 loc) · 4.45 KB

Claude Code API

OpenAI-compatible API server that wraps Claude Code's Agent SDK. Exposes standard OpenAI endpoints so any OpenAI-compatible client (Aider, OpenCode, Cursor, Continue, custom apps) can use Claude models.

Commands

  • npm run dev — start dev server (tsx)
  • npm run build — compile TypeScript to dist/
  • npm start — run compiled output
  • npx tsc --noEmit — type check without emitting

Configuration

Env var Default Description
PORT 3456 Server port
DEFAULT_MODEL sonnet Model when request omits model field
TOOLS all all = full agent mode, none = chat-only (for Aider, OpenCode, etc.)

Endpoints

Method Path Description
POST /v1/chat/completions OpenAI-compatible chat completions (streaming & non-streaming)
GET /v1/models Lists available Claude models (full IDs + aliases)
GET /health Health check

Architecture

src/
  index.ts                    — Hono app, routes, server startup
  config.ts                   — Env var loading (PORT, DEFAULT_MODEL, TOOLS)
  routes/
    chat-completions.ts       — POST /v1/chat/completions handler
    models.ts                 — GET /v1/models
    health.ts                 — GET /health
  core/
    claude-executor.ts        — Wraps @anthropic-ai/claude-agent-sdk query()
  transform/
    request.ts                — OpenAI messages[] → prompt string + SDK options
    response.ts               — SDKMessage[] → OpenAI JSON response
    streaming.ts              — SDKMessage async stream → SSE delta chunks
  types/
    openai.ts                 — OpenAI request/response TypeScript interfaces

Key Design Decisions

  • Stateless: each request is a fresh SDK query() call. No server-side session state. Full message history is flattened into the prompt.
  • Two tools modes:
    • TOOLS=all (default): full agent mode — Claude Code runs with all built-in tools (Bash, Edit, Read, etc.) via bypassPermissions. Tool activity is invisible to the API consumer, only final text is returned.
    • TOOLS=none: chat-only mode — no tools, Claude acts as a plain LLM. Required for clients that handle code editing themselves (Aider, OpenCode, Cursor, etc.).
    • Per-request override via x-claude-tools: none|all header.
  • Model mapping: pass-through only. Clients must send valid Claude model names (sonnet, opus, haiku, or full IDs like claude-sonnet-4-6).
  • Unsupported params: temperature, max_tokens, top_p, etc. are silently accepted and ignored.
  • No auth: local dev tool, relies on the machine's existing Claude authentication.
  • No OpenAI tool/function calling: the tools field in requests is accepted but ignored.
  • ESM: project uses "type": "module" with bundler module resolution.

SDK Notes

  • The Agent SDK (@anthropic-ai/claude-agent-sdk) spawns a Claude Code child process per query() call.
  • SDKMessage is a large union type. We only care about assistant (text content), stream_event (streaming deltas), and result (usage/finish).
  • Thinking model (Opus) thinking blocks are naturally excluded — we filter for block.type === "text" from BetaMessage.content.
  • persistSession: false prevents writing session files to disk.

Testing

# Non-streaming
curl http://localhost:3456/v1/chat/completions \
  -H "Content-Type: application/json" \
  -d '{"model":"sonnet","messages":[{"role":"user","content":"Hello"}]}'

# Streaming
curl http://localhost:3456/v1/chat/completions \
  -H "Content-Type: application/json" \
  -d '{"model":"sonnet","messages":[{"role":"user","content":"Hello"}],"stream":true}'

# Chat-only mode (per-request)
curl http://localhost:3456/v1/chat/completions \
  -H "Content-Type: application/json" \
  -H "x-claude-tools: none" \
  -d '{"model":"sonnet","messages":[{"role":"user","content":"Hello"}]}'

# List models
curl http://localhost:3456/v1/models

# With Aider (chat-only mode required)
TOOLS=none npm run dev
aider --openai-api-base http://localhost:3456/v1 --openai-api-key dummy --model openai/sonnet

Known Edge Cases

  • System-only messages (no user message): system prompt is used as the prompt
  • Null/empty content in messages: returns 400 with clear error
  • Multi-turn with shell-unsafe characters: use heredoc or --data-raw with curl
  • Aider/OpenCode require TOOLS=none — they expect a plain LLM, not an agent

Repository

https://github.com/gididaf/claude-code-api