A compatibility proxy that connects Cursor to DeepSeek thinking models (deepseek-v4-pro and deepseek-v4-flash) by properly handling the reasoning_content field for DeepSeek tool-call reasoning API requests.
This proxy can also help other applications and coding agents beyond Cursor that run into the same missing reasoning_content issue with DeepSeek's thinking-mode API. Just point their API base URL at the proxy.
- ✅ Injects
reasoning_contentinto outgoing tool-call requests since Cursor does not include the field, restoring previously cached reasoning from regular and streamed DeepSeek responses. See DeepSeek docs for more details. - ✅ Displays DeepSeek's thinking tokens in Cursor by forwarding them into Cursor-visible
<think>...</think>blocks. In BYOK (bring your own key) mode, Cursor renders these thinking blocks as plain text instead of a native collapsible thinking view. You can disable thinking token display with--no-display-reasoningor settingdisplay_reasoning: falsein the config file. - ✅ Starts an ngrok tunnel so Cursor can reach the local proxy through a public HTTPS URL.
- ✅ Provides other compatibility fixes to make DeepSeek models run well in Cursor.
This repository fixes the following Cursor + DeepSeek tool-call error with thinking mode enabled:
⚠️ Connection Error
Provider returned error:
{
"error": {
"message": "The reasoning_content in the thinking mode must be passed back to the API.",
"type": "invalid_request_error",
"param": null,
"code": "invalid_request_error"
}
}Cursor blocks non-public API URLs such as localhost, so the proxy needs a public HTTPS URL. ngrok can expose the local proxy to Cursor without opening router ports. Alternatively, you may use Cloudflare Tunnel. Create an ngrok account and visit ngrok's dashboard. You will find the authtoken and public URL there.
If you're using this proxy with another application that allows localhost API endpoints, you can skip this step entirely by setting ngrok: false in ~/.deepseek-cursor-proxy/config.yaml, or by starting the proxy with --no-ngrok.
Then, install and authenticate ngrok once:
brew install ngrok
ngrok config add-authtoken <your-ngrok-token>In Cursor, add the DeepSeek custom model and point it at this proxy:
- Model:
deepseek-v4-pro - API Key: your DeepSeek API key
- Base URL: your ngrok HTTPS URL with the
/v1API version path
The proxy respects the DeepSeek model name Cursor sends, such as deepseek-v4-pro or deepseek-v4-flash. The model field in config.yaml is used as a fallback only when a request does not include a model.
For example, if ngrok dashboard shows https://example.ngrok-free.dev, use:
https://example.ngrok-free.dev/v1
Note: you can toggle the custom API on and off with:
- macOS:
Cmd+Shift+0 - Windows/Linux:
Ctrl+Shift+0
Run with UV
# Install uv if you don't have it
curl -LsSf https://astral.sh/uv/install.sh | sh
# Install and start
# uv installs the program in .venv/ under the repo local folder
git clone https://github.com/yxlao/deepseek-cursor-proxy.git
cd deepseek-cursor-proxy
uv run deepseek-cursor-proxyRun with Conda
# Install conda if you don't have it
# Follow: https://www.anaconda.com/docs/getting-started/miniconda/install/overview
# Install
conda create -n dcp python=3.10 -y
conda activate dcp
git clone https://github.com/yxlao/deepseek-cursor-proxy.git
cd deepseek-cursor-proxy
pip install -e .
# Start
deepseek-cursor-proxyWhen ngrok is enabled, deepseek-cursor-proxy will print the ngrok public URL on start. If it differs from the one in Cursor, update it in Cursor's Base URL field.
On the first run, deepseek-cursor-proxy will create:
~/.deepseek-cursor-proxy/config.yaml: the configuration file~/.deepseek-cursor-proxy/reasoning_content.sqlite3: the reasoning content cache
Persistent settings live in ~/.deepseek-cursor-proxy/config.yaml. You can also override the config with command-line flags, for example:
# Hide thinking tokens displaying in Cursor UI
deepseek-cursor-proxy --no-display-reasoning
# Show full incoming and outgoing requests
deepseek-cursor-proxy --verbose
# Run without ngrok (run on localhost directly)
deepseek-cursor-proxy --no-ngrok
# Use a different local port
deepseek-cursor-proxy --port 9000Select deepseek-v4-pro in Cursor and use chat or agent mode as usual.
- Core fix: DeepSeek's thinking mode requires
reasoning_contentfrom assistant tool-call messages to be passed back in subsequent requests, but Cursor omits this field, causing a 400 error. The proxy (Cursor → ngrok → proxy → DeepSeek API) storesreasoning_contentfrom every DeepSeek response in a local SQLite cache, keyed by message signature, tool-call ID, and tool-call function signature, and patches outgoing requests with missingreasoning_contentbefore they reach DeepSeek. On a cold cache (proxy restart, model switch), it logs and drops unrecoverable history, continues from the latest user request, and prefixes the next Cursor response with a notice. - Multi-conversation isolation: To avoid collisions across concurrent conversations, the proxy scopes cache keys by a SHA-256 hash of the canonical conversation prefix (roles, content, and tool calls, excluding
reasoning_content) plus the upstream model, configuration, and an API-key hash. Different threads get different scopes, so reused tool-call IDs do not collide. Byte-identical cloned histories produce identical scopes. - Context caching compatibility: The proxy preserves compatibility by never injecting synthetic thread IDs, timestamps, or cache-control messages. It restores
reasoning_contentas the exact original string, so repeated prefixes remain intact for DeepSeek context cache. Cache hit rates are logged in the terminal output. - Additional compatibility fixes: Beyond reasoning repair, the proxy converts legacy
functions/function_callfields totools/tool_choice, preserves required and named tool-choice semantics, normalizesreasoning_effortaliases, strips mirrored<think>blocks from assistant content, flattens multi-part content arrays to plain text, and mirrorsreasoning_contentinto Cursor-visible<think>...</think>blocks.
Run unit tests:
uv run python -m unittest discover -s testsRun pre-commit hooks (code formatting and linting):
uv sync --dev
uv run pre-commit run --all-filesRun with verbose output:
deepseek-cursor-proxy --verboseRun without ngrok for local curl testing:
deepseek-cursor-proxy --no-ngrok --port 9000 --verboseUse another config file:
deepseek-cursor-proxy --config ./dev.config.yamlClear the local reasoning cache:
deepseek-cursor-proxy --clear-reasoning-cache



