Use GitHub Copilot, ChatGPT/Codex, and Anthropic accounts from applications that support an OpenAI-compatible API. Anthropic clients can also connect through the native Messages API.
Provider credentials stay on the machine running the proxy. Client applications connect to the proxy's local URL and can use separate proxy API keys.
You need Java 21 and access to at least one supported provider.
-
Download the JAR from GitHub Releases. Choose the
AIProxy-<version>.jarasset, then open a terminal in the folder where you saved it. The examples below useAIProxy-5.3.jar; substitute your downloaded filename if different. -
Log in to a provider. Choose one:
java -jar AIProxy-5.3.jar auth copilot login java -jar AIProxy-5.3.jar auth codex login java -jar AIProxy-5.3.jar auth anthropic login
-
Start the proxy:
java -jar AIProxy-5.3.jar
-
Configure your client:
Setting Value API type OpenAI-compatible Base URL http://127.0.0.1:10531/v1API key Any nonempty placeholder, such as local, until you enable proxy API keysModel An ID from the startup output or /v1/models
By default, the proxy listens only on your machine, enables providers with available credentials, and runs an inference check for each enabled provider. These checks can consume provider quota. To start without sending test prompts:
java -jar AIProxy-5.3.jar serve --startup-check credentialsStop the proxy with Ctrl+C.
Check your saved authentication settings with:
java -jar AIProxy-5.3.jar auth statusUse the same --config FILE for login and serving if you customize credential locations. Place options after the final subcommand, for example auth anthropic login --config production.yaml or serve --config production.yaml.
Proxy-managed logins default to ~/.aiproxy/ on every platform (~ is the home directory of the user running Java):
| Provider | Default file |
|---|---|
| Copilot | ~/.aiproxy/copilot-auth.json |
| Native Codex | ~/.aiproxy/codex-auth.json |
| Anthropic | ~/.aiproxy/anthropic-auth.json |
Explicit CLI, environment, and YAML path overrides still take precedence, in that order. Codex CLI/device-login credentials retain their separate CODEX_HOME / ~/.codex/auth.json locations.
java -jar AIProxy-5.3.jar auth copilot login
java -jar AIProxy-5.3.jar serve --provider copilotFollow the printed device-login instructions. If your login expires, run the login command again.
To supply an existing token, set AIPROXY_COPILOT_TOKEN or use --copilot-token-file PATH. The file can contain a raw token, a proxy credential file, or a Copilot CLI JSONC configuration. An explicit file takes precedence over the environment token and saved login.
For GitHub Enterprise Cloud, use --copilot-github-host tenant.ghe.com for both login and serving, with separate credentials. Enterprise Cloud has not been live-validated; GitHub Enterprise Server is unsupported.
To remove the proxy's saved login:
java -jar AIProxy-5.3.jar auth copilot logoutThis leaves explicitly supplied token files untouched.
For the proxy's native ChatGPT login:
java -jar AIProxy-5.3.jar auth codex login
java -jar AIProxy-5.3.jar serve --provider codex --codex-auth-mode nativeComplete sign-in in a browser on the same computer. Use --no-browser to print the URL instead of opening it automatically. Account access to native sign-in is required.
If you already use the official Codex CLI, you can use its saved login instead:
java -jar AIProxy-5.3.jar serve --provider codex --codex-auth-mode cliFor device login, including from a headless machine, install the official Codex CLI on PATH, then run:
java -jar AIProxy-5.3.jar auth codex login --device-auth
java -jar AIProxy-5.3.jar serve --provider codex --codex-auth-mode cliEnable device-code login in your ChatGPT account or workspace settings if required. Device login uses CLI credentials and can replace an existing CLI login. To choose a destination, pass --codex-oauth-file PATH to both commands; the filename must be auth.json.
--codex-auth-mode |
When to use it |
|---|---|
auto (default) |
Prefer an explicitly configured CLI file, then a native login, then an existing Codex CLI login |
native |
Use only the proxy's native login; customize its location with --codex-native-auth-file PATH |
cli |
Use Codex CLI credentials; customize their location with --codex-oauth-file PATH |
To switch the native account, run auth codex login --new-account. Restart the proxy after changing the login. Only one native account is active at a time.
To log out of the native account:
java -jar AIProxy-5.3.jar auth codex logout --yesCLI credentials are separate. Remove them with the official codex logout, using the same CODEX_HOME and credential-storage settings used for login. In auto mode, a remaining CLI login may be selected after native logout.
Native mode has request restrictions: omit temperature, top-p, token-limit, metadata, and background-execution options. Unsupported options return HTTP 400. Use the default native endpoint and settings; CLI endpoint overrides, --codex-store, and prompt-cache header forwarding are incompatible with native mode.
java -jar AIProxy-5.3.jar auth anthropic login
java -jar AIProxy-5.3.jar serve --provider anthropicFor SSH or a headless machine:
java -jar AIProxy-5.3.jar auth anthropic login --no-browserOpen the printed URL on another device and paste the returned code#state into the original terminal. Without an interactive console, add --allow-stdin-oauth-code.
You can also supply CLAUDE_CODE_OAUTH_TOKEN. To customize the saved login location, use --anthropic-oauth-file PATH for both login and serving.
java -jar AIProxy-5.3.jar auth anthropic logoutList the models available through the running proxy:
curl http://127.0.0.1:10531/v1/modelsIn Windows PowerShell, use curl.exe if curl refers to a PowerShell alias. If proxy authentication is enabled, add -H "Authorization: Bearer YOUR_PROXY_KEY".
Copy a returned model ID into your client. Copilot IDs include copilot/. You can explicitly select another provider with codex/<model-id> or anthropic/<model-id>. Qualify the model when multiple providers offer the same ID.
The OpenAI-compatible model list includes optional OpenRouter-style metadata: name, context_length, top_provider.max_completion_tokens, architecture.input_modalities, and a known subset of supported_parameters. These are extensions, not standard OpenAI model fields. Missing fields mean unknown, not unlimited or unsupported. Limits come from the authenticated provider catalog; configured-only models and fallback model names do not acquire guessed limits.
Each entry also has an aiproxy object with provider, qualified_id, and metadata_source (upstream, configured, seed, or unknown). When available it includes fetched_at, max_input_tokens, upstream capabilities, reasoning_efforts, and upstream_endpoints. Discovered Codex entries also identify their auth_profile as native or cli. Context length, input limits, and output limits are distinct; do not add them together. fetched_at is the snapshot's original fetch time, including when served from cache or last-good fallback. Upstream capabilities describe model facts, not a guarantee that every feature works through every proxy protocol. No pricing is inferred.
For a simple Chat Completions request, save this as request.json, replacing MODEL_ID:
{
"model": "MODEL_ID",
"messages": [{"role": "user", "content": "Hello!"}]
}Then send it:
curl http://127.0.0.1:10531/v1/chat/completions -H "Content-Type: application/json" --data-binary "@request.json"For streaming, add "stream": true to the JSON and use curl -N.
| Client or task | URL |
|---|---|
| OpenAI-compatible client base URL | http://127.0.0.1:10531/v1 |
| Chat Completions request | POST /v1/chat/completions |
| Responses request | POST /v1/responses |
| Anthropic client base URL | http://127.0.0.1:10531 |
| Anthropic Messages request | POST /v1/messages (Anthropic or eligible Copilot models) |
| Available models | GET /v1/models |
| Token usage | GET /v1/usage |
| Health check | GET /health |
Proxy API keys go in Authorization: Bearer YOUR_PROXY_KEY; Anthropic clients can use x-api-key instead. Do not put provider OAuth tokens in client configuration.
If a Responses continuation fails after a restart or account change, resend the full conversation instead of the previous response ID. See API compatibility for supported request features and limitations.
Clients do not all consume OpenRouter-style fields automatically. Codex supports an explicit model_context_window in its configuration; pi supports contextWindow and maxTokens in its custom model configuration. The repository includes a Python 3.11+ helper that fetches the catalog and prints a configuration snippet for one model:
python scripts/export-client-config.py --client codex --model copilot/MODEL_ID > codex-proxy.toml
python scripts/export-client-config.py --client pi --model copilot/MODEL_ID > pi-proxy.jsonSet AIPROXY_API_KEY to your proxy key before fetching; in open mode, use a placeholder such as local-proxy so the clients can resolve a credential. The generated files reference that environment variable and never embed its value. Use --base-url http://HOST:PORT/v1 for another proxy, or --catalog models.json to convert a saved OpenAI-compatible model-list response offline. The helper never changes client settings itself.
Review and merge the Codex snippet into your Codex config.toml, or merge the pi provider entry into ~/.pi/agent/models.json. Codex uses Responses; pi uses Chat Completions. Both exports pin the provider-qualified model ID so its limits stay attached to the same route. pi exports reported text/image input modes and compatible reasoning-level mappings; Codex chooses an advertised compatible effort when known. If a required limit is missing, the helper stops: supply a verified --context-window N and, for pi, --max-output-tokens N. Unknown capabilities are omitted and client defaults may still apply. Regenerate snippets after changing models or account limits. These config shapes are offline-tested, not live-validated in the client applications.
The pi exporter rejects the native Codex auth profile: pi's standard Chat Completions adapter sends output-token limits, which that profile forbids. Use a Codex CLI-profile, Copilot, or Anthropic route for pi. Changing metadata does not relax native protocol restrictions.
Claude Code uses the native Anthropic model response, which this proxy forwards unchanged, including max_input_tokens, max_tokens, and capabilities when returned. Requests with anthropic-version or x-api-key select that native format; use Bearer authorization without those headers to fetch the extended OpenAI format. Native discovery continues to list Anthropic models only. For Copilot, use the explicit model setup below; these OpenAI metadata extensions do not add Copilot models to Claude Code's native picker. Claude Code feature detection remains subject to its gateway and model configuration.
Start the proxy with your Copilot login:
java -jar AIProxy-5.3.jar serve --provider copilotChoose a copilot/<model-id> from /v1/models whose account catalog advertises /v1/messages. Listing a model does not guarantee Messages support: unsupported models return HTTP 400. This route accepts any model advertising that endpoint, regardless of its name; models offering only Chat Completions or Responses cannot be used here. Claude Code was user-tested successfully with Sonnet and Haiku, including Haiku tool use and continuation. See validation details.
In a separate PowerShell terminal, replace copilot/MODEL_ID with that qualified ID:
$env:ANTHROPIC_BASE_URL = "http://127.0.0.1:10531"
$env:ANTHROPIC_AUTH_TOKEN = "YOUR_PROXY_KEY"
$env:ANTHROPIC_MODEL = "copilot/MODEL_ID"
$env:CLAUDE_CODE_EFFORT_LEVEL = "auto"
$env:ANTHROPIC_DEFAULT_HAIKU_MODEL = $env:ANTHROPIC_MODEL
$env:ANTHROPIC_DEFAULT_SONNET_MODEL = $env:ANTHROPIC_MODEL
$env:ANTHROPIC_DEFAULT_OPUS_MODEL = $env:ANTHROPIC_MODEL
$env:CLAUDE_CODE_SUBAGENT_MODEL = $env:ANTHROPIC_MODEL
claudeUse your proxy key, or a placeholder such as local-proxy when the proxy runs in open mode. Keep the base URL without /v1. Pinning the auxiliary models keeps those requests on Copilot too. Use effort auto for Haiku: an explicit medium effort caused an upstream 400 in testing. Claude Code may display unrecognized-model and auto-mode classifier billing notices; these did not block the tested requests. See Claude Code's gateway setup and model configuration.
Use explicit model IDs: the native Anthropic model picker does not list Copilot models, and /v1/messages/count_tokens is not supported. Unqualified names and anthropic/<model-id> still use Anthropic; Copilot Messages requests never fail over to another provider or protocol.
java -jar AIProxy-5.3.jar serve --provider copilot,codex
java -jar AIProxy-5.3.jar serve --provider all
java -jar AIProxy-5.3.jar serve --provider-order codex,copilot,anthropicautoenables providers with available credentials.allrequires all three providers;bothmeans Codex and Anthropic.--provider-orderchanges preference without enabling additional providers. The default order is Copilot, Codex, Anthropic.--default-provider codexsets an explicit preference among enabled providers. Use qualified model IDs when you need a particular provider.
To allow retries through another provider offering the same model, add --failover. It is disabled by default, applies only to eligible unqualified requests, and never substitutes a different model. Qualified requests and conversation continuations stay with their selected provider.
Use --copilot-models, --codex-models, or --anthropic-models with comma-separated IDs to restrict or configure model choices. Copilot and native Codex accept only models listed for your account. CLI Codex and Anthropic treat these lists as explicit overrides.
Create production.yaml with the settings you need:
server:
host: 127.0.0.1
port: 10531
routing:
provider: auto
startup:
check: credentialsjava -jar AIProxy-5.3.jar serve --config production.yaml
java -jar AIProxy-5.3.jar config show --config production.yamlYAML is loaded only when you pass --config. Paths inside it are relative to the YAML file's directory. For more settings, copy aiproxy.example.yaml or run:
java -jar AIProxy-5.3.jar serve --helpCommand-line options override environment variables, which override YAML settings. For example, --port 8080, AIPROXY_PORT=8080, and server.port: 8080 set the same option at different priorities.
Common environment variables include AIPROXY_PROVIDER, AIPROXY_HOST, AIPROXY_PORT, AIPROXY_STARTUP_CHECK, and AIPROXY_CLIENT_KEYS_FILE. Provider settings use AIPROXY_COPILOT_*, AIPROXY_CODEX_*, or AIPROXY_ANTHROPIC_* names. Use config show to check effective settings and their sources without printing secrets.
Store proxy client keys in files or environment variables, not inline in YAML. Custom provider and OAuth URLs must use HTTPS, except for local development addresses.
By default, the proxy accepts requests without a client API key. To require one, follow these steps after logging in to a provider. Proxy client keys are separate from your Copilot, Codex, or Anthropic credentials.
-
Create a client key file. Run these commands from the directory containing the downloaded JAR.
macOS/Linux (Bash):
java -jar AIProxy-5.3.jar key generate myapp > keys.txtWindows PowerShell:
java -jar AIProxy-5.3.jar key generate myapp | Set-Content -Encoding ascii keys.txt
The file contains one entry such as
myapp:sk-proxy-<32 hex characters>. Use the generated value, not this placeholder. Keep this file private. These commands replacekeys.txt; to add another client, append with>> keys.txtin Bash orAdd-Content -Encoding ascii keys.txtin PowerShell. Use onename:keyor bare key per line. -
Start the proxy with key enforcement enabled. Stop any existing proxy instance first.
java -jar AIProxy-5.3.jar serve --client-keys-file keys.txt
-
Configure the client application.
Setting Value Base URL http://127.0.0.1:10531/v1API key The sk-proxy-...portion fromkeys.txt, without themyapp:prefixModel An ID from /v1/modelsor the startup outputReplace the quick-start placeholder API key (
local) with this generated key. -
Verify authentication. In a second terminal, replace
YOUR_GENERATED_KEYbelow with the bare key:curl -i http://127.0.0.1:10531/v1/models -H "Authorization: Bearer YOUR_GENERATED_KEY" curl -i http://127.0.0.1:10531/v1/modelsIn PowerShell, use
curl.exeinstead ofcurl. The authenticated request should return HTTP 200; the request without a key should return HTTP 401./healthremains accessible without authentication.
For YAML configuration, add this to your configuration file and start with serve --config production.yaml. The key-file path is relative to the YAML file:
client_auth:
keys_file: ./keys.txtOrdinary client keys can view their own /v1/usage statistics. An admin key can view usage for all keys. Generate a separate bare key with java -jar AIProxy-5.3.jar key generate and save it in admin-key.txt using the same redirection or PowerShell encoding shown above. Then start with:
java -jar AIProxy-5.3.jar serve --client-keys-file keys.txt --admin-client-key-file admin-key.txtSend the admin key as a Bearer token when querying /v1/usage; give ordinary applications their own client keys.
To accept connections from other machines, add --host 0.0.0.0. Network binding requires proxy API keys. Clients must use the proxy machine's address instead of 127.0.0.1.
For browser applications, allow their origin with --cors-origin https://your-app.example. Origins must not include paths. --allow-any-cors requires proxy API keys.
java -jar AIProxy-5.3.jar doctor
java -jar AIProxy-5.3.jar doctor --inferenceAdd --config FILE when using a configuration file. doctor checks credentials and model availability; --inference also sends test prompts. A failed check or degraded model catalog produces a nonzero exit code. If diagnostics report an Anthropic credential file is already in use, stop the process using that file before retrying.
Choose a startup check with --startup-check:
| Mode | What to expect |
|---|---|
inference (default) |
Sends test prompts and reports the successful model |
credentials |
Checks credentials without sending test prompts |
off |
Skips the check; credential loading and model discovery still occur |
Inference checks choose the first model ID containing luna or haiku (case-insensitive), otherwise the last model in the list. Copilot and Codex can try other models after a failure, up to six in total. Anthropic tests only the selected model.
The startup Auth line shows the selected credential source and profile. Use Check to see whether the chosen test passed. The Models line helps diagnose availability:
| Model status | What to do |
|---|---|
discovered, cache |
Use the listed model IDs |
configured |
Check your model overrides if requests fail |
stale cache |
Read the discovery warning; the list could not be refreshed |
fallback (built-in) |
Anthropic discovery failed; the built-in list does not confirm account access |
unavailable |
Resolve the reported credential or discovery error before using that provider |
Ready with warnings means the proxy is running but one or more checks or model lookups need attention.
| Problem | Next step |
|---|---|
| No providers enabled | Log in to a provider, then run auth status |
| Wrong Codex login selected | Set --codex-auth-mode native or cli, and inspect config show |
| Invalid credential file | Correct the selected file or log in again; an invalid explicit file does not fall back to another login |
| Copilot says "model not available for integrator" | Try another listed model. If using a custom OAuth client ID, check copilot.oauth_client_id and log in again after changing it |
| HTTP 400 in native Codex mode | Remove unsupported request options; see the native-mode restrictions above |
| Unknown model or ambiguous provider | Copy an ID from /v1/models and qualify the provider if needed |
| Option rejected | Put options after the final subcommand and check that command's --help |
Operational and access logs use SLF4J + Logback and go to stderr by default. CLI command output remains on stdout. Access records contain structured key/value fields: method, path (without query strings), HTTP and upstream status, duration, provider, mode, byte counts, and stream-error state. The request_id field correlates request-time operational errors, access records, and optional diagnostic files. Operational logs do not capture request bodies or headers; unexpected exceptions are identified by class rather than potentially sensitive exception messages.
Set -Daiproxy.log.level=DEBUG before -jar to change the operational level, or -Daiproxy.log.access.level=OFF to suppress access records. For custom formats, per-package levels, or rolling-file/async appenders, supply a standard Logback configuration with -Dlogback.configurationFile=/path/to/logback.xml. Keep %X{request_id} and %kvp in custom text layouts to retain correlation and structured fields. Debug logging in third-party HTTP libraries can expose private traffic; enable it only temporarily.
Collection and retention: in production, collect stderr through your service manager/container logging driver and configure its size limits and retention (for example Docker's local driver with max-size: 10m and max-file: 5). Shell redirection alone does not bound disk usage. Optional diagnostic files below bypass Logback appenders: protect them with owner-only directory permissions, use an external rotation/cleanup policy and disk monitoring, and keep payload capture disabled except during investigations. Diagnostic writes remain synchronous; use them sparingly on busy deployments.
To compare successful and failed calls in a single append-only JSONL file:
java -jar AIProxy-5.3.jar serve --request-log-file ./logs/requests.jsonlThe filename enables capture by itself; --log-requests and --error-log-bodies are not required. Each completed HTTP request produces one record with timestamp, request ID, method, path, final HTTP status, original client request headers/body, and buffered response body. Authentication failures, unknown routes, and successful requests are included. Existing file contents are preserved and parent directories are created as needed.
Capture limits: sensitive headers and known structured secrets are redacted, and each body is capped at 256 KiB (truncated: true when capped). This is payload capture, not an unredacted wire dump. SSE response bytes are not buffered; response_body_available: false identifies responses without a buffered body. The original incoming streaming request is still captured. Prompts and tool output can remain private despite redaction: restrict file access and manage rotation/retention externally. A record is written at request completion, so in-flight requests are not yet present.
Equivalent configuration: YAML logging.request_file: ./logs/requests.jsonl or environment AIPROXY_REQUEST_LOG_FILE. This is independent of --log-requests --request-log-dir, which captures provider-stage diagnostics in separate files, and of --error-log-file. Use different filenames for all-request and error-only logs to avoid duplicate error records in one file.
Enable error logging by providing a filename—no separate activation flag is needed:
java -jar AIProxy-5.3.jar --error-log-file ./logs/errors.jsonlWithout a configured filename, error logging is off. Each HTTP 4xx/5xx response appends one JSON line (existing entries are preserved), containing timestamp, request ID, method, path, and status. Detected failures inside HTTP 200 streams are also logged, with stage: stream_error and stream_error: upstream_error; HTTP status and forwarded SSE bytes are not changed. These records can also indicate transport interruption/client disconnect, which cannot always be distinguished from an upstream failure. Parent directories are created as needed. Alternatively set logging.error_file: ./logs/errors.jsonl in YAML or AIPROXY_ERROR_LOG_FILE. This file is independent of the full request log directory.
Privacy change: error logs are now metadata-only by default. Add --error-log-bodies (YAML logging.error_bodies: true, environment AIPROXY_ERROR_LOG_BODIES=true) to opt into response-body and original request-header/body capture. Each captured body is capped at 256 KiB; known structured secrets and sensitive headers are redacted. Unreadable request bodies are marked body_unavailable. Stream payloads are not buffered for error logging. --no-error-log-bodies overrides a configured opt-in. Prompts, tool output, and unstructured/malformed content may still contain private data despite redaction.
For full request diagnostics, use --log-requests --request-log-dir ./logs/requests. Logs can contain sensitive responses, prompts, and tool output despite redaction; protect the directory and manage retention yourself.
To build from source instead, install Java 21 and Maven, clone this repository, and run mvn clean package from its root. The JAR is written to target/AIProxy-5.3.jar; run the examples above with that path.
The Java launcher is ProxyApplication. CLI commands live in cli, startup/assembly in bootstrap, HTTP dispatch in server, and each provider owns its backends, authentication and catalog under provider/<name>. Shared wire-format code lives in protocol, with common model, transport, SSE and replay infrastructure in dedicated packages. See the developer guide for package boundaries and naming rules.