# ============================================================================= # Cursor OpenAI Gateway - environment configuration # Copy this file to `.env` and fill in the values you need. # NEVER commit your real `.env` file - it is already listed in .gitignore. # # None of this is actually required to get started: run `npm run dev` (or # `npm start` after `npm run build`) with no `.env` at all and the gateway # boots into a first-run setup wizard in your browser instead. Everything # below is for headless deployments (Docker, CI, servers with no browser) or # advanced users who'd rather configure via environment variables/CLI than # the web UI. Settings changed from the dashboard are persisted to # `.cursor-gateway/settings.json` and take precedence over these on boot. # ============================================================================= # --- Cursor authentication ---------------------------------------------------- # Optional. Get a user API key from https://cursor.com/dashboard/api # or a team service-account key from Team Settings -> Service accounts. If # left empty (and CURSOR_KEY_MODE=server), the gateway starts anyway and # serves a setup wizard at http://localhost:8787 until this is configured # (via the wizard, which writes it to .cursor-gateway/settings.json, or here). CURSOR_API_KEY= # "server" (default) - always use CURSOR_API_KEY above for every request. # "passthrough" - use the client's `Authorization: Bearer ` value as the # Cursor API key for that request instead. Lets one running # gateway serve multiple Cursor accounts, at the cost of not # being able to gate access with AUTH_KEY (see below). CURSOR_KEY_MODE=server # --- Gateway HTTP server ------------------------------------------------------- # If this port is already in use when the gateway starts, it automatically # tries the next few ports instead of failing - check the startup log or the # dashboard for the port actually in use if that happens. PORT=8787 # Network interface to bind. 0.0.0.0 (default) accepts connections from other # devices on your network - the startup log prints the exact base URLs to use, # and the admin dashboard stays local-only regardless (see ADMIN_ALLOW_REMOTE). # Set 127.0.0.1 to make the gateway reachable ONLY from this machine. # When bound to 0.0.0.0 in server mode, set AUTH_KEY (below) or anyone who can # reach the port can use your Cursor plan. See the README "Exposing it on your # network or the internet" section. HOST=0.0.0.0 NODE_ENV=production # Optional bearer token clients must send to use this gateway (Authorization: # Bearer ). Leave empty to allow unauthenticated local access. # Ignored when CURSOR_KEY_MODE=passthrough (the client's bearer token is the # Cursor key in that mode, so there is nothing left to gate with). AUTH_KEY= # Comma-separated list of allowed CORS origins, or * for any origin. CORS_ORIGIN=* # --- Cursor agent runtime ------------------------------------------------------- # "local" (default) runs agents on this machine against a scratch working # directory. "cloud" runs on a Cursor-hosted VM (higher latency per request, # better isolation from this host). CURSOR_RUNTIME=local # Root directory for local agents' isolated per-session working directories. # Defaults to /.cursor-gateway/workspaces if left empty. Each session # gets its own subfolder so agents cannot see each other's files. CURSOR_WORKDIR= # "agent" (default) is Cursor's standard coding-agent conversation mode. # "plan" restricts the agent to planning without applying changes. CURSOR_AGENT_MODE=agent # Model id passed when a client omits "model" or sends "auto"/"gpt-3.5-turbo"-style # placeholders that don't map to a real Cursor model. Run `npm run smoke` or hit # GET /v1/models to see the live catalog for your account. DEFAULT_MODEL=composer-2.5 # --- Feature toggles ----------------------------------------------------------- # Stream Cursor's chain-of-thought/"thinking" text as OpenAI-style # `reasoning_content` delta fields (supported by many OpenAI-compatible clients # for reasoning models). Set to false to omit thinking output entirely. CURSOR_INCLUDE_THINKING=true # Reuse a persistent Cursor agent across turns of the same conversation instead # of replaying full history on every request. CURSOR_ENABLE_SESSIONS=true # When sessions are enabled, automatically detect follow-up requests whose # messages[] extend a previously seen conversation (by hash) and reuse that # session's agent, even without an explicit session id from the client. CURSOR_AUTO_SESSION=true # Milliseconds a cached session may sit idle before it is disposed. SESSION_TTL_MS=1800000 # Maximum number of live agent sessions to keep cached at once (LRU eviction). MAX_CACHED_AGENTS=50 # Maximum number of Cursor agent runs this process will execute concurrently. # Extra requests queue (FIFO) until a slot frees up. MAX_CONCURRENT_RUNS=8 # Hard timeout for a single chat completion request end-to-end. REQUEST_TIMEOUT_MS=300000 # Bridge OpenAI-style `tools`/function-calling through Cursor's native # custom-tool mechanism. See README "Tool calling" for the full behavior. ENABLE_TOOL_BRIDGE=true # How the tool bridge maps an OpenAI tool loop onto Cursor's inline-tool SDK: # "hold" (default) - keep ONE Cursor run alive across the whole tool loop # by parking its tool callbacks until the client returns # results. A multi-step tool conversation is then ONE # metered Cursor run (like the native Cursor app), and # parallel tool calls in a turn are all captured. # "cancel" - legacy: cancel the run on the first tool call and let # the client's follow-up start a fresh run (N runs per # loop; only the first tool call per turn is observed). TOOL_BRIDGE_MODE=hold # Hold mode only: how long (ms) a held run may wait for the client's tool # result before it's torn down, freeing the agent and its concurrency slot. # Default 900000 (15 min). Raise it if your tools legitimately take longer. TOOL_RESULT_TIMEOUT_MS=900000 # Max JSON request body size in megabytes (1-1024, default 25 - in line with # major provider APIs; Anthropic caps requests at 32 MB). Oversized bodies get # a proper HTTP 413, which vision-heavy clients (e.g. Hermes) treat as the # signal to compress their history and continue. Deliberately bounded: bodies # are buffered and parsed in RAM, so an unlimited value would let one request # OOM the process. JSON_BODY_LIMIT_MB=25 # How GET /v1/models is projected. "canonical" (default) lists one id per # Cursor model plus useful variants (including composer-2.5-fast). "all" # also lists aliases (previous behavior). "deduplicated" is accepted as an # alias of "canonical". Aliases still resolve on chat/responses requests # either way. Bare composer-2.5 is always the non-Fast selection. MODEL_LIST_MODE=canonical # Optional comma-separated allowlist of model ids to expose on GET /v1/models. # Empty means no list filter. Does not block requesting other catalog ids. # ALLOWED_MODELS=composer-2.5,gpt-5.4-mini,claude-sonnet-5 ALLOWED_MODELS= # --- Rate limiting --------------------------------------------------------------- RATE_LIMIT_WINDOW_MS=60000 RATE_LIMIT_MAX=120 # --- Logging ----------------------------------------------------------------- # fatal | error | warn | info | debug | trace | silent LOG_LEVEL=info # Pretty-print logs for local development. Leave false in production so logs # stay newline-delimited JSON for log aggregators. LOG_PRETTY=false # --- Admin dashboard ----------------------------------------------------------- # Automatically opens the setup wizard/dashboard in your default browser on # startup. Set to false for headless servers, CI, and Docker (there's no # browser to open in a container - the provided Dockerfile already does this). AUTO_OPEN_BROWSER=true # The admin dashboard and its API (/api/admin/*) only accept requests from # this machine (127.0.0.1/::1) by default, regardless of AUTH_KEY - so binding # HOST=0.0.0.0 to expose the OpenAI endpoints on your LAN doesn't also expose # configuration/credentials to that same network. Set true only if you # understand the risk and need to configure this gateway remotely. ADMIN_ALLOW_REMOTE=false