deepseek
by iiSwxft
README.md
# DS IDE to Web
Let any AI tool talk to the DeepSeek chat you are already logged in to in your browser.
A small local app plus a browser extension turn your DeepSeek web session into two things other tools understand:
- an **OpenAI-compatible API** (`http://127.0.0.1:8787/v1`) for Cursor, Continue, Open WebUI, LangChain, scripts, and anything else that speaks the OpenAI format;
- an **MCP server** that gives any MCP client tools to ask DeepSeek, continue a chat, edit a message, and manage your chats.
```
any AI / IDE / script
| OpenAI API (http://127.0.0.1:8787/v1) MCP over stdio (mcp-stdio.mjs)
v |
bridge app (daemon.mjs) <------- local HTTP -------------+
| WebSocket on 127.0.0.1, key + extension-origin check
v
browser extension -> your chat.deepseek.com tab (your login, your proof-of-work)
```
> **Read this first.** The bridge drives the consumer web app with your own signed-in session. That is very likely outside DeepSeek's terms of service, and your account could be limited. If you need something dependable, use the official DeepSeek API. Treat this as a tool for personal experiments. DeepSeek's private web routes can also change without notice and break it.
## Quick start
You need Node.js 20 or newer and Chrome, Edge, or Firefox 128+.
1. **Install and start the bridge app** (keep it running while you use it):
```bash
npm run setup
npm start
```
2. **Build the extension:**
```bash
npm run build
```
This writes `bridge/build/chrome` and `bridge/build/firefox`.
3. **Load it in your browser.**
- **Chrome or Edge:** open `chrome://extensions`, turn on *Developer mode*, choose *Load unpacked*, and select the `bridge/build/chrome` folder itself (the one that contains `manifest.json`).
- **Firefox:** open `about:debugging#/runtime/this-firefox`, choose *Load Temporary Add-on*, and pick `bridge/build/firefox/manifest.json`. Then open `about:addons`, select DeepSeek Bridge, and under *Permissions* allow access to `chat.deepseek.com`. Firefox removes temporary add-ons when it restarts.
4. **Open https://chat.deepseek.com and sign in.** If the tab was already open when you installed the extension, refresh it. A small status pill appears in the bottom right. The toolbar popup shows the connection and the details to copy.
Check that everything is connected:
```bash
curl -H "Authorization: Bearer deepseek-bridge" http://127.0.0.1:8787/api/status
```
The extension and the app share a preset key, `deepseek-bridge`, so there is nothing to configure.
## For AI assistants: setting this up and using it
If you are an AI assistant helping a user with this repository, follow this.
**Set up** (you can do steps 1 to 3; the user must do step 4, because only they can install a browser extension and sign in):
1. Check `node --version` is 20 or newer.
2. Run `npm run setup`, then `npm run build`.
3. Start the bridge app in the background with `npm start` and leave it running. A second browser needs its own port: `DS_BRIDGE_PORT=8788 npm start`. See "Using two browsers at once".
4. Ask the user to load `bridge/build/chrome` (Chrome or Edge) or `bridge/build/firefox/manifest.json` (Firefox) as described in the quick start, sign in at https://chat.deepseek.com, and refresh that tab.
5. Verify with `curl -H "Authorization: Bearer deepseek-bridge" http://127.0.0.1:8787/api/status`. You need `"extensionConnected": true` and `"loggedIn": true`. If `loggedIn` is missing, read the `error` field.
The app has to be running whenever the bridge is used. The extension cannot start it, so if the popup says **Bridge app not running**, start it with `npm start`.
**Use it** through the MCP tools (or the OpenAI-compatible endpoint):
- Start a new chat by calling `deepseek_ask` without `conversation_id` or `chat`. Pass `new_chat_title`, and for AI-to-AI work pass `preset: "collaborator"`. Keep the `conversation_id` from the reply and pass it on every follow-up. Send only the new message each time, because DeepSeek remembers the chat.
- To change a message you already sent, use `deepseek_edit_message` instead of sending a correction.
- Do not call `deepseek_delete_chat` unless the user explicitly asked to delete those specific chats. Deletion is permanent.
- Stop looping when you have what you need. Do not run an open-ended back and forth.
**Errors** the bridge returns, and what to do:
| Code | Meaning | What to do |
| --- | --- | --- |
| `extension_not_connected` (503) | No browser extension is connected. | Ask the user to open chat.deepseek.com with the extension installed, and check the bridge app is running on the right port. |
| `no_deepseek_tab` | The extension is connected but its page script is not running on a DeepSeek tab. | Ask the user to refresh the chat.deepseek.com tab. |
| `not_logged_in` | The DeepSeek tab is signed out. | Ask the user to sign in, then retry. |
| `unauthorized` (401) | Wrong or missing key. | Use `Authorization: Bearer deepseek-bridge`, or the custom key if one is set. |
| `forbidden_origin`, `forbidden_host` (403) | The request came from a web page or with a non-local host. | Call the bridge from a local program, not from a browser page. |
| `chat_not_found` (404), `unknown_conversation` (404) | No chat matches that title or conversation id. | List chats with `deepseek_list_chats` and use an id. |
| `chat_ambiguous` (409) | The title matches several chats. | Use the id from the candidates in the message. |
| `message_not_found` (404) | That message is not one of the user's messages in the chat. | Read the chat and use an id from the editable list in the message. |
| `confirmation_required` (400) | A delete was sent without `confirm: true`. | Only add it after the user has explicitly confirmed. |
| `unknown_model`, `invalid_request` (400) | A bad parameter. | Fix the request. The message says which one. |
| `deepseek_refused`, `deepseek_http_error` (502) | DeepSeek rejected the call, or its web routes changed. | Show the user the message. Do not retry in a loop. |
| `extension_disconnected`, `timeout` (504), `cancelled` | The connection dropped, took too long, or the caller cancelled. | Retry once. If it repeats, check the status endpoint. |
## The extension popup
| Tab | What it does |
| --- | --- |
| **Connect** | A bridge drawing that shows the connection at a glance: green and carrying traffic when connected, amber while connecting, red with a broken deck when the app is not running, faint when no DeepSeek tab is open. Below it: the base URL and API key with copy buttons. |
| **Prompt** | A system prompt that is sent as the first message of every new chat. |
| **Settings** | A custom key and the port. |
## Use it from other tools
### OpenAI-compatible
Point any OpenAI client at `http://127.0.0.1:8787/v1` with the API key `deepseek-bridge`.
| Model id | What you get |
| --- | --- |
| `deepseek-web` | The default model, thinking off. |
| `deepseek-web-thinking` | The default model, thinking on. The reasoning comes back as `reasoning_content`. |
| `deepseek-web-expert` | The expert model, thinking on. |
Extra request fields: `conversation_id` (continue a chat; every response includes one), `chat` (continue an existing chat by id or title), `new_chat_title`, `ds_search: true` for web search, `ds_thinking: true|false`, `system`, and `preset`.
Function and tool calling is not supported. When you start a new chat from a multi-message history, the messages are sent as one prompt. When you continue a chat, only your newest message is sent, because DeepSeek already has the history.
### MCP
Add the bridge as a stdio server in your MCP client, using the absolute path to this folder:
```json
{
"mcpServers": {
"deepseek": {
"command": "node",
"args": ["C:/path/to/DS-ide-to-web/bridge/server/mcp-stdio.mjs"]
}
}
}
```
| Tool | What it does |
| --- | --- |
| `deepseek_ask` | Send a prompt. Starts a new chat by default. `chat` (id or title) or `conversation_id` continues one. Options: `new_chat_title`, `system`, `preset`, `model`, `thinking`, `search`, `include_reasoning`. |
| `deepseek_edit_message` | Replace one of your earlier messages, like the pencil Edit button on the site, and get DeepSeek's answer to the new text. Defaults to your latest message. Later messages leave the active conversation. |
| `deepseek_list_chats` | List your chats, optionally filtered by title. |
| `deepseek_read_chat` | Read a chat's messages by id or title. After an edit, only the live conversation is shown. |
| `deepseek_rename_chat` | Rename a chat. |
| `deepseek_pin_chat` | Pin or unpin a chat. |
| `deepseek_delete_chat` | Permanently delete chats. Needs `confirm: true`, handles at most 20, and checks every target before deleting any. |
| `deepseek_status` | Report whether the extension is connected and signed in. |
The same actions are plain HTTP on the bridge: `POST /api/ask`, `POST /api/edit`, `GET /api/chats`, `GET /api/chat?chat=`, `POST /api/chat/rename`, `POST /api/chat/pin`, and `POST /api/chat/delete`. Chat titles match exactly first, then by unique partial match. An ambiguous title is refused with the candidates listed.
## System prompts and AI-to-AI work
DeepSeek's web chat has no system role, so instructions for a **new** chat are sent inside its first message, clearly labelled. They are never repeated when you continue the chat.
Where the instructions come from, most specific first:
1. `system` text in the request.
2. A `preset`: `collaborator` (written for AI-to-AI work), `reviewer`, or `concise`.
3. The **Prompt** tab in the extension popup.
4. `DS_BRIDGE_SYSTEM`, or the text in `~/.ds-ide-to-web/system.txt`.
DeepSeek titles a new chat from its first message, which would include these instructions, so pass `new_chat_title` when you set a system prompt.
**Making your AI talk back and forth with DeepSeek.** The AI you are using drives the loop. It calls `deepseek_ask`, reads the reply, does its own work with it, then calls `deepseek_ask` again with the `conversation_id` from the previous reply. The MCP server sends these instructions to the client, so most assistants pick the pattern up on their own. For example:
> Start a chat with DeepSeek using the collaborator preset. Have it draft the design, review what it says, push back on anything weak, and iterate until it is solid. Stop after at most five rounds.
The bridge does not run the loop itself, because the other side of the conversation has to be an AI you choose.
## Using two browsers at once
One bridge app talks to one browser at a time: a newer extension connection replaces the older one, so two browsers on the same port keep knocking each other off. To use Chrome and Firefox together, give each its own port and run one bridge app per port:
```bash
npm start # Chrome, port 8787 (the default)
DS_BRIDGE_PORT=8788 npm start # Firefox, port 8788
```
On Windows PowerShell, set the variable first: `$env:DS_BRIDGE_PORT = '8788'; npm start`.
In the Firefox extension, open the popup, go to **Settings**, set the port to `8788`, and save. Then use `http://127.0.0.1:8788/v1` as the base URL for tools that should talk to Firefox, and set `DS_BRIDGE_PORT=8788` in the environment of any MCP server entry that should use it.
## Configuration
| Setting | How | Default |
| --- | --- | --- |
| Port | `DS_BRIDGE_PORT` (and the port in the popup's Settings tab) | `8787` |
| Key | `DS_BRIDGE_TOKEN`, or put a value in `~/.ds-ide-to-web/token`, then enter the same value in the popup's Settings tab | `deepseek-bridge` |
| Default system prompt | the Prompt tab, `DS_BRIDGE_SYSTEM`, or `~/.ds-ide-to-web/system.txt` | none |
| Config folder | `DS_BRIDGE_HOME` | `~/.ds-ide-to-web` |
## Security
- Everything listens on `127.0.0.1` only. Requests whose `Host` header is not localhost are refused, which blocks DNS rebinding.
- HTTP needs `Authorization: Bearer <key>` and refuses any request that carries a browser `Origin` header, so web pages cannot call it.
- The extension connection only accepts `chrome-extension://` and `moz-extension://` origins, plus the key.
- The extension can call a fixed list of DeepSeek chat routes only: list, history, rename, delete, pin, and stop. It cannot reach account settings, logout, or "delete all history".
- The default key is public on purpose, so any program on your own computer can use the bridge, and therefore your DeepSeek session, while the app runs. If that matters, set a custom key and stop the app when you are not using it.
## Troubleshooting
| What you see | What to do |
| --- | --- |
| Popup says **Bridge app not running** | Start it with `npm start`. The extension reconnects by itself. |
| Popup says **Connected** but requests say the extension is not running on any DeepSeek tab | Refresh the chat.deepseek.com tab. Browsers only add the extension to tabs opened after it was installed. |
| Chrome says *Manifest file is missing or unreadable* | You selected the `deepseek` subfolder. Select `bridge/build/chrome` itself. |
| Firefox connects, then requests fail | Check `about:addons`, DeepSeek Bridge, Permissions: access to `chat.deepseek.com` must be on. It can reset when you reload a temporary add-on. |
| Two browsers keep disconnecting each other | They share one port. Give each browser its own port, see "Using two browsers at once". |
| Requests say you are not logged in | Sign in on the DeepSeek tab, then retry. |
| A new chat does not show in the DeepSeek sidebar | Refresh the page. |
## Project layout
```
bridge/
server/ the bridge app: HTTP + OpenAI API, MCP server, chat tools (Node, ESM)
extension/ the browser extension: popup, background worker, page script
src/vendor/ DeepSeek web client code reused from deepseek-pp (see NOTICE)
test/ tests for that client code
icons/ generated icons
wasm/ DeepSeek's proof-of-work module
tools/ make-icons.mjs, renders the extension icons
build.mjs bundles the extension for Chrome and Firefox
```
## Development
```bash
npm run build # build the extension for both browsers
npm run typecheck # TypeScript check
npm run test # extension tests (vitest) and server tests (node:test)
```
The server tests use a fake extension over a real WebSocket, so they need no browser.
## License and credits
Apache-2.0, see [LICENSE](LICENSE). The DeepSeek web client code under `bridge/extension/src/vendor` comes from [zhu1090093659/deepseek-pp](https://github.com/zhu1090093659/deepseek-pp) and was translated and modified for this project. Details are in [NOTICE](NOTICE).
This server cannot be deployed
Maintenance
ActivityMaintained
ResponsivenessNo recent community issues