cd /news/ai-agents/agent-bridge-an-openai-compatible-ap… · home › topics › ai-agents › article
[ARTICLE · art-152316] src=github.com ↗ pub= topic=ai-agents verified=true sentiment=↑ positive

Agent-bridge – an OpenAI-compatible API for headless coding-agent CLIs

An independent project called agent-bridge, a Go HTTP server released under the GNU GPL v3 or later, exposes an OpenAI-compatible API at /v1/chat/completions and /v1/models and fulfils each request by running the official Claude Code CLI in headless mode (claude -p --output-format stream-json). The server also supports the OpenAI Codex CLI (codex exec --json) and the Kimi Code CLI (kimi acp, over the Agent Client Protocol) as backends, letting clients such as LangChain, LlamaIndex, CrewAI, AutoGen and the OpenAI Python, Node and Go SDKs switch to it by changing only base_url, api_key and model. It requires the claude CLI version 2.1.128 or later plus one interactive login, ships as a single binary for Linux, macOS and Windows on amd64 and arm64, and is not developed, endorsed or supported by Anthropic.

read18 min views2 publishedOct 18, 2026
Agent-bridge – an OpenAI-compatible API for headless coding-agent CLIs
Image: Michielbdejong (auto-discovered)

English · Italiano

agent-bridge is an HTTP server written in Go that exposes an OpenAI-compatible API (/v1/chat/completions, /v1/models) and fulfils every request by running the official Claude Code CLI in headless mode (claude -p --output-format stream-json).

It can also run the OpenAI Codex CLI (codex exec --json) or the Kimi Code CLI (kimi acp, over the Agent Client Protocol) instead, with the same API: see the Codex backend and the Kimi backend.

Any client or framework that speaks OpenAI can use it by changing only base_url, api_key and model: chat, streaming, function calling (tool calling) and image input work with the official SDKs unchanged.

In short: an OpenAI-compatible API server, proxy and gateway for Claude Code. It lets OpenAI-based multi-agent systems, ReAct orchestrators and frameworks that accept a custom OpenAI base_url (for example LangChain, LlamaIndex, CrewAI, AutoGen or the OpenAI SDKs for Python, Node and Go) use Claude Code headless as a drop-in chat completions model or as a coding agent, with tool calling, MCP servers, sessions, streaming (SSE) and per-agent models. The same server is an OpenAI-compatible API, proxy and gateway for the OpenAI Codex CLI and for the Kimi Code CLI (Moonshot AI, Kimi K2), and an ACP (Agent Client Protocol) client.

Independent project. agent-bridge is not developed, endorsed or supported by Anthropic. It runs the official claude binary installed on the machine, without modifying it, and never accesses its credentials.

  • Single binary, Go standard library only at runtime.
  • Linux, macOS and Windows (amd64 and arm64).
  • Same build for development (subscription login) and production (API key).
  • OpenAI function calling (tools ,tool_calls ), streaming included.
  • Image input (image_url parts: data URLs and http(s) URLs).
  • Three CLI backends: the default one, the OpenAI Codex CLI (backend: "codex" , seedocs/codex.md ) and the Kimi Code CLI (backend: "kimi" , seedocs/kimi.md ).
  • Free software, licensed under the GNU GPL v3 or later.
As a model As an agent on the server
Who defines the agent Your client or platform: system ,tools , model choice The bridge configuration file
Available tools Only the functions the client sends in tools , executed by the client CLI tools on the server machine (files, commands, search, MCP servers), plus any client functions
model bridge/opus ,bridge/sonnet ,bridge/haiku ,bridge/<model name> The name of the configured agent
Configuration None JSON file
Section Using it as a model Agents on the server

"Tools" therefore come in two kinds, both supported:

  • client functions (OpenAI function calling): defined and executed by the client; the model calls them throughtool_calls ;
  • agent tools :Read ,Edit ,Bash , MCP servers… executed by the CLI on the server, within the agent's permissions; the client only sees the final result.

Prerequisite: the claude CLI installed, version 2.1.128 or later, and one interactive login (claude).

go install github.com/r3n3r0/agent-bridge/cmd/agent-bridge@latest
AGENT_BRIDGE_AUTH_MODE=subscription agent-bridge
curl -s http://127.0.0.1:8787/v1/chat/completions \
  -d '{"model":"bridge/haiku","messages":[{"role":"user","content":"Hello!"}]}'

Without a configuration file the server listens on 127.0.0.1:8787 and accepts the models bridge/opus, bridge/sonnet, bridge/haiku and bridge/<model name> (the name after the prefix is passed to the CLI as is; the bridge/ prefix can be changed with builtin_prefix). They are chat-only models: no server tools, an empty temporary working directory, but they accept client functions and images.

Alternatively, download a binary from the releases page (archives and checksums.txt for every platform).

This is the scenario of a platform that already has its own agents, graph and lifecycle (for example a ReAct system): the bridge behaves like an OpenAI model and the agent is the one defined in the platform.

In the platform In the request
Agent instructions system message
Agent tools tools
Model bridge/<alias or model name>

Two options make the bridge stateless and make the platform's agent the only agent. Set them in the configuration, or per request with a header:

Configuration key Header Effect
client_tools.mode: "stateless" X-Agent-Bridge-Tool-Mode: stateless After returning tool_calls the process exits: every request is one step of the graph and the history sent by the client is the only source of truth. Branches, retries and checkpoints stay in the platform
system_messages: "replace" X-Agent-Bridge-System-Mode: replace system messages become the effective system prompt instead of the CLI one, at every step. Withappend they are added to it

Recommended configuration for this scenario:

{ "client_tools": { "mode": "stateless" }, "system_messages": "replace" }

In this scenario do not send X-Session-Id: every request starts a new process with the full history. It costs some latency per step, in exchange for no hidden state in the bridge.

Functions defined by the client with tools work as with the OpenAI API: the model answers with tool_calls and finish_reason: "tool_calls", the client runs the functions and sends the results back as role: "tool" messages, and the model continues. It also works when streaming (delta.tool_calls) and without session headers.

r = client.chat.completions.create(model="bridge/sonnet", messages=messages, tools=tools)
while r.choices[0].finish_reason == "tool_calls":
    messages.append(r.choices[0].message)
    for call in r.choices[0].message.tool_calls:
        messages.append({"role": "tool", "tool_call_id": call.id,
                         "content": run(call.function.name, call.function.arguments)})
    r = client.chat.completions.create(model="bridge/sonnet", messages=messages, tools=tools)

Complete example: examples/python/tools.py.

Supported: tools (type function), tool_choice (auto, none, required, a specific function), parallel_tool_calls (parallel calls returned together) and the deprecated functions/ function_call format.

How it works. Client functions are offered to the CLI as tools of a standard MCP server, served by the bridge itself (internal mcp-tools subcommand). When the model calls one, the bridge returns the call to the client. Then, depending on client_tools.mode:

Mode What happens When to choose it
stateful (default) The process waits; the request carrying the results is recognized by its tool_call_id s and the same process resumes where it stopped Simple clients: lower latency between steps
stateless The process exits; the request carrying the results starts a new process with the full history Orchestrators with their own state (see Using it as a model )

Differences to be aware of:

  • tool_choice other thanauto /none andparallel_tool_calls: false are expressed as instructions to the model: the model follows them, but they are not hard constraints as in the OpenAI API. Functionparameters schemas (andstrict ) also guide the model but are not enforced.
  • In stateful mode the waiting process holds a concurrency slot. If the results do not arrive withinclient_tools.wait_timeout_seconds (default
  1. the process is stopped; a later request with the results starts a new process from the full history, so it still works.
  • With the deprecated functions format results carry no id: every call ends the process, as instateless mode.
  • In a tool_calls responseusage covers only the model's last message; turn totals are in the logs and in/stats .

Messages can contain image_url parts, as in the OpenAI API, with any model:

r = client.chat.completions.create(model="bridge/sonnet", messages=[{"role": "user", "content": [
    {"type": "text", "text": "What is in this picture?"},
    {"type": "image_url", "image_url": {"url": "data:image/png;base64,..."}},
]}])
  • Sources :data: URLs (base64) andhttp(s) URLs, which the bridge downloads. Downloads from non-public addresses (loopback, private networks, cloud metadata) are refused unlessimages.allow_private_networks is set;images.fetch_urls: false accepts onlydata: URLs.
  • Formats : PNG, JPEG, GIF, WebP, recognized from the content. Up toimages.max_count images per request (default 20) andimages.max_bytes each (default 5 MB); the whole input, images included, must stay within 9 MB.
  • How : images are sent to the CLI as image blocks of a structured message (--input-format stream-json ); the text refers to them as[image 1] ,[image 2] … in order. Requests without images keep plain-text input.
  • In a resumed session only the images of the new messages are sent; earlier ones are already in the session. images.enabled: false rejects images with 400unsupported_content .

The bridge drives the CLI, not the model API directly, so it supports the OpenAI parameters the CLI can express, and emulates two more:

OpenAI parameter How Fidelity
reasoning_effort CLI --effort ;none /minimal becomelow ,xhigh andmax are accepted as an extension Direct
response_format (json_object ,json_schema ) CLI --json-schema : the answer is validated against the schema and returned as compact JSON incontent Direct, validated
max_tokens ,max_completion_tokens CLAUDE_CODE_MAX_OUTPUT_TOKENS for the process Partial: limits every model response of the turn, not the turn total; finish_reason is not set tolength
stop (string or up to 4) The final text is cut at the first occurrence Emulated: the model still generates the full answer; never applied to response_format JSON or to progress texts
n (1-8) n independent processes in parallel, one choice each, also when streaming Emulated: costs n times; no session resume, and client functions run stateless
temperature ,top_p ,seed ,logprobs ,presence_penalty ,frequency_penalty ,logit_bias — Accepted and ignored: the CLI has no equivalent

If response_format cannot be satisfied (the CLI gives up after its retries, or ends without structured output), the request fails with 502.

A JSON file maps a model name to an agent: model, system prompt, CLI tools, permissions and working directory. Use it when the agent must work on the server machine, for example on a repository.

{
  "agents": {
    "reviewer": {
      "model": "opus",
      "system_prompt": "You are a strict reviewer. Do not modify files.",
      "allowed_tools": ["Read", "Grep", "Glob"],
      "cwd": "/srv/project"
    },
    "coder": {
      "model": "sonnet",
      "allowed_tools": ["Read", "Edit", "Write", "Bash(go test *)"],
      "permission_mode": "acceptEdits",
      "cwd": "/srv/project"
    }
  }
}
agent-bridge check-config -config agents.json   # validate and print the resolved agents
AGENT_BRIDGE_AUTH_MODE=subscription agent-bridge -config agents.json

The client picks the agent with the model field:

from openai import OpenAI
client = OpenAI(base_url="http://127.0.0.1:8787/v1", api_key="unused")
r = client.chat.completions.create(
    model="reviewer",
    messages=[{"role": "user", "content": "Review the error handling."}],
    extra_headers={"X-Session-Id": "review-42"},
)
print(r.choices[0].message.content)

An agent on the server can also receive client functions in tools: it will use both its own tools and those.

Full reference: docs/configuration.md. Annotated example: examples/agents.example.json.

  • Without X-Session-Id (OpenAI behavior): every request starts a new process; the history sent by the client is turned into a single prompt.
  • With X-Session-Id (or the user field): the same key resumes the CLI session with--resume , and the CLI receives only the new messages (user messages and tool results) after the assistant's last answer. Faster in long conversations. The key, generated when missing, is always returned in theX-Session-Id response header.
  • system messages are placed at the top of the prompt (system_messages: "prompt" , default): in a resumed session only those of the first turn count. Withappend orreplace they are passed as the system prompt and apply to every turn.
  • Answers to tool_calls instateful mode resume the waiting process through thetool_call_id s, regardless of the session.

The mode is chosen only by configuration, with the mandatory AGENT_BRIDGE_AUTH_MODE variable:

Mode Use Credentials
subscription Development on your own machine Login done with claude ;ANTHROPIC_API_KEY andANTHROPIC_AUTH_TOKEN must not be set
api Production and shared instances ANTHROPIC_API_KEY required (Claude Console key)

The server refuses to start if the mode and the environment are inconsistent. In subscription mode the API key variables are removed from the child process environment anyway, and if the CLI reports using an API key the server logs an error on every turn.

Every turn records tokens and cost (logs, optional usage_log file, /stats, /metrics). In subscription mode cost_usd is an estimate computed by the CLI at API prices, not a charge: it helps forecast what production with an API key will cost.

compose.yaml runs the bridge with all three CLIs, one service per backend (ports 8787, 8788 and 8789 on the host's loopback), sharing one home volume for the CLIs' logins:

make up       # creates .env with a random API key, builds, starts, prints the addresses
make admin    # opens the login page
make smoke    # one request to every service

make help lists the other targets (logs, doctor, terminal logins, update, reset). Without make: cp .env.example .env, set AGENT_BRIDGE_API_KEY, docker compose up -d.

Then log in to the CLIs from the admin page, http://127.0.0.1:8787/admin: it runs each CLI's official login flow on the server and shows its link and one-time code (or a box to paste the code back). The CLIs keep the credentials; the bridge never reads them. Details: docs/docker.md.

With backend: "codex" (or -backend codex, AGENT_BRIDGE_BACKEND=codex) every turn runs codex exec --json instead of the default CLI. The API, sessions, function calling, images, structured output and reasoning effort work the same way; built-in models become bridge/default (the model configured in Codex) and bridge/<model>; agents with tools use a sandbox (read-only, workspace-write, danger-full-access) instead of permission_mode and allowed_tools. Authentication: codex login in subscription mode, CODEX_API_KEY in api mode.

codex login
AGENT_BRIDGE_AUTH_MODE=subscription agent-bridge -backend codex

Full mapping, agent fields and differences: docs/codex.md.

With backend: "kimi" (or -backend kimi, AGENT_BRIDGE_BACKEND=kimi) every turn runs kimi acp, the Kimi Code CLI as an Agent Client Protocol agent, with the bridge as the ACP client. The API, sessions, function calling, images, structured output and reasoning effort work the same way; built-in models become bridge/default (the default model of Kimi Code) and bridge/<model>; the bridge answers the CLI's permission requests from allowed_tools, disallowed_tools and permission_mode (default, plan, auto, yolo). Authentication: kimi login in subscription mode, KIMI_MODEL_NAME and KIMI_MODEL_API_KEY in api mode. ACP reports no token usage or cost.

kimi login --region global   # kimi.ai; mainland-cn for kimi.com
AGENT_BRIDGE_AUTH_MODE=subscription agent-bridge -backend kimi

Full mapping, agent fields and differences: docs/kimi.md.

agent-bridge is a tool: whoever uses it remains responsible for complying with the terms of their plan. In short:

  1. No access to credentials. The code never reads, copies, stores, logs or forwards credential files or tokens. Login happens only through the CLI's official flow, and the subscription token is never used to call the API directly.
  2. One subscription, one user. Insubscription mode the server must serve only the account holder, on their own machine. The default configuration listens on loopback only; remote access must be enabled explicitly and is meant for reaching your own instance from your own laptop (e.g. a personal VPN), not for other users.
  3. Shared instances only with an API key. An instance used by several people (e.g. a lab or a team) must run inapi mode with the owner's key, without reselling or brokering costs.

References, to be checked again because the rules may change:

An agent on the server with tools enabled can read, write and run commands: whoever reaches the server has the equivalent of a restricted shell. Defaults are closed and every opening is explicit.

  • Network. Listens on127.0.0.1:8787 by default. A non-loopback address requiresallow_remote: true (or-allow-remote )andAGENT_BRIDGE_API_KEY . There is no built-in TLS: for remote access use a reverse proxy with TLS, an SSH tunnel or a VPN.
  • Client authentication. WithAGENT_BRIDGE_API_KEY set,/v1/* ,/stats and/metrics requireAuthorization: Bearer <key> (constant-time comparison)./healthz and/readyz stay open.
  • CORS is disabled; enable it with an exact list of origins (cors_origins ), no wildcards.
  • bridge/* models are always chat-only: no server tools, in any mode.
  • Client functions are executed only by the client, never on the server. Calls go through an internal broker on127.0.0.1 with a random port and a different token for every turn; the turn's files are private and removed at the end.
  • Per-request overrides (X-Agent-Cwd ,X-Agent-Allowed-Tools ,X-Agent-System-Prompt ) are rejected with 403 unless enabled inoverrides ; they can only narrow tools, the directory must be inside an allowed root (also after resolving symlinks), and the system prompt is only appended. Never applicable tobridge/* models.
  • system_messages. With replace the CLI system prompt is replaced by the request's messages (the agent'ssystem_prompt stays first); the permissions of the agent's tools do not change.
  • bypassPermissions is rejected unless allow_bypass_permissions: true , in which case a warning is printed at startup for every affected agent.
  • Logs. Atinfo level and above, prompts and answers are never logged; atdebug they are truncated to 500 characters.sk-ant-… keys and the bridge key are replaced with[REDACTED] in logs and errors.
  • Processes. Every turn is a separate process, started without a shell, with the prompt on stdin. Timeouts, client disconnection or server shutdown terminate the whole process tree (process group on Unix,taskkill /T on Windows).

Client examples:

Endpoints:

Method and path Purpose
POST /v1/chat/completions Runs a turn (JSON or SSE)
GET /v1/models ,GET /v1/models/{id} Lists configured agents and bridge/* aliases (bridge/<model name> names answer the detail endpoint but are not listed); the prefix followsbuiltin_prefix
GET /stats Per-agent statistics; ?reset=true resets them after returning them
GET /metrics Prometheus metrics (with metrics: true )
GET /healthz ,GET /readyz Liveness and readiness

Request headers:

Header Effect
X-Session-Id ,X-Session-Reset Session to resume or restart
X-Agent-Bridge-Tool-Mode stateful orstateless for client functions
X-Agent-Bridge-System-Mode prompt ,append orreplace forsystem messages
X-Agent-Cwd ,X-Agent-Allowed-Tools ,X-Agent-System-Prompt Overrides, only if enabled
X-Request-Id Identifier reported in logs and in the response

Response headers: X-Session-Id, X-Request-Id and, in non-stream responses, X-Agent-Bridge-Turns, X-Agent-Bridge-Cost-USD, X-Agent-Bridge-Duration-Ms.

Admin page (with admin: true or AGENT_BRIDGE_ADMIN=1): GET /admin and /admin/api/logins to log in to the CLIs (docs/docker.md).

Commands: agent-bridge serve (default), check-config, doctor [-live], version; mcp-tools is internal, started by the CLI for client functions. doctor checks the CLI and its version, the consistency of the authentication mode and directory permissions; with -live it runs a test turn on Haiku (with the Codex and Kimi backends: the model configured in the CLI, and for Codex codex login status).

  • temperature ,top_p ,seed ,logprobs ,presence_penalty ,frequency_penalty andlogit_bias are accepted and ignored;stop ,n andmax_tokens are emulated or partial (seeGeneration parameters ).
  • Function calling: a binding tool_choice ,parallel_tool_calls: false and function schemas are instructions to the model, not enforced constraints.
  • Content parts other than text and image_url (audio, files) return 400unsupported_content ;detail on images is accepted and ignored.
  • No support for /v1/responses ,/v1/embeddings , image generation, audio, or other providers.
  • Prompts up to 9 MB (the CLI limits stdin to 10 MB).
  • When streaming, the X-Agent-Bridge-Turns ,-Cost-USD and-Duration-Ms response headers are not available (headers are sent before execution); cost and tokens are in the logs, in/stats and in theusage chunk when requested withstream_options.include_usage .
  • When streaming, progress is sent as single-line SSE comments without a following blank line, because some SDKs treat a blank line after a comment as an empty event.
  • A turn can take minutes: the model may work through several steps before answering.
  • The exact values of the apiKeySource field of theinit event are not documented: the server treats onlynone as "no API key".

Does it support tools? Yes, of two kinds: client functions through OpenAI function calling, and agent tools on the server (files, commands, search, MCP servers) for configured agents. See Two ways to use it.

Why don't temperature and top_p work? The bridge drives the Claude Code CLI, which has no option for them, and calling the model API directly would bypass the CLI (and, with a subscription, its terms). What the CLI does offer is mapped: see Generation parameters.

Does it accept images? Yes, image_url parts with data: or http(s) URLs, as in the OpenAI API. See Images.

Do I need to change my OpenAI client? No: change base_url (http://127.0.0.1:8787/v1), api_key (the AGENT_BRIDGE_API_KEY, or any value if it is not set) and model.

Do I need a configuration file for every agent? No, if agents are defined in the client: instructions in system, tools in tools, a bridge/* model. The file is only needed for agents that use the server's tools.

Is the context duplicated if I send the whole history? Without X-Session-Id every request starts again from the full history, like a stateless API. With X-Session-Id the session is resumed and the CLI receives only the new messages.

What happens if I change working directory mid-conversation? CLI sessions are tied to the project directory: the server starts a new one.

Can I expose the server on the network? Only explicitly (allow_remote plus AGENT_BRIDGE_API_KEY), behind TLS, and in subscription mode only for the account holder. For several users use api mode.

Two requests on the same session? They are serialized (on_busy: wait) or the second gets 409 (on_busy: reject).

Layered structure with interfaces and replaceable implementations: docs/architecture.md. Full technical specification, revision 1.4: docs/specification.md.

go test -race ./...          # uses a fake CLI binary, no credentials
./scripts/coverage.sh        # minimum coverage of the main packages

CI never uses the real CLI or credentials. Manual checks before every release are in docs/release-checklist.md.

agent-bridge is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. See LICENSE.

The license covers this project's code only. Claude Code is a separate product with its own terms, installed and used by you under your agreement with Anthropic.

── more in #ai-agents 4 stories · sorted by recency
── more on @agent-bridge 3 stories trending now
sponsored brought to you by zahid.host 4,200+ EU-deployed projects
reading about agents? ship yours in a single git push.

Run your AI side-project on zahid.host

EU-based hosting, git-push deploys, automatic HTTPS, no cold starts. Free tier with a custom domain — perfect for shipping the agent you just read about.

$git push zahid main
→ Live at https://your-agent.zahid.host ✓
Get free account → Pricing
from €0/mo · no card required
LIVE [news/agent-bridge-an-open…] indexed:0 read:18min 2026-10-18 · —