Skip to main content
Glama
README.md
# deepseek-agent

Use DeepSeek's web chat as a **code-review tool from any AI coding harness** (omp, Claude Code, Codex, Gemini CLI, opencode, …) over [MCP](https://modelcontextprotocol.io).

DeepSeek publishes no public chat API, so this speaks the browser protocol directly: it solves the site's proof-of-work challenge in WASM and parses the completion stream. Point your harness at it and you get `deepseek_review`, `deepseek_ask`, and `deepseek_status` as tools.

## Why

Give a second, independent opinion on a diff. A useful property of this one: it reads the code before judging, and it **declines to report what it cannot prove**. On a planted contract change with no callers, it explicitly said so instead of padding the report.

## Install

```bash
git clone https://github.com/khanshifaul/deepseek-agent.git
cd deepseek-agent
pip install -r requirements.txt
```

You also need the site's WASM proof-of-work solver, which is not redistributed here:

```bash
curl -o sha3_wasm_bg.wasm \
  https://fe-static.deepseek.com/chat/static/sha3_wasm_bg.7b9ca65ddd.wasm
```

Place it next to `deepseek-agent.py` (or point `$DEEPSEEK_WASM` at it).

## Credentials

DeepSeek has no API key, so this authenticates the way the web client does. You need a **logged-in `chat.deepseek.com` browser session** and two values from it:

| Value | Where it comes from |
|---|---|
| `bearer` | the `Authorization: Bearer …` token the web client sends |
| `session` | the `ds_session_id` cookie |

In Chrome: open <https://chat.deepseek.com>, DevTools → **Network** → send any message → click the `completion` request. The bearer is in the **Request Headers**; `ds_session_id` is in **Application → Cookies**.

Then either export them:

```bash
export DEEPSEEK_BEARER="…"
export DEEPSEEK_SESSION="…"
```

or write `~/.config/deepseek-agent/credentials.json` (mode `600`):

```json
{ "bearer": "…", "session": "…" }
```

Nothing is stored in this repository, and the script refuses to run without them.

## Use it as an MCP server

### omp

```bash
./deepseek-agent.py --install-mcp     # writes ~/.omp/agent/mcp.json
./deepseek-agent.py --uninstall-mcp
```

Then `/mcp reload` in omp.

### Claude Code

`claude mcp add deepseek -- /path/to/deepseek-agent.py --mcp`, or add a `.mcp.json` in your project:

```json
{ "mcpServers": { "deepseek": {
  "command": "python3", "args": ["/path/to/deepseek-agent.py", "--mcp"] } } }
```

### Codex

In `~/.codex/config.toml`:

```toml
[mcp_servers.deepseek]
command = "python3"
args = ["/path/to/deepseek-agent.py", "--mcp"]
```

### Gemini CLI

In `~/.gemini/settings.json`:

```json
{ "mcpServers": { "deepseek": {
  "command": "python3", "args": ["/path/to/deepseek-agent.py", "--mcp"] } } }
```

### opencode

In `~/.config/opencode/opencode.json`:

```json
{ "mcp": { "deepseek": {
  "type": "local",
  "command": ["python3", "/path/to/deepseek-agent.py", "--mcp"],
  "enabled": true } } }
```

Other MCP clients work too — it is plain JSON-RPC 2.0 over stdio.

## Tools

| Tool | Arguments | What it does |
|---|---|---|
| `deepseek_review` | `diff`, `repo_path`, `focus`, `allow_write`, `thinking`, `max_tool_calls` | Reviews a diff. If `diff` is empty it fetches the diff from the repo itself. |
| `deepseek_ask` | `question`, `repo_path`, `allow_write`, `thinking`, `max_tool_calls` | Answers a question about a codebase. |
| `deepseek_status` | `live` | Reports whether credentials and the WASM solver are present. `live: true` proves the account works. |

## Use it from a terminal

```bash
# review the current diff
./deepseek-agent.py --review --repo .

# review a specific ref, or a patch file
./deepseek-agent.py --review --repo . --diff-ref origin/main
./deepseek-agent.py --review --diff-file fix.patch

# ask a question
./deepseek-agent.py --ask "where is auth enforced?" --repo .

# machine-readable output
./deepseek-agent.py --ask "…" --json

# interactive
./deepseek-agent.py
```

## Safety

**Read-only by default.** The model gets `read_file`, `list_dir`, `glob`, `grep`, and `git_readonly` — nothing else. `git_readonly` enforces a hardcoded subcommand whitelist (`diff`, `log`, `show`, `status`, `blame`, …), so destructive git operations are refused regardless of what the model asks for. Every path is resolved and rejected if it escapes the workspace root. Writes, Python execution, and shell access appear only with `--allow-write` / `allow_write: true`, which is off by default.

**You are responsible for your account.** This automates an unofficial interface. See below.

## Caveats

- **This is not an official DeepSeek integration.** It uses endpoints intended for the web client and may break without notice, and it may conflict with DeepSeek's terms of service. Use it at your own risk, on an account you don't mind losing.
- **Credentials are account credentials.** Anyone with your bearer token can act as you. Treat it like a password.
- **It rate-limits.** Bursts of requests get empty streams, which are retried a few times. Heavy sustained use needs a longer backoff than the default.
- **Read-only is a strong default, not a sandbox.** A prompt injection in the code being reviewed could still try to get the model to call a tool. Only the read tools are reachable, but treat untrusted diffs as untrusted input.

## Protocol notes

Reverse-engineered from live traffic, since it is undocumented:

- The completion stream is **positional**. A bare `{"v": text}` frame and a `response/fragments/-1/content` frame both append to the *current* fragment; only a `response/fragments` frame names a fragment's type, and it opens the *next* one. The opening snapshot carries fragment 0's first text and it is never re-sent, and the first content frame of a fragment omits the `o` field.
- Reasoning turns may answer in **DSML** (`<||DSML|| invoke …>`) rather than the requested JSON, so both encodings are accepted.
- The endpoint intermittently opens a 200 stream and closes it with no content. That is retried rather than reported as a blank answer.

## License

MIT — see [LICENSE](LICENSE).

Not affiliated with DeepSeek.