Three Subscriptions, One Endpoint: Open-Sourcing LLM-Bridge
Putting Claude Code, Codex, and Antigravity behind one OpenAI-compatible API. What it does — and what it deliberately doesn't.
If you pay for Claude, ChatGPT (Codex), and Google’s Antigravity, you’re holding three subscriptions, three CLIs, three interfaces, with quota siloed in each. Want to call one from your own script? You get to remember three sets of commands and three output formats.
LLM-Bridge puts all three behind one OpenAI-compatible local endpoint, with a built-in chat UI. It reuses each tool’s own subscription login — no API keys to buy, no tokens to extract from anywhere.
from openai import OpenAI
client = OpenAI(base_url="http://127.0.0.1:8787/v1", api_key="unused")
for model in ["claude/claude-sonnet-5", "codex/gpt-5.5", "agy/gemini-3.5-flash-medium"]:
r = client.chat.completions.create(model=model, messages=[{"role": "user", "content": "Hi!"}])
print(model, "→", r.choices[0].message.content)
Same OpenAI client, swap the model string, and you go from Claude to GPT to Gemini. That’s the whole idea.
Why “OpenAI-compatible”
Not because OpenAI’s format is the best one — because it’s the de facto universal socket. Nearly every LLM client, SDK, and editor plugin lets you point base_url at a custom address. Choosing it as the outward protocol means every existing tool connects with zero changes: your Python openai library, the AI plugin in your editor, a curl you scribbled — change one address and it works.
So internally, OpenAI Chat Completions is also the single canonical format. Requests arrive in it, each backend’s native output is converted back into it, then streamed out. The three backends’ differences get compressed into the adapter layer; the outside sees exactly one shape.
Three backends, each through its own official channel
| Backend | Harness | Typical models |
|---|---|---|
| claude | claude-agent-sdk (bundles its own CLI) |
Fable 5, Opus 4.8, Sonnet 5, Haiku 4.5 |
| codex | codex exec --json subprocess |
GPT-5.5, GPT-5.4, GPT-5.4-mini… |
| agy | agy -p - subprocess (Antigravity CLI) |
Gemini 3.5/3.1, Claude 4.6 Thinking, GPT-OSS 120B |
Not one line assembles a backend API request by hand. claude goes through the official Agent SDK; codex and agy each shell out to the official CLI. Auth, token refresh, subscription billing — all handled by each vendor’s own harness. The gateway never touches a single token. That line isn’t fastidiousness; it’s the precondition for this project existing at all — which gets its own post.
What it deliberately doesn’t do
A tool’s boundaries often say more about what it is than its features do. LLM-Bridge explicitly does not:
- Chat only. No tool calling, no multi-modal content. A request with
toolswon’t pretend to support them; sampling params (temperature,max_tokens) are accepted but not forwarded — the underlying CLIs don’t expose those knobs, and faking it only fools yourself. - Single-user, localhost-first. Binds
127.0.0.1by default, no rate limiting, stores no credentials, CORS locked to the built-in UI’s origin. It’s a personal gateway on your own machine, not a serving stack for a team. - Never touches tokens. Everything goes through official harnesses; it never extracts or replays OAuth tokens — vendors ban it (Anthropic shipped server-side blocks in January 2026, fully enforced by April), and the direct backend APIs now return 403.
These aren’t “not built yet.” They’re deliberately not built. Putting them in the first screen of the README is so anyone using it knows the boundaries up front, instead of hitting a wall halfway in.
Install and run
uv tool install git+https://github.com/mahui/llm-bridge
llm-bridge
Then open http://127.0.0.1:8787. Prerequisites: Python 3.12+, uv, and at least one logged-in CLI (claude, codex login, or agy).
The built-in chat UI streams, lets you pick a model per conversation, runs conversations concurrently, and has an API view that hands you copy-ready cURL / Python / JS snippets tracking your selected model. Theme follows the system. Model output is rendered through marked and then sanitized with DOMPurify — model output is untrusted input, and that line holds here too.
Takeaways
LLM-Bridge solves a narrow problem: three subscriptions you’ve already paid for, quota no longer scattered across three interfaces.
- One OpenAI endpoint outward: every existing tool connects unchanged; internally, one canonical format absorbs three backends’ differences;
- Three official channels inward: claude via the Agent SDK, codex / agy via official CLI subprocesses, the gateway never touching tokens;
- Boundaries in plain sight: chat only, single-user, localhost-first — deliberate restraint, not unfinished work.
MIT licensed, code at github.com/mahui/llm-bridge. The next posts take apart the compliance line, the adapter design, and the subprocess lifecycle — one hole I stepped in at a time.
Comments