Forked from jacksonkasi1/copilot-for-cursor with full Anthropic → OpenAI conversion + Responses API bridge.
Unlock the full power of GitHub Copilot in Cursor IDE.
Use all Copilot models (GPT-5.6, Claude Opus 4.8, Gemini 3.5, etc.) in Cursor — including Plan mode, Agent mode, and tool calls.
npx copilot-for-cursor@latestRequires Node.js 20+ and Bun. The package downloads its
copilot-apidependency and launches it directly rather than nesting anothernpxcommand. On first run, the launcher opens GitHub's device-login page and runs the provider-specific authentication flow before starting the proxy stack.
This starts both copilot-api (port 4141) and the proxy (port 4142) in a single terminal.
npm install -g copilot-for-cursor@latest
copilot-for-cursorTo update or uninstall the persistent command:
npm install -g copilot-for-cursor@latest
npm uninstall -g copilot-for-cursorgit clone https://github.com/CharlesYWL/copilot-for-cursor.git
cd copilot-for-cursor
npm install
bun run start.ts# Global installation
copilot-for-cursor --max
# npx
npx copilot-for-cursor@latest --maxMax mode automatically compacts conversation history when the estimated token count exceeds 80% of the model's input token limit. It summarizes older messages into a structured summary while keeping the most recent messages intact — letting you have much longer coding sessions without hitting token limits.
🛡️ Always-on safety net: Even without
--max, the proxy now auto-compacts at 95% of the model's input limit and falls back to hard truncation of the oldest messages if summarization fails. This prevents Cursor from ever hitting upstreamcontext_length_exceedederrors. Use--maxif you want proactive (80%) compaction for smoother long sessions.
Start the stack and a Cloudflare Quick Tunnel in one command:
# Global installation
copilot-for-cursor --tunnel
# npx
npx copilot-for-cursor@latest --tunnelChoose another provider or suppress a persisted auto-start setting:
# Global installation
copilot-for-cursor --tunnel=ngrok
copilot-for-cursor --tunnel=bore
copilot-for-cursor --no-tunnel
# npx
npx copilot-for-cursor@latest --tunnel=ngrok
npx copilot-for-cursor@latest --tunnel=bore
npx copilot-for-cursor@latest --no-tunnel--tunnel defaults to cloudflared. When the public tunnel is ready, the CLI prints the complete Cursor /v1 endpoint and copies it to the system clipboard. CLI tunnel flags affect the current run; use the dashboard or live settings API to persist auto-start.
The tunnel exposes the model API, not the management API. Dashboard and
/api/*management endpoints are intentionally limited to loopback access.
Cursor needs a publicly reachable endpoint rather than localhost. You have two options:
Option A — One-click tunnel (recommended)
Open the dashboard at http://localhost:4142/, go to the Tunnel tab, pick a provider (Cloudflare, ngrok, bore, or Tailscale) and click Start Tunnel. The public URL, QR code, and Cursor endpoint will appear instantly. Cloudflare and bore are downloaded automatically — no signup, no config. For a URL that survives restarts, see Fixed tunnel URL.
Option B — Run a tunnel manually
# Cloudflare (free, no signup)
cloudflared tunnel --url http://localhost:4142
# Or ngrok
ngrok http 4142
# Or bore (no signup)
bore local 4142 --to bore.pubCopy the resulting public URL (e.g., https://xxxxx.trycloudflare.com or http://bore.pub:PORT).
Cloudflare and ngrok give you an HTTPS URL. bore exposes plain HTTP on a
bore.pubport, which also works — reach for it when Cloudflare's account-less Quick Tunnels are rate limited (error code: 1015).
A quick tunnel hands you a new random URL on every restart, which means re-pasting the endpoint into Cursor each time. Pick one of these to get a URL that stays put, then tick Auto-start on the Tunnel tab so the same URL comes back on every launch.
| Provider | URL you get | Cost | Needs a domain? |
|---|---|---|---|
| ngrok static domain | your-name.ngrok-free.dev |
Free | No |
| Tailscale Funnel | machine.tailnet.ts.net |
Free | No |
| Cloudflare named tunnel | copilot.example.com |
Free | Yes — zone on Cloudflare |
| Cloudflare quick tunnel | random *.trycloudflare.com |
Free | No |
| bore.pub | bore.pub:PORT (plain HTTP) |
Free | No |
ngrok — easiest, no domain required
Every free ngrok account includes one static domain. Claim it under Domains in the ngrok dashboard, then paste it into the Tunnel tab with your authtoken. Equivalent CLI:
ngrok http 4142 --url https://your-name.ngrok-free.devTailscale Funnel — fixed URL derived from the machine
Install Tailscale and run tailscale up. In the admin console enable MagicDNS and HTTPS certificates, and grant the node the funnel attribute:
{ "nodeAttrs": [{ "target": ["autogroup:member"], "attr": ["funnel"] }] }Then pick Tailscale Funnel in the dashboard, or copilot-for-cursor --tunnel=tailscale. Equivalent CLI:
tailscale funnel 4142Cloudflare named tunnel — your own hostname
Requires a domain already on Cloudflare. Create the tunnel once and route DNS at it, then enter the hostname plus either the tunnel token (remote-managed) or the tunnel name (locally-managed):
cloudflared tunnel login
cloudflared tunnel create copilot-proxy
cloudflared tunnel route dns copilot-proxy copilot.example.comNamed tunnels never print their hostname, so the dashboard asks for it explicitly and uses it for the Cursor endpoint.
Staying up: Reconnect automatically is on by default. If the tunnel process dies, the proxy relaunches it with exponential backoff (capped at 30s) — and because the URL is fixed, Cursor keeps working without reconfiguration.
Tokens are stored in ~/.copilot-proxy/settings.json (owner-only where the OS supports it) and are never returned to the dashboard — it only ever sees a __saved__ placeholder.
Cursor → (public tunnel) → proxy-router (:4142) → copilot-api (:4141) → GitHub Copilot
- Port 4141 (
copilot-api): Authenticates with GitHub, provides the OpenAI-compatible API, and natively handles the Responses API for GPT-5.x models.- Powered by @jeffreycao/copilot-api, installed with this package.
- Port 4142 (
proxy-router): Converts Anthropic-format messages to OpenAI format, bridges Responses API for GPT-5.x models, handles thecus-prefix, and serves the dashboard. - Public tunnel: Cursor cannot reach
localhost, so a tunnel exposes the local proxy.
| File | Responsibility |
|---|---|
proxy-router.ts |
Entrypoint — Bun.serve, routing, CORS, dashboard, model list |
anthropic-transforms.ts |
Anthropic → OpenAI normalization (fields, tools, messages) |
responses-bridge.ts |
Chat Completions → Responses API bridge for GPT-5.x / goldeneye |
responses-converters.ts |
Responses API → Chat Completions format (sync & streaming SSE) |
stream-proxy.ts |
Streaming passthrough with chunk logging and error detection |
upstream-retry.ts |
Retries transient upstream failures (bare 403 forbidden, 408, 425, 429, 5xx, dropped connections) with backoff |
debug-logger.ts |
Request/response debug logging helpers |
start.ts |
One-command launcher for copilot-api + proxy-router |
max-mode.ts |
Auto-compaction for long conversations (--max flag) |
settings-config.ts |
Persistent live settings (~/.copilot-proxy/settings.json) |
startup-options.ts |
CLI parsing for max mode and tunnel overrides |
usage-db.ts |
Persistent request/token usage tracking |
auth-config.ts |
API key generation, validation, and config persistence |
upstream-auth.ts |
Upstream copilot-api authentication and key management |
- Go to Settings (Gear Icon) → Models.
- Add a new OpenAI Compatible model:
- Base URL: your tunnel URL plus
/v1— e.g.https://your-tunnel-url.trycloudflare.com/v1orhttp://bore.pub:PORT/v1 - API Key:
dummy(any value works) - Model Name: Use a prefixed name — e.g.,
cus-gpt-5.6-sol,cus-claude-opus-4-8
- Base URL: your tunnel URL plus
⚠️ Important: You must use thecus-prefix. Without it, Cursor routes the request to its own backend.
💡 Tip: Visit the Dashboard to see all available models and copy their IDs.
Cursor exposes Settings → Models → Task Models → Explore Subagent Model, where a cus-* model can be selected:
Known Cursor limitation: With custom/BYOK models, Cursor may ignore this selection and send the child request with the parent agent's model. The proxy does not force both models to match; it forwards the model ID Cursor sends. Same-model subagents work reliably, while selecting a different Explore model may still inherit the parent model.
The following catalog was returned by GitHub Copilot on July 9, 2026. Availability can change by account, organization policy, and upstream rollout; the dashboard and /v1/models endpoint are authoritative.
| Cursor Model Name | Model | Input Limit | Output Limit |
|---|---|---|---|
cus-claude-haiku-4-5 |
Claude Haiku 4.5 | 136K | 64K |
cus-claude-opus-4-5 |
Claude Opus 4.5 | 168K | 32K |
cus-claude-opus-4-6 |
Claude Opus 4.6 | 936K | 64K |
cus-claude-opus-4-7 |
Claude Opus 4.7 | 936K | 64K |
cus-claude-opus-4-8 |
Claude Opus 4.8 | 936K | 64K |
cus-claude-sonnet-4-5 |
Claude Sonnet 4.5 | 168K | 32K |
cus-claude-sonnet-4-6 |
Claude Sonnet 4.6 | 936K | 64K |
cus-claude-sonnet-5 |
Claude Sonnet 5 | 936K | 64K |
cus-gemini-2.5-pro |
Gemini 2.5 Pro | 128K | 64K |
cus-gemini-3-flash-preview |
Gemini 3 Flash (Preview) | 128K | 64K |
cus-gemini-3.1-pro-preview |
Gemini 3.1 Pro | 936K | 64K |
cus-gemini-3.5-flash |
Gemini 3.5 Flash | 936K | 64K |
cus-gpt-5-mini |
GPT-5 Mini | 128K | 64K |
cus-gpt-5.3-codex |
GPT-5.3 Codex | 272K | 128K |
cus-gpt-5.4 |
GPT-5.4 | 922K | 128K |
cus-gpt-5.4-mini |
GPT-5.4 Mini | 272K | 128K |
cus-gpt-5.5 |
GPT-5.5 | 922K | 128K |
cus-gpt-5.6-luna |
GPT-5.6 Luna | 922K | 128K |
cus-gpt-5.6-sol |
GPT-5.6 Sol | 922K | 128K |
cus-gpt-5.6-terra |
GPT-5.6 Terra | 922K | 128K |
cus-mai-code-1-flash-picker |
MAI-Code-1-Flash | 128K | 128K |
Embedding-only models also listed by the upstream API are cus-text-embedding-3-small, cus-text-embedding-3-small-inference, and cus-text-embedding-ada-002.
Claude minor-version aliases use dashes in Cursor (for example,
cus-claude-opus-4-8). The proxy translates them to the dotted upstream ID (claude-opus-4.8). GPT-5.x models are automatically routed through the Responses API bridge when required.
Cursor's model settings display 200K for BYOK models regardless of what your endpoint reports in /v1/models, and there is no UI field to change it. Renaming the model does not affect the displayed number.
That number is cosmetic. It does not cap what Cursor actually sends. Measured against this proxy, a single claude-opus-5 request reached 322,668 prompt tokens and was accepted upstream (HTTP 200) while Cursor still displayed 200K. The real ceiling is the upstream limit — 936K usable prompt tokens for the 1M Claude models — and the proxy's compaction always works from those real limits, not from Cursor's display.
So if you only wanted a larger context, you already have it; no configuration is needed.
Model aliases exist for a different reason: exposing a model under a shorter or more memorable name.
curl -X PATCH http://localhost:4142/api/settings \
-H 'Content-Type: application/json' \
-d '{"modelAliases":{"opus5x":"claude-opus-5"}}'cus-opus5x then appears in /v1/models carrying the real capabilities of claude-opus-5, and requests for it are forwarded upstream as claude-opus-5.
Notes:
- Alias names and targets accept letters, digits,
.,_, and-. Invalid or self-referential entries are dropped. - An alias whose target is not advertised upstream is skipped in the model list.
- Aliases compose with Claude minor-version translation, so
{"bigopus":"claude-opus-4-8"}resolves toclaude-opus-4.8. - Aliases do not change the context window Cursor displays.
| Cursor sends (Anthropic format) | Proxy converts to (OpenAI format) |
|---|---|
system as top-level field |
System message |
tool_use blocks in assistant messages |
tool_calls array |
tool_result blocks in user messages |
tool role messages |
input_schema on tools |
parameters (cleaned) |
tool_choice objects (auto/any/tool) |
OpenAI format (auto/required/function) |
stop_sequences |
stop |
thinking / cache_control blocks |
Stripped |
metadata / anthropic_version |
Stripped |
| Images in Claude requests | [Image Omitted] placeholder |
GPT-5.x max_tokens |
Converted to max_completion_tokens |
| GPT-5.x Responses API | Bridge built in (needs copilot-api support) |
- 💬 Chat & Reasoning: Full conversation context with all models
- 📋 Plan Mode: Works with tool calls and multi-turn conversations
- 🤖 Agent Mode: File editing, terminal, search, MCP tools
- 📂 File System:
Read,Write,StrReplace,Delete - 💻 Terminal:
Shell(run commands) - 🔍 Search:
Grep,Glob,SemanticSearch - 🔌 MCP Tools: External tools (Neon, Playwright, etc.)
- 🗜️ Max Mode: Auto-compact long conversations to stay within token limits (
--max) - 🤖 BYOK Subagents: Passes
SubagentandTasktools through; local GPT Responses calls remove the invalid cloud-onlycloud_base_branchargument - ⚙️ Live Settings: Agents can update max mode, API-key enforcement, and tunnel state without restarting
Use the local management endpoint to inspect or update the running proxy:
curl http://localhost:4142/api/settingsStart a Cloudflare tunnel immediately, remember the provider, and enable tunnel auto-start for future launches:
curl -X PATCH http://localhost:4142/api/settings \
-H "Content-Type: application/json" \
-d '{"maxMode":true,"tunnel":{"enabled":true,"autoStart":true,"provider":"cloudflared"}}'Stop the current tunnel without changing its saved auto-start preference:
curl -X PATCH http://localhost:4142/api/settings \
-H "Content-Type: application/json" \
-d '{"tunnel":{"enabled":false}}'Available fields:
| Field | Type | Behavior |
|---|---|---|
maxMode |
boolean | Enables/disables proactive 80% conversation compaction immediately and persists it |
requireApiKey |
boolean | Enables/disables API-key enforcement for /v1/* requests |
tunnel.enabled |
boolean | Starts or stops the tunnel immediately |
tunnel.autoStart |
boolean | Starts the saved tunnel provider on future launches |
tunnel.autoReconnect |
boolean | Relaunches the tunnel automatically if the process dies (default true) |
tunnel.provider |
string | cloudflared, ngrok, bore, or tailscale |
tunnel.options |
object | Per-provider settings, including the ones that pin a fixed URL (see Fixed tunnel URL) |
tunnel.authtoken |
string | Legacy alias for tunnel.options.authtoken |
modelAliases |
object | Maps a Cursor-facing alias to a real upstream model ID (see Context window and model aliases); does not change Cursor's displayed context window |
tunnel.options fields:
| Field | Applies to | Behavior |
|---|---|---|
cloudflaredMode |
cloudflared | quick (random URL) or named (fixed hostname) |
cloudflaredHostname |
cloudflared | Public hostname routed at the named tunnel |
cloudflaredToken |
cloudflared | Remote-managed tunnel token; stored, never returned |
cloudflaredName |
cloudflared | Locally-managed tunnel name |
ngrokDomain |
ngrok | Reserved static domain |
authtoken |
ngrok | ngrok authtoken; stored, never returned |
borePort |
bore | Requested remote port (best effort) |
tailscalePort |
tailscale | Public HTTPS port: 443, 8443, or 10000 |
Start a named Cloudflare tunnel on a fixed hostname and remember it for future launches:
curl -X PATCH http://localhost:4142/api/settings \
-H "Content-Type: application/json" \
-d '{"tunnel":{"enabled":true,"autoStart":true,"provider":"cloudflared","options":{"cloudflaredMode":"named","cloudflaredHostname":"copilot.example.com","cloudflaredToken":"eyJhIjoi..."}}}'Secrets read back as __saved__; send that placeholder to keep the stored value, or an empty string to clear it.
Settings mutations are serialized so concurrent agents cannot overwrite one another. Call management endpoints through localhost or another trusted connection; they control the live proxy and tunnel.
The dashboard and /api/* management endpoints control the local proxy. Keep them on a trusted connection and avoid sharing the dashboard URL. Model requests can be protected separately with API-key enforcement.
Manage API keys directly from the Endpoint tab in the dashboard:
- Toggle "Require API Key" to enable authentication
- Click "+ Create Key" to generate a new
cpk-xxxkey - Copy the key (shown only once!) and paste it into Cursor's API Key field
- Enable/disable or delete keys as needed
When enabled, all /v1/* requests must include Authorization: Bearer <your-key>.
| Usage Tab | Console Log Tab |
|---|---|
![]() |
![]() |
Access the dashboard at http://localhost:4142
Four tabs:
- Endpoint — Proxy URL, API keys, model list
- Usage — Request stats, token counts, per-model breakdown, recent requests
- Tunnel — Provider selection, live tunnel status, and auto-start preference
- Console Log — Real-time proxy logs with color-coded levels
| Feature | Status |
|---|---|
| Basic chat & tool calling | ✅ Works |
| Streaming | ✅ Works |
| Plan mode | ✅ Works |
| Agent mode | ✅ Works |
| All GPT-5.x models | ✅ Works |
| Max mode (long session compaction) | ✅ Works (--max flag) |
| Live settings API | ✅ Works (GET/PATCH /api/settings) |
| Tunnel auto-start | ✅ Works (--tunnel or persisted dashboard/API setting) |
| BYOK subagents using the parent model | ✅ Works |
| Separate Explore Subagent Model with custom/BYOK models | |
| Extended thinking (chain-of-thought) | ❌ Stripped |
Prompt caching (cache_control) |
❌ Stripped |
| Claude Vision | ❌ Not supported via Copilot |
| Tunnel URL changes on restart | ✅ Fixed via ngrok static domain, Tailscale Funnel, or a Cloudflare named tunnel — see Fixed tunnel URL |
"Model name is not valid" in Cursor:
Make sure you're using the cus- prefix (e.g., cus-gpt-5.4, not gpt-5.4).
Plan mode response cuts off:
Ensure idleTimeout: 255 is set in proxy-router.ts (already configured). Slow models like Opus need longer timeouts.
GPT-5.x returns "use /v1/responses": The proxy auto-routes these. Make sure you're running the latest version.
Explore subagent uses the same model as the parent:
This is a Cursor-side limitation observed with custom/BYOK models. Even when a different cus-* model is selected under Settings → Models → Task Models → Explore Subagent Model, Cursor may send the child request with the parent model ID. The proxy cannot recover the ignored selection because it is not included in the request.
"connection refused":
Ensure services are running: bun run start.ts or check http://localhost:4142.
GitHub auth never completes / "copilot-api failed to start":
On first run, the launcher runs copilot-api auth login --provider copilot with
terminal access, opens GitHub's device-login page, and waits for authentication
to finish before starting copilot-api. This avoids TTY failures from the
upstream provider-selection prompt. If authentication still fails (firewall,
corporate proxy, SSO redirect, etc.), run it directly and then restart:
# Global installation
copilot-for-cursor auth
# npx
npx copilot-for-cursor@latest auth
# Then start the stack normally with either command:
copilot-for-cursor
npx copilot-for-cursor@latestThe token is stored at:
- Windows:
%USERPROFILE%\.local\share\copilot-api\github_token - macOS / Linux:
~/.local/share/copilot-api/github_token
To reset auth, delete that file (and optionally revoke the device at https://github.com/settings/apps/authorizations).
Max mode not compacting:
Compaction only triggers when estimated tokens exceed 80% of the model's limit and there are at least 15 messages. Check the console log for 🗜️ Max mode messages.
⚠️ DISCLAIMER: This project is unofficial and for educational purposes only. It interacts with undocumented internal APIs of GitHub Copilot and Cursor. Use at your own risk. The authors are not affiliated with GitHub, Microsoft, or Anysphere (Cursor). Please use your API credits responsibly and in accordance with the provider's Terms of Service.




