copilot-proxy
High-performance Go proxy that exposes Anthropic and OpenAI-compatible APIs, forwarding all requests to GitHub Copilot's backend (api.githubcopilot.com). This lets you use any tool that speaks the Anthropic or OpenAI protocol — such as Claude Code, the OpenAI Python SDK, or curl — with your GitHub Copilot subscription.
Features
- Anthropic Messages API (
POST /v1/messages) — full request/response translation between Anthropic and OpenAI formats
- OpenAI Chat Completions API (
POST /v1/chat/completions) — near zero-copy passthrough (tools-aware, see below)
- OpenAI Responses API (
POST /v1/responses) — near zero-copy passthrough
- SSE streaming support for all endpoints
- Tool use — Anthropic tool definitions are translated to OpenAI function calling format
- Parallel tool use — reliable parallel tool calls on both Anthropic and OpenAI paths via forced-streaming aggregation
- Extended thinking — Anthropic
thinking parameter is mapped to max_completion_tokens
- GitHub OAuth device code flow with automatic token caching and refresh
- Automatic retry with exponential backoff on transient upstream errors (429, 502, 503, 504)
- Compressed request bodies — transparent decompression of
gzip and zstd Content-Encoding (used by Codex CLI)
- Structured JSON logging with configurable log levels
- Connection pooling and HTTP/2 support
- Single static binary, zero runtime dependencies (distroless Docker image)
Quick Start
Build from source
go build -o copilot-proxy .
./copilot-proxy
Docker
docker pull docker.io/sozercan/copilot-proxy:latest
docker run -p 1337:1337 -v ~/.config/copilot-proxy:/home/nonroot/.config/copilot-proxy sozercan/copilot-proxy:latest
Or build from source:
docker build -t copilot-proxy .
docker run -p 1337:1337 -v ~/.config/copilot-proxy:/home/nonroot/.config/copilot-proxy copilot-proxy
Kubernetes
A sample deployment manifest is included in k8s/copilot-proxy.yaml:
kubectl apply -f k8s/copilot-proxy.yaml
The image is available at docker.io/sozercan/copilot-proxy with multi-arch support (linux/amd64, linux/arm64).
First Run
On first run, you'll be prompted to authenticate via GitHub's device code flow:
- Visit the URL shown in the terminal
- Enter the one-time code displayed
- Authorize the application
Tokens are cached to ~/.config/copilot-proxy/ and automatically refreshed before expiry.
Configuration
| Flag |
Env Var |
Default |
Description |
--port |
PORT |
1337 |
Listen port |
--host |
HOST |
0.0.0.0 |
Listen host |
--token-dir |
TOKEN_DIR |
~/.config/copilot-proxy |
Token storage directory |
--log-level |
LOG_LEVEL |
info |
Log level (debug, info, or error) |
Usage Examples
Claude Code
export ANTHROPIC_BASE_URL=http://localhost:1337
# Claude Code will use the /v1/messages endpoint automatically
OpenAI Codex CLI
export OPENAI_BASE_URL=http://localhost:1337/v1
codex --model gpt-5.4
Anthropic Messages API
curl http://localhost:1337/v1/messages \
-H "Content-Type: application/json" \
-d '{
"model": "claude-sonnet-4",
"max_tokens": 1024,
"messages": [
{"role": "user", "content": "Hello, world!"}
]
}'
Anthropic Messages API (streaming)
curl http://localhost:1337/v1/messages \
-H "Content-Type: application/json" \
-d '{
"model": "claude-sonnet-4",
"max_tokens": 1024,
"stream": true,
"messages": [
{"role": "user", "content": "Hello, world!"}
]
}'
OpenAI Chat Completions API
curl http://localhost:1337/v1/chat/completions \
-H "Content-Type: application/json" \
-d '{
"model": "gpt-4o",
"messages": [
{"role": "user", "content": "Hello, world!"}
]
}'
OpenAI Responses API
curl http://localhost:1337/v1/responses \
-H "Content-Type: application/json" \
-d '{
"model": "gpt-4o",
"input": "Hello, world!"
}'
API Endpoints
POST /v1/messages (Anthropic)
Full Anthropic Messages API compatibility. Incoming requests are translated to OpenAI Chat Completions format, forwarded to GitHub Copilot, and responses are translated back to Anthropic format.
Supported features:
- Text and tool-use content blocks
- System messages (string or content block array)
- Tool definitions and tool choice (
auto, any, tool)
- Stop sequences
- Extended thinking (
thinking.type: "enabled")
- Streaming with proper Anthropic SSE event translation (
message_start, content_block_start, content_block_delta, content_block_stop, message_delta, message_stop)
Model name normalization:
Dated model suffixes (e.g., claude-sonnet-4-20250514) are stripped automatically. Hyphenated version numbers (e.g., claude-sonnet-4-5) are mapped to dotted form (claude-sonnet-4.5).
Available models (GET /v1/models):
The list of available models is fetched dynamically from the upstream GitHub Copilot API. Query the endpoint to see all currently available models:
curl http://localhost:1337/v1/models
Known models
| Provider |
Model name |
| OpenAI |
gpt-4o |
| OpenAI |
gpt-4.1 |
| OpenAI |
gpt-5-mini |
| OpenAI |
gpt-5.1 |
| OpenAI |
gpt-5.1-codex |
| OpenAI |
gpt-5.1-codex-mini |
| OpenAI |
gpt-5.1-codex-max |
| OpenAI |
gpt-5.2 |
| OpenAI |
gpt-5.2-codex |
| OpenAI |
gpt-5.3-codex |
| OpenAI |
gpt-5.4 |
| Anthropic |
claude-haiku-4.5 |
| Anthropic |
claude-sonnet-4 |
| Anthropic |
claude-sonnet-4.5 |
| Anthropic |
claude-sonnet-4.6 |
| Anthropic |
claude-opus-4.5 |
| Anthropic |
claude-opus-4.6 |
| Anthropic |
claude-opus-4.6-1m |
| Google |
gemini-2.5-pro |
| Google |
gemini-3-pro-preview |
| Google |
gemini-3-flash-preview |
| Google |
gemini-3.1-pro-preview |
POST /v1/chat/completions (OpenAI)
Near zero-copy passthrough for requests without tools. When tools are present, the proxy injects parallel_tool_calls: true and forces streaming to the upstream for reliable parallel tool call support, then aggregates the response back to non-streaming JSON for the client. Streaming requests with tools are passed through as-is (parallel tool calls work natively in streaming mode).
POST /v1/responses (OpenAI)
Near zero-copy passthrough for the OpenAI Responses API. Only authentication headers are injected.
GET /v1/models
Proxies the upstream GitHub Copilot models endpoint, returning all available models dynamically. No hardcoded model list — new models are available as soon as Copilot adds them.
GET /healthz
Health check endpoint. Returns {"status":"ok"}.
Architecture
┌─────────────────────────────────────────────────────┐
│ copilot-proxy │
│ │
│ /v1/messages ──► Translate ──► /chat/completions │
│ Anthropic→OAI api.githubcopilot │
│ Translate ◄── .com │
│ OAI→Anthropic │
│ │
│ /v1/chat/completions ─────────► /chat/completions │
│ (passthrough; forced-stream │
│ + aggregate when tools present) │
│ /v1/responses ────────────────► /responses │
│ (passthrough) │
│ │
│ auth/ ── Device code flow ── Token cache & refresh │
└─────────────────────────────────────────────────────┘
Packages:
| Package |
Responsibility |
main |
CLI flags, HTTP server setup, graceful shutdown |
auth/ |
GitHub OAuth device code flow, Copilot token exchange, disk caching, auto-refresh with sync.RWMutex |
proxy/ |
HTTP handlers, Anthropic↔OpenAI translation, SSE streaming, retry with backoff |
models/ |
Request/response type definitions for both APIs (data-only, no logic) |
logger/ |
Structured JSON logging with level filtering |
server/ |
Reusable HTTP server lifecycle (Start/Stop/IsRunning) |
cmd/menubar/ |
macOS menubar app using systray |
A native macOS menubar app that lets you start/stop the proxy without a terminal.
Build & Run
make build-app
open "Copilot Proxy.app"
Features
- Start/Stop toggle — click the menu item to start or stop the proxy
- Status icon — white robot when running, gray when stopped
- Launch at Login — optional macOS LaunchAgent integration
- Hover the icon for status tooltip (running/stopped, port)
Development
# Build
go build -o copilot-proxy .
# Build menubar app
make build-app
# Run all tests
go test ./... -count=1
# Run specific tests
go test ./proxy/ -run TestHandle -v
go test ./proxy/ -run TestMapStopReason/stop -v
# Lint
go vet ./...
License
MIT