A local proxy that connects Cursor to DeepSeek thinking models (deepseek-v4-pro / deepseek-v4-flash) via any OpenAI-compatible API. Designed for opencode subscriptions, compatible with any DeepSeek API endpoint.
简体中文 · Why this exists · Features · Getting started · Usage · Configuration · Troubleshooting
DeepSeek thinking-mode tool calls require the complete multi-round reasoning_content chain to be sent back in later requests. Cursor omits that field, causing a 400 error:
Provider returned error: reasoning_content must be passed back to the API.
This proxy sits between Cursor and the upstream API, storing and restoring reasoning_content so DeepSeek thinking models work correctly in Cursor.
- reasoning_content injection — Restores
reasoning_contentthat Cursor omits in tool-call requests, preventing DeepSeek's "reasoning_content must be passed back" error - Thinking display — Mirrors reasoning tokens into Cursor-visible collapsible Markdown blocks (
<details><summary>Thinking</summary>...) - ngrok tunnel — Auto-starts a public HTTPS tunnel so Cursor can reach the proxy (Cursor blocks non-public API endpoints)
- Conversation isolation — Scopes reasoning caches per conversation via SHA-256 hashes, preventing tool-call ID collisions across concurrent threads
- Context caching compatible — Never injects synthetic thread IDs or timestamps, preserving DeepSeek's KV cache hit rates
- Portable cache keys — Backfills cross-scope portable aliases so reasoning can be recovered even when conversation scope changes
- Auto-recovery — Automatically recovers from history gaps when reasoning content is missing from cache, with a system notice explaining the truncated context
- Legacy API compatibility — Converts
functions/function_calltotools/tool_choice, normalizesreasoning_effort, strips Cursor thinking blocks from upstream payloads - SQLite cache — Persists reasoning content locally with configurable TTL and row limits
- Interactive setup wizard — First-run guided configuration for API provider, model, and tunnel settings
Cursor sends requests to the proxy instead of the upstream API. The proxy:
- Receives a
/v1/chat/completionsrequest from Cursor - Normalizes the payload — strips unsupported fields, converts legacy function calls, injects cached
reasoning_contentinto tool-call messages that Cursor omitted it from - Forwards the transformed request to the upstream API
- Rewrites the response — for streaming responses, accumulates SSE chunks, mirrors
reasoning_contentinto visible Markdown blocks, and caches reasoning content to SQLite for future requests - Returns the rewritten response to Cursor
The proxy uses SHA-256 hashes of the conversation context as cache keys, ensuring reasoning content is correctly matched across concurrent conversations with overlapping tool-call IDs.
- Node.js 20+
Cursor blocks non-public API URLs such as localhost, so the proxy needs a public HTTPS URL. ngrok can expose the local proxy without opening router ports.
- Create a free account at ngrok.com
- Find your authtoken on the ngrok dashboard
- Install and authenticate ngrok once:
brew install ngrok
ngrok config add-authtoken <your-token>Note
You do not need to start ngrok manually — the proxy will start it automatically when you run dsl start.
Tip
You can also use Cloudflare Tunnel as an alternative. If your tool allows localhost API endpoints, skip ngrok with the --no-ngrok flag.
npm install -g deepseek-lane
dsl startOn first run, an interactive setup wizard will guide you through configuration: API provider, default model, port, thinking mode, reasoning effort, and ngrok settings. The config is saved to ~/.deepseek-lane/config.yaml.
When ngrok is enabled, the proxy prints the public URL on startup:
✓ Model: deepseek-v4-pro (thinking, max)
▸ Local: http://127.0.0.1:9000/v1
▸ Public: https://your-tunnel.ngrok-free.dev/v1
- Open Cursor Settings → Models → Add Custom Model
- Add a model with the name
deepseek-v4-proordeepseek-v4-flash - Set the API key to your opencode subscription key (or DeepSeek API key)
- Set the base URL to the ngrok public URL with
/v1suffix:https://your-tunnel.ngrok-free.dev/v1 - Toggle the custom API with
Cmd+Shift+0(macOS) orCtrl+Shift+0
Select deepseek-v4-pro or deepseek-v4-flash in the model picker and start chatting.
# Start with interactive setup wizard
dsl start
# Stop the proxy
dsl stop
# Restart in background
dsl restart
# Check if running
dsl status
# View recent logs
dsl log# Use a different port
dsl start --port 9001
# Point to a custom API endpoint
dsl start --base-url https://api.deepseek.com/v1
# Verbose logging
dsl start --verbose
# Run without ngrok (local-only)
dsl start --no-ngrok
# Clear the reasoning cache
dsl start --clear-reasoning-cache
# Skip interactive wizard
dsl start --no-interactive| Option | Description |
|---|---|
--config <path> |
Config file path |
--host <host> |
Bind host (default: 127.0.0.1) |
--port <port> |
Bind port (default: 9000) |
--model <model> |
Upstream model name |
--base-url <url> |
Upstream API base URL |
--thinking <mode> |
Thinking mode: enabled / disabled |
--reasoning-effort <level> |
low / medium / high / max / xhigh |
--request-timeout <s> |
Upstream request timeout in seconds |
--missing-reasoning-strategy <s> |
recover (default) or reject |
--clear-reasoning-cache |
Clear the local reasoning cache and exit |
--ngrok / --no-ngrok |
Enable/disable ngrok tunnel |
--verbose / --no-verbose |
Enable/disable verbose logging |
--display-reasoning / --no-display-reasoning |
Show reasoning in visible content |
--collapsible-reasoning / --no-collapsible-reasoning |
Use collapsible Markdown for reasoning |
Auto-generated at ~/.deepseek-lane/config.yaml on first run:
base_url: https://opencode.ai/zen/go/v1
model: deepseek-v4-pro
thinking: enabled
reasoning_effort: max
display_reasoning: true
collapsible_reasoning: true
host: 127.0.0.1
port: 9000
ngrok: true
verbose: false
request_timeout: 300
max_request_body_bytes: 20971520
missing_reasoning_strategy: recover
reasoning_cache_max_age_seconds: 2592000
reasoning_cache_max_rows: 100000CLI flags override config file values.
| Path | Description |
|---|---|
~/.deepseek-lane/config.yaml |
Configuration file |
~/.deepseek-lane/reasoning_content.sqlite3 |
Reasoning content cache |
~/.deepseek-lane/pid |
Process ID file |
~/.deepseek-lane/log |
Log output |
Main endpoint. Accepts the standard OpenAI chat completions format. Requires Authorization: Bearer <key> header.
Returns available model list.
Health check — returns {"ok": true}.
deepseek-lane/
├── packages/
│ ├── core/ @deepseek-lane/core
│ │ ├── src/config.ts YAML config loading, CLI arg merging
│ │ ├── src/logging.ts Colored terminal output
│ │ ├── src/reasoning-store.ts SQLite reasoning cache
│ │ ├── src/server.ts Hono HTTP server, request routing, streaming
│ │ ├── src/streaming.ts SSE stream accumulator, display adapter
│ │ ├── src/transform.ts Request normalization, recovery logic
│ │ ├── src/tunnel.ts ngrok subprocess management
│ │ ├── src/types.ts TypeScript type definitions
│ │ └── src/__tests__/ Test suite
│ └── cli/ deepseek-lane
│ ├── src/cli.ts Commander-based CLI entry point
│ ├── src/daemon.ts PID management, background process
│ └── src/commands/ start, stop, restart, status, log
├── vite.config.ts Vite+ configuration
├── tsconfig.base.json Shared TypeScript config
└── package.json Workspace root
Proxy won't start, port in use
lsof -ti:9000 | xargs killngrok errors
Ensure ngrok is installed and authenticated:
ngrok config checkThe proxy looks for the ngrok API at http://127.0.0.1:4040/api.
"reasoning_content must be passed back"
This error means the reasoning cache was cleared or missed. The default recover strategy handles this automatically. If the issue persists, clear the cache:
dsl start --clear-reasoning-cacheVerbose debugging
Run with full request/response logging:
dsl start --verbose --no-ngrokgit clone <your-repo-url>
cd deepseek-lane
vp install| Command | Description |
|---|---|
vp dev |
Start with auto-reload and --no-ngrok |
vp test |
Run test suite |
vp check |
Lint, format, and type check |
vp fmt |
Format all source files |
vp pack |
Build the core library |
Inspired by yxlao/deepseek-cursor-proxy.