Overview
Copilot Bridge is a small, self-hosted proxy that exposes your GitHub Copilot subscription through two API dialects at once: the OpenAI Chat Completions API and the Anthropic Messages API. Any tool that can point at a custom base URL — Claude Code, Codex CLI, opencode, Cline, Kilo Code, or the official SDKs — can then run on Copilot.
It runs entirely on your own machine using your own credentials. Nothing is routed through a third party.
copilot. You can also
self-host your own copy.
Install
Run it on demand with npx, or clone the repo.
# on demand npx copilotbridge --help # or from a clone git clone <repo-url> copilotbridge cd copilotbridge node src/index.js --help
Authenticate
Authenticate once with GitHub's device flow. You'll open a URL, enter a short code, and approve the login in your browser.
npx copilotbridge auth
Your GitHub token is stored at
~/.copilotbridge/github-token with file
mode 600. The bridge uses it to mint
short-lived Copilot tokens as needed. In CI, set
GH_TOKEN instead of logging in.
Start the server
npx copilotbridge start # 127.0.0.1:4141 by default
npx copilotbridge start --port 8080
npx copilotbridge start --host 0.0.0.0 --port 8080
BRIDGE_API_KEY so clients must present a
key. See Security.
Configure your tool
Claude Code · Anthropic
export ANTHROPIC_BASE_URL=https://157-245-80-136.sslip.io export ANTHROPIC_AUTH_TOKEN=copilot # any value, or your BRIDGE_API_KEY claude
Codex CLI · OpenAI SDK
export OPENAI_BASE_URL=https://157-245-80-136.sslip.io/v1 export OPENAI_API_KEY=copilot
opencode
opencode has native GitHub Copilot support (opencode auth login
→ GitHub Copilot), so it doesn't need the bridge. If you want it to share one
key with your other tools anyway, add an OpenAI-compatible provider:
// opencode.json
{
"provider": {
"copilotbridge": {
"npm": "@ai-sdk/openai-compatible",
"options": { "baseURL": "https://157-245-80-136.sslip.io/v1", "apiKey": "copilot" }
}
}
}
Cline / Kilo Code
Choose an OpenAI-compatible provider, set the base URL to
https://157-245-80-136.sslip.io/v1, and use any
non-empty API key (or your BRIDGE_API_KEY).
OpenAI SDK (JavaScript)
import OpenAI from "openai"; const client = new OpenAI({ baseURL: "https://157-245-80-136.sslip.io/v1", apiKey: "copilot", });
Anthropic SDK (JavaScript)
import Anthropic from "@anthropic-ai/sdk"; const client = new Anthropic({ baseURL: "https://157-245-80-136.sslip.io", apiKey: "copilot", });
Endpoints
| Endpoint | Description |
|---|---|
| POST /v1/chat/completions | OpenAI chat — streaming, tools, images |
| GET /v1/models | Models available to your Copilot plan |
| POST /v1/messages | Anthropic Messages — streaming, tools, images |
| POST /v1/messages/count_tokens | Approximate input-token count |
| GET /healthz | Liveness check |
| GET / | This site |
Quick check once the server is running:
curl https://157-245-80-136.sslip.io/healthz
curl https://157-245-80-136.sslip.io/v1/models -H "authorization: Bearer copilot"
Models
Which models you can call depends on your Copilot plan. List them
with GET /v1/models, then pass one of the
returned ids as the model in your request or via
ANTHROPIC_MODEL /
--model.
# Claude Code example export ANTHROPIC_MODEL=claude-sonnet-4.5 claude --model gpt-5
Configuration
| Variable | Default | Meaning |
|---|---|---|
PORT | 4141 | Listen port (also --port) |
BRIDGE_API_KEY | unset | If set, clients must send this exact key. If unset, any non-empty key is accepted. |
GH_TOKEN | unset | Use this GitHub token instead of the stored one (for CI). |
How it works
- Login.
authruns GitHub's OAuth device flow using the public Copilot client id and stores the GitHub token locally. - Token exchange. On demand, the bridge trades that GitHub token for a short-lived Copilot API token, caching it and refreshing about two minutes before it expires.
- Upstream. Requests go to Copilot's API with the editor and integration headers it expects.
- Translation. OpenAI-shaped requests pass through nearly untouched. Anthropic-shaped requests are translated to OpenAI form on the way in and back to Anthropic form — including streaming SSE events — on the way out.
Troubleshooting
"Not authenticated"
Run copilotbridge auth, or set GH_TOKEN.
Copilot token exchange fails
Your GitHub account may not have an active Copilot subscription, or the token expired. Re-run auth.
401 from the bridge
You set BRIDGE_API_KEY but the client sent a different key. Make the client's key match, or unset the variable.
Model not found
Call GET /v1/models and use an id it returns. Availability follows your plan.
Streams stall
The bridge sends an SSE keepalive every 15 seconds so idle watchdogs don't tear down slow responses. If a client still disconnects, raise its idle timeout.
Limitations
count_tokensreturns a character-based estimate; Copilot exposes no exact counter.- The Anthropic surface implements what Claude Code exercises in practice, not every corner of the Anthropic API.
- Copilot's token-exchange endpoint is undocumented and could change; this is unofficial software.
Running a hosted instance
The default is single-user: the process holds one GitHub token. Set
BRIDGE_MODE=hosted to run a shared bridge where
anyone can sign in with their own GitHub account at /login
and receive a bridge key tied to their own Copilot subscription.
export BRIDGE_MODE=hosted export BRIDGE_SECRET=$(node -e "console.log(require('crypto').randomBytes(32).toString('hex'))") export BRIDGE_PUBLIC_URL=https://bridge.example.com copilotbridge start --host 127.0.0.1 --port 4141 # put Caddy or nginx in front for TLS
- GitHub tokens are encrypted at rest with AES-256-GCM under
BRIDGE_SECRET. Lose the secret and every user must sign in again. - Bridge keys are stored only as SHA-256 hashes and are shown to the user exactly once. Signing in again revokes old keys.
POST /api/keys/rotatewith an existing key as the bearer token revokes it and returns a fresh one.- Each request is served with that user's own Copilot token, so usage and rate limits are per person, not per server.
- Login starts are throttled per IP. Always run behind HTTPS.
Security
- Keep the bridge bound to
127.0.0.1unless you have a reason not to. - If you bind to a public interface, always set
BRIDGE_API_KEY. - The stored GitHub token grants Copilot access. It lives at
~/.copilotbridge/github-tokenwith mode600; treat it like a password.
Copilot Bridge is unofficial community software. Not affiliated with, endorsed by, or official software from GitHub or Microsoft. Use it in accordance with the GitHub Copilot terms of service.