README
¶
Codex OAuth Proxy
Codex OAuth Proxy exposes the models included with your ChatGPT Codex access through OpenAI-compatible HTTP endpoints. It handles Codex OAuth credentials, forwards requests to the ChatGPT Codex backend, and translates streaming responses for clients such as OpenCode, editors, and agent tools.
It also provides an MCP server at /mcp. Agents can use it to discover the Codex models available to your account and send one-shot prompts without configuring an OpenAI API client.
┌───────────────┐ ┌───────────────────┐ ┌───────────────────────┐
│ External Tool │ │ Local Proxy │ │ Codex Backend │
│ (OpenCode/etc)│ │ │ │ (ChatGPT Responses) │
└───────┬───────┘ └─────────┬─────────┘ └───────────┬───────────┘
│ │ │
│ API or MCP request │ Codex API request │
│ ─────────────────────────▶ │ ─────────────────────────────▶ │
│ │ OAuth access token │
│ │ │
│ API or MCP response │ Codex API response │
│ ◀───────────────────────── │ ◀───────────────────────────── │
│ JSON or SSE stream │ │
│ │ │
▼ ▼ ▼
Use it when a client supports the OpenAI Chat Completions or Responses API but cannot sign in to Codex directly.
Quick start
You need a ChatGPT account with Codex access and an existing Codex CLI login.
Install the proxy with npm:
npm install -g codex-oauth-proxy
Other installation options:
# mise
mise use -g go:github.com/dvcrn/codex-oauth-proxy/cmd/codex-oauth-proxy@latest
# Go
go install github.com/dvcrn/codex-oauth-proxy/cmd/codex-oauth-proxy@latest
Sign in with the Codex CLI if you have not already, then start the proxy with a key of your choice:
codex login
ADMIN_API_KEY="replace-with-a-long-random-value" codex-oauth-proxy
The server listens on http://localhost:9879 by default. Send the admin key as the bearer token:
curl http://localhost:9879/v1/chat/completions \
-H "Authorization: Bearer replace-with-your-admin-key" \
-H "Content-Type: application/json" \
-d '{
"model": "gpt-5.5",
"messages": [{"role": "user", "content": "Explain this repository in one sentence."}],
"stream": false
}'
Point OpenAI-compatible clients at http://localhost:9879/v1 and use the same admin key as their API key.
Authentication
There are two separate credentials:
- Codex OAuth credentials authenticate the proxy with the upstream ChatGPT Codex service. The default
autostore imports an existing Codex CLI login into$XDG_CONFIG_HOME/codex-oauth-proxy/auth.json(or~/.config/codex-oauth-proxy/auth.jsonwhenXDG_CONFIG_HOMEis unset), keeps its token chain separate from the CLI, and refreshes it when needed. ADMIN_API_KEYauthenticates clients with your proxy. Protected endpoints accept eitherAuthorization: Bearer <key>orX-API-Key: <key>.
The default credential mode should work for most local installations. Other modes are available when you need an explicit source:
codex-oauth-proxy --creds-store=xdg
codex-oauth-proxy --creds-store=xdg --creds-path=/path/to/auth.json
codex-oauth-proxy --creds-store=legacy
Endpoints
| Endpoint | Purpose |
|---|---|
POST /v1/chat/completions |
OpenAI-compatible chat completions |
POST /v1/responses |
OpenAI-compatible Responses API |
GET /v1/models |
Models available to the signed-in account, including reasoning-effort variants |
POST /mcp |
Stateless MCP server with ask_codex and ask_codex_models |
POST /admin/auth/start |
Start Workers device authorization |
GET /admin/auth/status |
Read the current device authorization state |
POST /admin/auth/status |
Poll device authorization and store completed credentials |
POST /admin/tokens |
Store Codex credentials manually in Workers KV |
GET /admin/status |
Report whether Workers credentials are configured |
GET /health |
Health check |
The model list comes from the Codex backend. Query /v1/models instead of hard-coding model IDs. Clients that cannot set reasoning effort separately can append a suffix such as -low, -medium, -high, -xhigh, or -max when supported by that model.
MCP clients
The /mcp endpoint lets MCP clients use your Codex account through two tools. It uses stateless streamable HTTP with JSON responses, so the server keeps no conversation or session state between calls.
MCP requests use the proxy's Codex OAuth credentials upstream and the same ADMIN_API_KEY as the protected API endpoints. No separate OpenAI API key is required.
MCP configuration varies by client. Configure a streamable HTTP server with:
| Setting | Value |
|---|---|
| URL | http://localhost:9879/mcp |
| Header | Authorization: Bearer replace-with-your-admin-key |
For clients that use an mcpServers JSON object:
{
"mcpServers": {
"ask-codex": {
"type": "http",
"url": "http://localhost:9879/mcp",
"headers": {
"Authorization": "Bearer replace-with-your-admin-key"
}
}
}
}
The client discovers these tools after it connects:
| Tool | Input | Result |
|---|---|---|
ask_codex_models |
None | Available model IDs, display names, and supported reasoning efforts |
ask_codex |
model, prompt |
The requested model, model that served the request, and response text |
Call ask_codex_models first when the model ID is not already known. Reasoning effort can be selected with a model suffix such as gpt-5.5-high when that effort appears in the model listing.
ask_codex is one-shot. It does not retain conversation history, so prompt must include all context needed for that call. The returned model may differ from requested_model when the proxy normalizes a model ID.
Configuration
| Setting | Default | Description |
|---|---|---|
ADMIN_API_KEY |
required | Key used to protect generation, admin, and MCP requests |
PORT |
9879 |
Listening port |
ENV |
development |
Use production for JSON logs |
DISABLE_HEALTH_LOGS |
false |
Disable request logs for /health |
--creds-store |
auto |
Credential source for normal Codex CLI logins: auto, xdg, or legacy |
--creds-path |
platform default | Credential file for xdg or legacy mode |
Deploy to Cloudflare Workers
The ChatGPT Codex endpoints reject direct Cloudflare Workers egress with HTTP 403. A Workers VPC tunnel is therefore required so requests leave through a machine outside Cloudflare's Worker IP ranges.
Install mise, just, and Wrangler 4, then run mise install and wrangler login. The account, namespace, and tunnel IDs checked into wrangler.toml belong to the maintainer deployment and must be replaced for another Cloudflare account.
-
In Cloudflare Dashboard > Workers VPC > Tunnels, create a remotely managed tunnel. Run the generated
cloudflaredinstaller on a machine with normal Internet access. Workers VPC requirescloudflared2025.7.0 or newer and QUIC access over outbound UDP port 7844. See Cloudflare's tunnel setup. -
Create a KV namespace:
wrangler kv namespace create CODEX_OAUTH_PROXY_KV -
Put the account ID, returned KV ID, and tunnel UUID in
wrangler.toml:account_id = "<ACCOUNT_ID>" kv_namespaces = [ { binding = "CODEX_AUTH", id = "<KV_NAMESPACE_ID>" } ] vpc_networks = [ { binding = "CODEX_EGRESS", tunnel_id = "<TUNNEL_ID>", remote = true } ]The VPC network binding is documented in Cloudflare's VPC Networks guide.
-
Deploy and set the client-facing admin key at Wrangler's secure prompt:
wrangler deploy wrangler secret put ADMIN_API_KEY -
Optional: configure a hostname as a Workers Custom Domain instead of creating a CNAME to
workers.dev:routes = [ { pattern = "codex.example.com", custom_domain = true } ]Run
wrangler deployagain after adding the route.
Authorize Codex on Workers
The device flow is available only in the Workers build. All admin requests require Authorization: Bearer <ADMIN_API_KEY> or X-API-Key: <ADMIN_API_KEY>.
read -rs ADMIN_API_KEY
export ADMIN_API_KEY
BASE_URL="https://<WORKER_NAME>.<SUBDOMAIN>.workers.dev"
curl -X POST "$BASE_URL/admin/auth/start" \
-H "Authorization: Bearer $ADMIN_API_KEY"
Open the returned verificationUrl, enter userCode, and approve access. Poll no faster than retryAfterSeconds. Stop on authenticated, denied, expired, or failed; the session also expires automatically after 15 minutes:
curl -X POST "$BASE_URL/admin/auth/status" \
-H "Authorization: Bearer $ADMIN_API_KEY"
curl "$BASE_URL/admin/status" \
-H "Authorization: Bearer $ADMIN_API_KEY"
POST /admin/tokens provides manual credential setup. It accepts accessToken plus optional refreshToken, accountId, idToken, and RFC 3339 lastRefresh fields. Successful device or manual authorization stores credentials in the configured CODEX_AUTH KV namespace.
Development
mise run test
mise run build