deepseek-web-retrieval 0.7.0 → 0.9.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (44) hide show
  1. package/CHANGELOG.md +20 -0
  2. package/README.md +100 -101
  3. package/dist/cli.js +526 -175
  4. package/docs/agents/claude-code.md +279 -0
  5. package/docs/agents/codex.md +9 -1
  6. package/docs/agents/opencode-v2.md +13 -3
  7. package/docs/agents/pi.md +8 -1
  8. package/docs/configuration.md +116 -0
  9. package/docs/design/installer-design.md +20 -17
  10. package/docs/hosts.jsonc +5 -1
  11. package/docs/integration.md +9 -6
  12. package/docs/research/control-matrix.md +34 -9
  13. package/docs/research/mcp-compatibility.md +2 -2
  14. package/docs/research/ori-question-sourcing.md +12 -1
  15. package/package.json +3 -2
  16. package/src/core/auth.ts +7 -0
  17. package/src/core/client.ts +89 -25
  18. package/src/core/config-summary.ts +1 -0
  19. package/src/core/constants.ts +17 -2
  20. package/src/core/descriptions.ts +10 -1
  21. package/src/core/env.ts +12 -7
  22. package/src/core/format.ts +25 -4
  23. package/src/core/options.ts +47 -0
  24. package/src/core/search.ts +97 -14
  25. package/src/core/types.ts +17 -0
  26. package/src/installer/apply.ts +4 -4
  27. package/src/installer/cli.ts +2 -2
  28. package/src/installer/config-command.ts +4 -3
  29. package/src/installer/hosts/claude-code.ts +311 -0
  30. package/src/installer/hosts/codex.ts +3 -14
  31. package/src/installer/hosts/index.ts +5 -2
  32. package/src/installer/hosts/opencode.ts +1 -1
  33. package/src/installer/hosts/pi.ts +38 -18
  34. package/src/installer/hosts/shared.ts +23 -0
  35. package/src/installer/ownership.ts +2 -2
  36. package/src/installer/paths.ts +2 -2
  37. package/src/installer/{state.ts → record.ts} +16 -16
  38. package/src/installer/types.ts +2 -2
  39. package/src/installer/uninstall.ts +8 -5
  40. package/src/mcp/serve.ts +1 -2
  41. package/src/mcp/server.ts +16 -19
  42. package/src/opencode/plugin.ts +15 -10
  43. package/src/pi/extension.ts +23 -17
  44. package/src/mcp/auth.ts +0 -11
@@ -0,0 +1,279 @@
1
+ # Claude Code
2
+
3
+ Claude Code adapter for [DeepSeek Web Retrieval](../../README.md).
4
+
5
+ Integration mechanism: MCP server over stdio, registered in Claude Code's own MCP config
6
+ files — `.mcp.json` at the project root (project scope) or the top-level `mcpServers` key
7
+ of `~/.claude.json` (user scope, where `claude mcp add --scope user` writes). Claude Code
8
+ negotiates MCP revision `2026-07-28`; the server accepts clients from `2024-11-05` through
9
+ `2026-07-28`, so the same build works for other MCP hosts (see
10
+ [the compatibility research](../research/mcp-compatibility.md)).
11
+
12
+ When registered, it adds:
13
+
14
+ - A `deepseek_web_search` tool backed by DeepSeek Search
15
+ - A `deepseek_fetch` tool backed by DeepSeek Fetch
16
+
17
+ The tools appear in Claude Code as `mcp__deepseek__deepseek_web_search` and
18
+ `mcp__deepseek__deepseek_fetch`. Claude Code's built-in `WebSearch` and `WebFetch` tools
19
+ are not replaced; see [Control tiers](#control-tiers) for routing them away.
20
+
21
+ For shared search/fetch limits, retries, and privacy behavior, see
22
+ [General behavior](../../README.md#general-behavior).
23
+ For configuration layers, the full knob mapping, and defaults, see the
24
+ [configuration guide](../configuration.md).
25
+
26
+ ## Requirements
27
+
28
+ - Claude Code at the tested version in [`docs/hosts.jsonc`](../hosts.jsonc); init reports
29
+ version drift, and newer versions may require rechecking routing behavior
30
+ - Node.js 20 or newer (`npx` runs the server)
31
+ - A DeepSeek account with access to `/v1/tools/search` and `/v1/tools/fetch`
32
+ - A funded DeepSeek account; Search and Fetch are billed per successful request
33
+ - `DEEPSEEK_API_KEY` set in the shell that launches Claude Code
34
+
35
+ ## Install
36
+
37
+ The quickest option is the installer CLI:
38
+
39
+ ```bash
40
+ npx deepseek-web-retrieval init --host claude-code --scope project \
41
+ --search <tier> --fetch <tier>
42
+ ```
43
+
44
+ It prints the full plan — every file, old value → new value, and notes about existing MCP
45
+ servers — and writes nothing until you pass `--apply`. Add `--pin` to register the exact
46
+ version of the CLI you are running instead of the always-latest package name. `uninstall`
47
+ removes recorded additions and restores snapshotted values that have not since been edited.
48
+ `--scope project` registers the server in `./.mcp.json` (shareable via VCS) and writes tier
49
+ artifacts to `./.claude/settings.json` and `./CLAUDE.md`; `--scope global` uses
50
+ `~/.claude.json`, `~/.claude/settings.json`, and `~/.claude/CLAUDE.md` instead — the two
51
+ scopes use disjoint files, so both can coexist. An `off` capability is written as the
52
+ tool's full id in the `permissions.deny` array — Claude Code's own mechanism, which removes
53
+ the tool from the model's context while the server keeps running — merging with any entries
54
+ you put there yourself. When `claude` is on PATH, `init` also probes `claude --version` and
55
+ adds a plan note when the installed release differs from the tested lock (see
56
+ [hosts.jsonc](../hosts.jsonc)) — version-anchored behavior such as the deny rules may have
57
+ drifted between the two.
58
+
59
+ For an existing installer-managed setup, follow the
60
+ [upgrade instructions](../integration.md#upgrading-existing-installs) from the original
61
+ project directory and scope.
62
+
63
+ ### Manual install
64
+
65
+ Add the server to `.mcp.json` at the project root:
66
+
67
+ ```json
68
+ {
69
+ "mcpServers": {
70
+ "deepseek": {
71
+ "command": "npx",
72
+ "args": ["-y", "deepseek-web-retrieval"],
73
+ "env": { "DEEPSEEK_API_KEY": "${DEEPSEEK_API_KEY}" }
74
+ }
75
+ }
76
+ }
77
+ ```
78
+
79
+ > [!NOTE]
80
+ > `"${DEEPSEEK_API_KEY}"` is Claude Code's own `${VAR}` expansion: the key is read from
81
+ > the environment that launches Claude Code and never sits in the file. A literal value
82
+ > works too if you prefer it in the file. For a user-scope install, the same entry goes
83
+ > under the top-level `mcpServers` key of `~/.claude.json`, or use
84
+ > `claude mcp add --scope user --env 'DEEPSEEK_API_KEY=${DEEPSEEK_API_KEY}' deepseek -- npx -y deepseek-web-retrieval`.
85
+
86
+ Claude Code asks once per project whether to use servers from `.mcp.json`; approve the
87
+ `deepseek` server when prompted (or via the `/mcp` menu), or its tools will not load in
88
+ that project.
89
+
90
+ To disable one capability, add the tool's full id to `permissions.deny` in
91
+ `.claude/settings.json` — Claude Code removes the tool from the model's context while the
92
+ server keeps running:
93
+
94
+ ```json
95
+ {
96
+ "permissions": {
97
+ "deny": ["mcp__deepseek__deepseek_web_search"]
98
+ }
99
+ }
100
+ ```
101
+
102
+ The ids match the tool names verbatim (`mcp__deepseek__deepseek_web_search`,
103
+ `mcp__deepseek__deepseek_fetch`). The server also honors
104
+ `DEEPSEEK_ENABLE_SEARCH`/`DEEPSEEK_ENABLE_FETCH` in the server's `env` block if you prefer
105
+ switching the tools off at the source; the installer uses `permissions.deny`.
106
+
107
+ ### Local checkout
108
+
109
+ ```bash
110
+ git clone https://github.com/gwokhou/deepseek-web-retrieval.git
111
+ cd deepseek-web-retrieval && bun install && bun run build
112
+ ```
113
+
114
+ ```json
115
+ {
116
+ "mcpServers": {
117
+ "deepseek": {
118
+ "command": "node",
119
+ "args": ["/absolute/path/to/deepseek-web-retrieval/dist/cli.js"],
120
+ "env": { "DEEPSEEK_API_KEY": "${DEEPSEEK_API_KEY}" }
121
+ }
122
+ }
123
+ }
124
+ ```
125
+
126
+ ## Control tiers
127
+
128
+ Claude Code ships built-in `WebSearch` and `WebFetch` tools; neither is replaceable, so
129
+ both capabilities use exclusion for the forced tier:
130
+
131
+ | Capability | Optional | Preferred | Forced |
132
+ | --- | --- | --- | --- |
133
+ | Search | Tools registered; the built-in `WebSearch` stays available | Plus a CLAUDE.md rules block steering the model to `mcp__deepseek__deepseek_web_search` | Exclusion: `permissions.deny: ["WebSearch"]` |
134
+ | Fetch | Tools registered; the built-in `WebFetch` stays available | Plus the rules block | Exclusion: `permissions.deny: ["WebFetch"]` |
135
+
136
+ The installer maps each tier to these artifacts. Deny entries land in
137
+ `~/.claude/settings.json` (user) or `./.claude/settings.json` (project) and merge with
138
+ your existing `permissions.deny` entries. The CLAUDE.md rules block lands in
139
+ `~/.claude/CLAUDE.md` (user) or `./CLAUDE.md` (project); Claude Code loads the user file
140
+ plus every CLAUDE.md from the cwd up the directory tree, concatenating the levels — so
141
+ the block steers wherever it is installed. Under a forced tier, other web-capable MCP
142
+ servers detected in the config are denied by server-level rules (`mcp__<server>` denies
143
+ every tool of that server), so routing stays exclusive.
144
+
145
+ > [!WARNING]
146
+ > Deny rules outrank allow rules at every settings level and **cannot be carved out** —
147
+ > denying `WebSearch` or `WebFetch` removes the tool for every workflow in the install's
148
+ > scope (every project on the machine for `--scope global`). A managed policy layer
149
+ > (`managed-settings.json`) outranks both files; the installer reports when one exists.
150
+
151
+ ## Authentication
152
+
153
+ `DEEPSEEK_API_KEY` is the only source; MCP has no host credential store. It is forwarded
154
+ through the server's `env` block as `${DEEPSEEK_API_KEY}` and read per request, sent only
155
+ to `api.deepseek.com` as a Bearer token. Without it the server exits at startup with a
156
+ message on stderr.
157
+
158
+ ## Usage
159
+
160
+ Ask naturally:
161
+
162
+ ```text
163
+ Search the web for the latest Claude Code MCP config changes and cite the sources.
164
+ Fetch https://code.claude.com/docs/en/mcp and summarize the config keys.
165
+ ```
166
+
167
+ The model calls `mcp__deepseek__deepseek_web_search` or `mcp__deepseek__deepseek_fetch`.
168
+
169
+ The search tool accepts an optional `original_question` argument — the user's question in
170
+ their own words — forwarded as DeepSeek's `ori_question` to sharpen search intent; omit it
171
+ when the query already stands alone. It also takes an optional `count` (1–50) for the call,
172
+ and the fetch tool takes an optional `maxChars` (0–1,000,000) per-call output budget; each
173
+ overrides the configured default for that call, so the model can widen a truncated fetch or
174
+ ask for more results without rephrasing the query. When configured domain or language
175
+ operators change the query actually sent, the result text opens with
176
+ `Searched for: <effective query>` (plus any degradation applied to fit the API's
177
+ 128-character limit), so the model can attribute results to the configuration in effect.
178
+
179
+ Tool output is capped at 25k tokens by default (`MAX_MCP_OUTPUT_TOKENS`), and successful
180
+ text results longer than 50,000 characters are saved to a file and referenced — not
181
+ dropped silently.
182
+
183
+ ## Configuration
184
+
185
+ Set these in the server's `env` block in `.mcp.json` (or `~/.claude.json`). Values map onto
186
+ the shared option pipeline, so defaults and clamping match the other adapters.
187
+ `DEEPSEEK_API_KEY` goes in the same block as the `${VAR}` expansion.
188
+
189
+ > [!NOTE]
190
+ > A shared config file — `~/.config/deepseek-web-retrieval/config.json`
191
+ > (Windows: `%APPDATA%\deepseek-web-retrieval\config.json`), camelCase keys —
192
+ > sits under the environment layer and works with no env forwarding at all: the server
193
+ > reads the file itself, making it the way to set knobs without editing the MCP entry. The
194
+ > file carries neither the API key nor the host-integration switches. Run
195
+ > `npx deepseek-web-retrieval config` to see every knob's effective value and source
196
+ > (it replays file warnings visibly and exits 1 on file problems); `config --search "<query>"`
197
+ > verifies key, config, and network with one billed search.
198
+
199
+ ### Search
200
+
201
+ The "Config file" column names each key's camelCase twin in the shared config file —
202
+ one vocabulary pair, one pipeline. `—` marks host-integration entries the file rejects.
203
+
204
+ | Env variable | Config file | Default | Description |
205
+ | --- | --- | --- | --- |
206
+ | `DEEPSEEK_ENABLE_SEARCH` | — | `true` | Register the search tool. Only an explicit `false`/`0`/`no`/`off` disables it; at least one tool must stay enabled. |
207
+ | `DEEPSEEK_SEARCH_COUNT` | `count` | `10` | Results requested from DeepSeek, 1–50. Billing is per request, not per result. |
208
+ | `DEEPSEEK_SEARCH_MAX_RESULTS` | `maxResults` | `10` | Results returned to the model after filtering, 0–50; defaults to `DEEPSEEK_SEARCH_COUNT` when unset. `0` returns no results. |
209
+ | `DEEPSEEK_SEARCH_MIN_RERANK` | `minRerank` | `0` | Drop results below this relevance score (0–3); results without a finite score pass at zero and are dropped at a positive threshold. |
210
+ | `DEEPSEEK_SEARCH_MIN_AUTHORITY` | `minAuthority` | `0` | Drop known authorities below this tier. Authority is 0–4. |
211
+ | `DEEPSEEK_SEARCH_ALLOW_UNKNOWN_AUTHORITY` | `allowUnknownAuthority` | `true` | Keep results whose authority is missing or unknown. Set `false`/`0`/`no`/`off` to drop them. |
212
+ | `DEEPSEEK_SEARCH_BLOCKED_DOMAINS` | `blockedDomains` | unset | Comma-separated; adds `-site:` operators to the query. |
213
+ | `DEEPSEEK_SEARCH_INCLUDE_DOMAINS` | `includeDomains` | unset | Comma-separated; restricts results with `site:` operators. |
214
+ | `DEEPSEEK_SEARCH_LANGUAGE` | `language` | unset | Adds a `lang:` operator, for example `zh,en`. |
215
+ | `DEEPSEEK_SEARCH_LOCALIZATION` | `searchLocalization` | unset | Valid IPv4 or IPv6 address sent as DeepSeek's `localization` to prefer geo-localized results; invalid values are ignored. Deployment-level only, never a per-call parameter. |
216
+ | `DEEPSEEK_SEARCH_MAX_SUMMARY_CHARS` | `maxSummaryChars` | `1200` | Per-result summary cap, 0–1,000,000. |
217
+ | `DEEPSEEK_SEARCH_TIMEOUT_MS` | `searchTimeoutMs` | `30000` | Search timeout, 1,000–120,000 ms. |
218
+
219
+ ### Fetch
220
+
221
+ | Env variable | Config file | Default | Description |
222
+ | --- | --- | --- | --- |
223
+ | `DEEPSEEK_ENABLE_FETCH` | — | `true` | Register the fetch tool. Same false-only semantics as `DEEPSEEK_ENABLE_SEARCH`. |
224
+ | `DEEPSEEK_FETCH_MAX_CHARS` | `fetchMaxChars` | `16000` | Maximum characters returned, including the title, 0–1,000,000. |
225
+ | `DEEPSEEK_FETCH_TIMEOUT_MS` | `fetchTimeoutMs` | `30000` | Fetch timeout, 1,000–120,000 ms. |
226
+ | `DEEPSEEK_FETCH_ALLOWED_DOMAINS` | `fetchAllowedDomains` | unset | If set, only these hosts and subdomains may be fetched. |
227
+ | `DEEPSEEK_FETCH_BLOCKED_DOMAINS` | `fetchBlockedDomains` | unset | Always block these hosts and subdomains. Blocked wins over allowed. |
228
+ | `DEEPSEEK_FETCH_BLOCK_PRIVATE_HOSTS` | `fetchBlockPrivateHosts` | `true` | Reject localhost, private IPs, `.local`, `.internal`, and similar hosts. |
229
+
230
+ ### Credentials and retries
231
+
232
+ | Env variable | Config file | Default | Description |
233
+ | --- | --- | --- | --- |
234
+ | `DEEPSEEK_API_KEY` | — | required | DeepSeek API key, expanded from the launching environment via `${DEEPSEEK_API_KEY}`. The server refuses to start without it. |
235
+ | `DEEPSEEK_MAX_RETRIES` | `maxRetries` | `2` | Retries for 408, 425, 429, and 5xx responses, 0–5. |
236
+ | `DEEPSEEK_RETRY_BASE_DELAY_MS` | `retryBaseDelayMs` | `250` | Base delay for exponential backoff. `Retry-After` is honored up to 10 seconds. |
237
+
238
+ ## Troubleshooting
239
+
240
+ | Error or symptom | What to do |
241
+ | --- | --- |
242
+ | Server fails to start or `HTTP 401` from the tools | `DEEPSEEK_API_KEY` must be set in the environment that launches Claude Code; the `${DEEPSEEK_API_KEY}` expansion in the server's `env` block reads it at launch. |
243
+ | Project tools missing after install | Claude Code asks once per project whether to use `.mcp.json` servers; approve the `deepseek` server when prompted or via the `/mcp` menu. |
244
+ | Both tools missing after startup | At least one of `DEEPSEEK_ENABLE_SEARCH`/`DEEPSEEK_ENABLE_FETCH` must stay enabled; the server exits when both are false. |
245
+ | Forced tier still shows the built-in tool | The kill switch is the bare deny rule in `permissions.deny` at the install's scope; a managed policy layer (`managed-settings.json`) outranks it — the plan reports when one exists. |
246
+ | Routing not exclusive | Other MCP servers stay registered and are noted in the plan. Under a forced tier, web-capable servers detected in the config (and the other scope's file) are denied by server-level `mcp__<server>` rules; a server added after install is not covered — re-run init. |
247
+ | Fetch results appear as a file reference instead of content | Tool output is capped at 25k tokens by default; raise `MAX_MCP_OUTPUT_TOKENS` or lower `DEEPSEEK_FETCH_MAX_CHARS`. |
248
+ | `HTTP 402`, `HTTP 429` | Top up the DeepSeek account; wait, reduce concurrency, or set `DEEPSEEK_MAX_RETRIES`. |
249
+ | Fetch fails on localhost or an internal URL | Use a local fetch tool; DeepSeek cannot reach private networks. |
250
+
251
+ ## Compatibility
252
+
253
+ | Component | Version |
254
+ | --- | --- |
255
+ | Claude Code | locked in [`docs/hosts.jsonc`](../hosts.jsonc) |
256
+ | MCP revision | `2026-07-28` negotiated; server accepts `2024-11-05`–`2026-07-28` |
257
+ | MCP SDK | `@modelcontextprotocol/server` 2.3.1 |
258
+ | Runtime | Node.js 20+ (stdio) |
259
+ | DeepSeek API | `/v1/tools/search`, `/v1/tools/fetch` |
260
+
261
+ Tested with Claude Code at the `docs/hosts.jsonc` lock on Linux over stdio: the keyless
262
+ Docker e2e verifies the installer's registration, tiers, and uninstall with a real
263
+ `claude mcp list` handshake, and the billed scenario (`bun run test:e2e claude-code`,
264
+ requires `DEEPSEEK_API_KEY`) runs headless prompts end to end with the deny kill switch
265
+ asserted for both capabilities.
266
+
267
+ ## Uninstall
268
+
269
+ ```bash
270
+ npx deepseek-web-retrieval uninstall --host claude-code --scope project
271
+ ```
272
+
273
+ This removes the recorded `mcpServers.deepseek` entry and the installer-appended
274
+ `permissions.deny` entries only when their current values still match what the installer
275
+ wrote, removes the managed rules block, and deletes installer-created config files only
276
+ when left empty. Later user edits to configuration values are kept and reported. For a
277
+ manual installation, remove the `deepseek` entry from `.mcp.json` (or `~/.claude.json`),
278
+ any `deepseek-web-retrieval` rules block in your `CLAUDE.md`, and the deny entries the
279
+ setup added to `settings.json` if they were only for this integration.
@@ -24,6 +24,8 @@ both the text and `structuredContent`.
24
24
 
25
25
  For shared search/fetch limits, retries, and privacy behavior, see
26
26
  [General behavior](../../README.md#general-behavior).
27
+ For configuration layers, the full knob mapping, and defaults, see the
28
+ [configuration guide](../configuration.md).
27
29
 
28
30
  ## Requirements
29
31
 
@@ -146,7 +148,13 @@ The model calls `mcp__deepseek__deepseek_web_search` or `mcp__deepseek__deepseek
146
148
 
147
149
  The search tool accepts an optional `original_question` argument — the user's question in
148
150
  their own words — forwarded as DeepSeek's `ori_question` to sharpen search intent; omit it
149
- when the query already stands alone.
151
+ when the query already stands alone. It also takes an optional `count` (1–50) for the call,
152
+ and the fetch tool takes an optional `maxChars` (0–1,000,000) per-call output budget; each
153
+ overrides the configured default for that call, so the model can widen a truncated fetch or
154
+ ask for more results without rephrasing the query. When configured domain or language
155
+ operators change the query actually sent, the result text opens with
156
+ `Searched for: <effective query>` (plus any degradation applied to fit the API's
157
+ 128-character limit), so the model can attribute results to the configuration in effect.
150
158
 
151
159
  ## Configuration
152
160
 
@@ -15,6 +15,8 @@ if you want all Markdown fetches to go through DeepSeek.
15
15
 
16
16
  For shared search/fetch limits, retries, and privacy behavior, see
17
17
  [General behavior](../../README.md#general-behavior).
18
+ For configuration layers, the full knob mapping, and defaults, see the
19
+ [configuration guide](../configuration.md).
18
20
 
19
21
  ## Requirements
20
22
 
@@ -174,9 +176,17 @@ Fetch https://opencode.ai/v2/docs/build/plugins and summarize the plugin lifecyc
174
176
  The model calls `websearch` (provider `deepseek`) or `deepseek_fetch`. When
175
177
  `fetchOverrideWebfetch` is enabled, Markdown fetches go through `webfetch` instead.
176
178
 
177
- DeepSeek's optional `ori_question` is not sent on this path: OpenCode's `websearch` tool
178
- input is fixed to `query` at 2.0.32, so the provider has no channel for it (see the
179
- [research note](../research/ori-question-sourcing.md)).
179
+ DeepSeek's optional `ori_question` is not sent on this path, and the model cannot request a
180
+ per-call result count: OpenCode's `websearch` tool input is fixed to `query` (verified unchanged
181
+ at 2.1.4), so the provider has no channel for either. Upstream requests:
182
+ [anomalyco/opencode#58635](https://github.com/anomalyco/opencode/issues/58635) (session linkage)
183
+ and [#58636](https://github.com/anomalyco/opencode/issues/58636) (per-call `count`); see the
184
+ [research note](../research/ori-question-sourcing.md). The fetch tools are not constrained:
185
+ they accept an optional per-call `maxChars` (0–1,000,000) that overrides the configured
186
+ `fetchMaxChars` for one call, so the model can widen the budget after seeing a
187
+ `[content truncated]` marker. The effective-query header and query degradation notes have no
188
+ channel on this path either — OpenCode's result shape is closed — but degradation still runs,
189
+ and failures that cannot degrade surface as errors.
180
190
 
181
191
  ## Configuration
182
192
 
package/docs/agents/pi.md CHANGED
@@ -22,6 +22,8 @@ When loaded, it adds:
22
22
  Pi 1.3.0 ships no built-in web tool, so the registered tools are the only web path. For
23
23
  shared search/fetch limits, retries, and privacy behavior, see
24
24
  [General behavior](../../README.md#general-behavior).
25
+ For configuration layers, the full knob mapping, and defaults, see the
26
+ [configuration guide](../configuration.md).
25
27
 
26
28
  ## Requirements
27
29
 
@@ -108,7 +110,12 @@ The search tool accepts an optional `original_question` input — the user's que
108
110
  own words — forwarded as DeepSeek's `ori_question` to sharpen search intent. When the model
109
111
  leaves it blank, the extension falls back to the most recent user message in the session
110
112
  (best-effort; truncated to the API's 128-character limit). The MCP fallback path has no
111
- session access, so there a blank input simply omits the parameter.
113
+ session access, so there a blank input simply omits the parameter. The search tool also
114
+ takes an optional `count` (1–50) for the call, and the fetch tool an optional `maxChars`
115
+ (0–1,000,000) per-call output budget; each overrides the configured default for that call.
116
+ When configured domain or language operators change the query actually sent, the result text
117
+ opens with `Searched for: <effective query>` (plus any degradation applied to fit the API's
118
+ 128-character limit), so the model can attribute results to the configuration in effect.
112
119
 
113
120
  ## Configuration
114
121
 
@@ -0,0 +1,116 @@
1
+ # Configuration guide
2
+
3
+ The one reference for how every knob resolves on every host: the layering
4
+ chain, the shared config file, the full knob vocabulary, and which layer to set.
5
+ Behavior (what search and fetch do) lives in the README's
6
+ [General behavior](../README.md#general-behavior); per-host mechanics (output
7
+ caps, deadlines, how the environment reaches a stdio server) live in the host
8
+ guides: [Codex](agents/codex.md), [Claude Code](agents/claude-code.md),
9
+ [Pi](agents/pi.md),
10
+ [OpenCode](agents/opencode-v2.md).
11
+
12
+ ## The resolution chain
13
+
14
+ Every knob resolves through one chain, low → high, identically on every host:
15
+
16
+ ```
17
+ built-in defaults < shared config file < DEEPSEEK_* environment < host-native config
18
+ ```
19
+
20
+ A higher layer always wins, so nothing already configured loses effect. The
21
+ host-native layer means OpenCode's plugin options (`opencode.jsonc`), Pi's
22
+ `deepseekWebRetrieval` settings namespace, and — for MCP hosts — the per-server
23
+ `env` block in the host's registration (that block is how an environment
24
+ variable arrives, not a fifth layer).
25
+
26
+ ## The shared config file
27
+
28
+ `$XDG_CONFIG_HOME/deepseek-web-retrieval/config.json` when `XDG_CONFIG_HOME` is
29
+ set (this wins on every platform, Windows included); otherwise
30
+ `~/.config/deepseek-web-retrieval/config.json` on Linux and macOS and
31
+ `%APPDATA%\deepseek-web-retrieval\config.json` on Windows. camelCase keys:
32
+
33
+ ```json
34
+ {
35
+ "count": 20,
36
+ "blockedDomains": ["pinterest.com"],
37
+ "language": "zh",
38
+ "searchLocalization": "203.0.113.7"
39
+ }
40
+ ```
41
+
42
+ Unknown keys and host-integration keys (below) draw a one-line stderr warning
43
+ and are otherwise ignored — a bad file never blocks startup. The API key never
44
+ goes in the file; it keeps its existing channels (`DEEPSEEK_API_KEY` in the
45
+ environment, or OpenCode's stored credential).
46
+
47
+ ## Knob classes
48
+
49
+ | Knob class | Shared file | Examples |
50
+ | --- | --- | --- |
51
+ | API behavior — the user↔DeepSeek relationship | yes — that is the point of the file | `count`, `blockedDomains`, `language`, `maxRetries` |
52
+ | Host-sensitive — interacts with host output caps and deadlines | carries the safe defaults; a host layer is the recommended override point | `searchTimeoutMs`, `fetchTimeoutMs`, `fetchMaxChars`, `maxSummaryChars` |
53
+ | Host integration — the host's tool landscape | rejected — warned about and ignored | `searchEnabled`, `setDefaultWebsearch`, `apiKeySource` |
54
+
55
+ Keep host-independent intent in the shared file; prefer a host layer when a
56
+ host-sensitive knob would cross its host's cap or deadline.
57
+
58
+ ## The knob vocabulary
59
+
60
+ One knob, two names: the camelCase key for the shared file and OpenCode's
61
+ options, the `DEEPSEEK_*` variable for the environment. The table generates
62
+ from the same config vocabulary the adapters resolve through, so it cannot
63
+ drift from the code (`bun run docs:matrix` regenerates it; CI checks it).
64
+
65
+ <!-- generated: knob-table — edit src/core/config-summary.ts or env.ts, then run `bun run docs:matrix` -->
66
+
67
+ | File / OpenCode key | Environment | Default |
68
+ | --- | --- | --- |
69
+ | `count` | `DEEPSEEK_SEARCH_COUNT` | 10 |
70
+ | `maxResults` | `DEEPSEEK_SEARCH_MAX_RESULTS` | follows `count` (10) |
71
+ | `minRerank` | `DEEPSEEK_SEARCH_MIN_RERANK` | 0 |
72
+ | `minAuthority` | `DEEPSEEK_SEARCH_MIN_AUTHORITY` | 0 |
73
+ | `allowUnknownAuthority` | `DEEPSEEK_SEARCH_ALLOW_UNKNOWN_AUTHORITY` | true |
74
+ | `blockedDomains` | `DEEPSEEK_SEARCH_BLOCKED_DOMAINS` | unset |
75
+ | `includeDomains` | `DEEPSEEK_SEARCH_INCLUDE_DOMAINS` | unset |
76
+ | `language` | `DEEPSEEK_SEARCH_LANGUAGE` | unset |
77
+ | `searchLocalization` | `DEEPSEEK_SEARCH_LOCALIZATION` | unset |
78
+ | `maxSummaryChars` | `DEEPSEEK_SEARCH_MAX_SUMMARY_CHARS` | 1200 |
79
+ | `searchTimeoutMs` | `DEEPSEEK_SEARCH_TIMEOUT_MS` | 30000 |
80
+ | `fetchMaxChars` | `DEEPSEEK_FETCH_MAX_CHARS` | 16000 |
81
+ | `fetchAllowedDomains` | `DEEPSEEK_FETCH_ALLOWED_DOMAINS` | unset |
82
+ | `fetchBlockedDomains` | `DEEPSEEK_FETCH_BLOCKED_DOMAINS` | unset |
83
+ | `fetchTimeoutMs` | `DEEPSEEK_FETCH_TIMEOUT_MS` | 30000 |
84
+ | `fetchBlockPrivateHosts` | `DEEPSEEK_FETCH_BLOCK_PRIVATE_HOSTS` | true |
85
+ | `maxRetries` | `DEEPSEEK_MAX_RETRIES` | 2 |
86
+ | `retryBaseDelayMs` | `DEEPSEEK_RETRY_BASE_DELAY_MS` | 250 |
87
+ | `baseUrl` | `DEEPSEEK_BASE_URL` | https://api.deepseek.com |
88
+
89
+ <!-- /generated: knob-table -->
90
+
91
+ ## Special semantics
92
+
93
+ - **`maxResults` follows `count`** when left unset — configure only `count` and
94
+ the cap moves with it. An explicit `maxResults` still caps; `count` never
95
+ follows `maxResults`.
96
+ - **Some switches are false-only.** `allowUnknownAuthority`,
97
+ `fetchBlockPrivateHosts`, `searchEnabled`, and `fetchEnabled` are on by
98
+ default; the environment can only turn them off (`false`, `0`, `no`, or
99
+ `off`). The last two have no file key — they are host-integration keys — so
100
+ they exist only as `DEEPSEEK_ENABLE_SEARCH` and `DEEPSEEK_ENABLE_FETCH`. A
101
+ value like `=true` changes nothing.
102
+ - **Invalid environment values are dropped** at startup and the default
103
+ applies. `npx deepseek-web-retrieval config` reports every dropped or
104
+ unknown `DEEPSEEK_*` variable as a warning and exits 1, alongside file
105
+ problems.
106
+ - **`searchLocalization` must be a valid IPv4 or IPv6 address**; anything else
107
+ is dropped at option resolution. It is deployment-level configuration, never
108
+ a per-call tool parameter.
109
+ - **`baseUrl` routes the retrieval API** (`/v1/tools/search` and
110
+ `/v1/tools/fetch` are composed from it). It is infrastructure routing for
111
+ proxies, gateways, and mock upstreams — not a provider switch, so nothing
112
+ falls back to another search service. Only an http(s) URL survives
113
+ resolution (trailing slashes are stripped); anything else drops back to
114
+ `https://api.deepseek.com`.
115
+ - **Domains** in the environment are comma-separated; entries that are not
116
+ domains are dropped (and reported by `config`).
@@ -1,13 +1,13 @@
1
1
  # Installer design
2
2
 
3
3
  Design note for the install-time flow, shipped as the `deepseek-web-retrieval init`,
4
- `uninstall`, and `list` subcommands for **OpenCode, Pi, and Codex**. It records how the
5
- [control tiers](../research/control-matrix.md) map to the artifacts the installer emits, so
6
- the design survives between conversations. Research backing lives in
4
+ `uninstall`, and `list` subcommands for **OpenCode, Pi, Codex, and Claude Code**. It records how the
5
+ [control tiers](../research/control-matrix.md) map to the artifacts the installer emits, so the design
6
+ survives between conversations. Research backing lives in
7
7
  [control-matrix.md](../research/control-matrix.md); host facts in [hosts.jsonc](../hosts.jsonc).
8
8
  Remaining hosts get host modules here as their guides ship.
9
9
  Examples involving other hosts describe design requirements for future modules;
10
- the current CLI accepts only the three shipped hosts above.
10
+ the current CLI accepts only the four shipped hosts above.
11
11
 
12
12
  ## Principles
13
13
 
@@ -44,9 +44,9 @@ already exists, and the installer uses three phases:
44
44
  Report the conflict surface: existing rules steering toward another web tool; other
45
45
  web-capable MCP servers already on the field; array-valued keys the plan must merge
46
46
  into rather than replace (`disabledTools`, `permissions.deny`, permissions
47
- `exclude`, `disabled_tools`); and higher layers (Claude Code managed policy, Devin
48
- enterprise team settings) that would outrank what the
49
- installer writes.
47
+ `exclude`, `disabled_tools`); and higher layers (Claude Code managed policy — its
48
+ existence is checked by the claude-code module; Devin enterprise team settings) that
49
+ would outrank what the installer writes.
50
50
  2. **Merge or snapshot.** Array keys merge with existing entries, recording exactly
51
51
  which entries the installer appended; single-value keys (`websearch.provider`, Pi's
52
52
  `exposure`) snapshot the prior value before overwriting; files the installer creates
@@ -71,7 +71,9 @@ already exists, and the installer uses three phases:
71
71
  competitor, not third-party web MCP servers — those must be excluded too (the
72
72
  per-host keys cover them), or routing is not actually forced. Concretely: Pi's
73
73
  per-server `enabled` key lets the plan disable detected web-capable competitors
74
- (each prior value snapshotted for uninstall); Codex has no per-server kill switch,
74
+ (each prior value snapshotted for uninstall); Claude Code excludes them the same way
75
+ via server-level `mcp__<server>` deny rules appended to `permissions.deny`; Codex has
76
+ no per-server kill switch,
75
77
  so a detected web-capable competitor makes the plan refuse the tier instead of
76
78
  promising non-exclusive routing; OpenCode's forced is replacement, which no
77
79
  third-party server overrides. Detection classifies a server web-capable from its
@@ -156,9 +158,9 @@ the artifacts below name the path where they differ.
156
158
  | Optional | Host config registering the integration: Pi's extension path appends an `npm:<package>` source to `packages` in settings.json; the MCP paths register the server (Pi's MCP entry needs `"exposure": "direct"`; the extension path needs nothing — extension tools are `direct` by Pi's own default) |
157
159
  | Preferred | The above plus a rules file for the host (file names per host in the [per-host reference](../research/control-matrix.md#per-host-reference)); the rules name the path's tool ids (`deepseek_web_search` for the extension, `mcp__deepseek__deepseek_web_search` for MCP) |
158
160
  | Forced (replacement) | OpenCode plugin options (`setDefaultWebsearch` / `fetchOverrideWebfetch`) |
159
- | Forced (exclusion) | The built-in competitor's kill switch (per-host keys in the per-host reference), plus the exclusion of any other web-capable MCP servers the detect phase found — Pi sets their `enabled` key to `false`; Codex, lacking a per-server kill switch, refuses the tier instead; Pi's extension path additionally refuses when a web-capable `packages`/`extensions` source is listed in settings.json (no per-package kill switch exists) |
161
+ | Forced (exclusion) | The built-in competitor's kill switch (per-host keys in the per-host reference), plus the exclusion of any other web-capable MCP servers the detect phase found — Pi sets their `enabled` key to `false`; Claude Code appends server-level `mcp__<server>` deny rules to `permissions.deny` (merging with the user's own entries); Codex, lacking a per-server kill switch, refuses the tier instead; Pi's extension path additionally refuses when a web-capable `packages`/`extensions` source is listed in settings.json (no per-package kill switch exists) |
160
162
  | Forced (de-facto) | Nothing beyond the Optional artifacts — no competitor exists |
161
- | Off | Codex writes the capability's raw tool name into the server entry's `disabled_tools` (host-native: the tool leaves the model's context; the user's own entries in that array are kept); the other MCP entry writes the capability's false-only server-side switch instead (`DEEPSEEK_ENABLE_SEARCH` / `DEEPSEEK_ENABLE_FETCH`); Pi's extension path writes the same switches into the `deepseekWebRetrieval` settings namespace the extension reads at load; both capabilities off refuses to install |
163
+ | Off | Codex writes the capability's raw tool name into the server entry's `disabled_tools` (host-native: the tool leaves the model's context; the user's own entries in that array are kept); Claude Code appends the capability's full tool id (`mcp__deepseek__*`) to `permissions.deny`, the same host-native removal; the other MCP entry writes the capability's false-only server-side switch instead (`DEEPSEEK_ENABLE_SEARCH` / `DEEPSEEK_ENABLE_FETCH`); Pi's extension path writes the same switches into the `deepseekWebRetrieval` settings namespace the extension reads at load; both capabilities off refuses to install |
162
164
 
163
165
  OpenCode additionally appends `permissions` allow entries for exactly the tools it
164
166
  registers (`websearch`, `deepseek_fetch`, and `webfetch` when fetch is forced), merged
@@ -184,18 +186,19 @@ into any existing `permissions` array rather than replacing it.
184
186
  comments and formatting in `opencode.jsonc` and Pi's `mcp.json` survive apply and
185
187
  uninstall; TOML edits go through `smol-toml` and drop comments, which the plan reports
186
188
  as a note. The hand-written JSONC stripper that predated this lived in `scripts/jsonc.ts`.
187
- - Install state lives per host and scope (global under
188
- `~/.config/deepseek-web-retrieval/`, project under `.deepseek-web-retrieval/`); it
189
- records snapshots, appended entries, created files, and created path prefixes — never
190
- full file contents, so other servers' secrets stay out of the state file.
189
+ - Install records live per host and scope (global under
190
+ `~/.config/deepseek-web-retrieval/`, project under `.deepseek-web-retrieval/`); they
191
+ record snapshots, appended entries, created files, and created path prefixes — never
192
+ full file contents, so other servers' secrets stay out of the install record.
191
193
  - Re-applying over a previous init inherits the original snapshots instead of recording
192
194
  the installer's own output as the "previous" value. Pi forced re-inits keep their
193
195
  competitor-disable ops even when those servers are already disabled, so orphan
194
196
  reverts cannot re-enable a still-excluded competitor.
195
197
  - Verify is file-level: after apply it re-reads each written file and asserts every op
196
198
  holds (set values in place, appended entries present, rules block present). Higher
197
- layers would be a plan-time detect concern, and none of the three shipped hosts
198
- documents a layer above user config, so there is nothing to check there yet.
199
+ layers are a plan-time detect concern; Claude Code documents one (managed policy), and
200
+ the claude-code module reports an existing managed-settings.json instead of failing
201
+ silently. The other shipped hosts document no layer above user config.
199
202
  - Only a bare `--apply` enables writes; assigning a value such as `--apply=false`
200
203
  is a usage error and writes nothing.
201
204
  - `--pin` is opt-in version pinning: the ref written into host config gets the
@@ -224,7 +227,7 @@ host-side tool filtering.
224
227
 
225
228
  ## Hosts without an installer story
226
229
 
227
- Claude Code, Cursor, Devin, GitHub Copilot, and Cline still have no
230
+ Cursor, Devin, GitHub Copilot, and Cline still have no
228
231
  host module; their artifacts (from the tier-to-artifact mapping) remain hand-edited
229
232
  config until a module lands here. OpenCode's `opencode plugin add` remains available but
230
233
  cannot pass options or manage snapshots; `init` supersedes it for tiered installs.
package/docs/hosts.jsonc CHANGED
@@ -102,11 +102,15 @@
102
102
  "nativeSearchProvider": false,
103
103
  "nativeFetchOverride": false,
104
104
  "paths": ["mcp"],
105
- "status": "no-adapter-yet",
105
+ "status": "supported",
106
106
  "control": {
107
107
  "search": { "optional": "yes", "preferred": "yes", "forced": "exclusion", "key": "`permissions.deny: [\"WebSearch\"]` removes the tool from context" },
108
108
  "fetch": { "optional": "yes", "preferred": "yes", "forced": "exclusion", "key": "`permissions.deny: [\"WebFetch\"]`, or `CLAUDE_CODE_DISABLE_WEB_FETCH=1`" }
109
109
  },
110
+ "adapter": "src/mcp",
111
+ "installer": "src/installer/hosts/claude-code",
112
+ "guide": "docs/agents/claude-code.md",
113
+ "e2eScenario": "claude-code"
110
114
  },
111
115
  {
112
116
  "name": "cursor",
@@ -17,7 +17,7 @@ ship built-in web tools. MCP tools can add equivalent tools, but do not replace
17
17
  | Native tool / fetch override | No | Yes | Host tool API | OpenCode v2 adapter |
18
18
  | Custom tool plugin | Yes | Yes | Host plugin API | Cline CLI/SDK; no adapter yet |
19
19
  | Extension API | Yes | Yes | Host extension API | Pi adapter (`src/pi/`) |
20
- | MCP server | Yes | Yes | Host MCP client | Shipped (`src/mcp/`; Pi and Codex guides) |
20
+ | MCP server | Yes | Yes | Host MCP client | Shipped (`src/mcp/`; Pi, Codex, and Claude Code guides) |
21
21
  | CLI/HTTP wrapper | Yes | Yes | Host can call external tools | Not shipped |
22
22
 
23
23
  MCP is a protocol, not an agent. It is one possible transport for hosts that support MCP
@@ -35,7 +35,7 @@ Rows are sorted with open-source hosts first, then by host name alphabetically.
35
35
  | Codex | 0.163.0 | Yes (Apache-2.0) | No | No | MCP server | Supported |
36
36
  | OpenCode | 2.0.32 | Yes (MIT) | Yes | Yes | native websearch provider or native tool/fetch override | Supported |
37
37
  | Pi | 1.3.0 | Yes (MIT) | No | No | extension API or MCP server | Supported |
38
- | Claude Code | 2.1.303 | No | No | No | MCP server | No adapter yet |
38
+ | Claude Code | 2.1.303 | No | No | No | MCP server | Supported |
39
39
  | Cursor | 2026.10.08 | No | No | No | MCP server | No adapter yet |
40
40
  | Devin | 3000.11.92 | No | No | No | MCP server | No adapter yet |
41
41
  | GitHub Copilot | 1.0.98 | No | No | No | MCP server | No adapter yet |
@@ -48,7 +48,7 @@ Codex are Apache-2.0; OpenCode and Pi are MIT. Claude Code and
48
48
  Cursor are proprietary. GitHub Copilot CLI and Devin CLI publish installers and docs, not
49
49
  source code, under proprietary terms.
50
50
 
51
- OpenCode, Pi, and Codex have supported setup guides and installer host modules. The
51
+ OpenCode, Pi, Codex, and Claude Code have supported setup guides and installer host modules. The
52
52
  MCP server in `src/mcp/` provides a possible stdio path for the remaining hosts, but
53
53
  protocol compatibility alone does not establish a tested host setup. For those rows,
54
54
  `No adapter yet` means no supported host-specific adapter or setup guide is shipped.
@@ -66,8 +66,10 @@ the details: built-in web tools and their kill switches, MCP registration, and r
66
66
  - **OpenCode v2** — Uses OpenCode's native plugin API and
67
67
  exposes both a websearch provider and a fetch tool/override. See
68
68
  [OpenCode v2](agents/opencode-v2.md).
69
- - **Claude Code** — Ships built-in `WebSearch` and `WebFetch` tools. See
70
- [Claude Code](research/control-matrix.md#claude-code).
69
+ - **Claude Code** — Ships built-in `WebSearch` and `WebFetch` tools. Supported through
70
+ the MCP server; see
71
+ [Claude Code](agents/claude-code.md) and
72
+ [Claude Code in the per-host reference](research/control-matrix.md#claude-code).
71
73
  - **Codex** — Ships a built-in `web_search` tool. Supported through the MCP server; see
72
74
  [Codex](agents/codex.md) and [Codex in the per-host reference](research/control-matrix.md#codex).
73
75
  - **Cursor** — Uses the same `mcp.json` servers as the editor; ships built-in web search
@@ -119,7 +121,8 @@ Code importing the core's removed `formatFetchContent` helper must migrate to
119
121
  ## Available paths and future hosts
120
122
 
121
123
  - **MCP server** — shipped in `src/mcp/` (the fallback path in the [Pi](agents/pi.md) guide
122
- and the path in the [Codex](agents/codex.md) guide); the broadest route for hosts that
124
+ and the path in the [Codex](agents/codex.md) and
125
+ [Claude Code](agents/claude-code.md) guides); the broadest route for hosts that
123
126
  support MCP tools. Protocol compatibility across these hosts is researched in
124
127
  [mcp-compatibility.md](research/mcp-compatibility.md).
125
128
  - **Native extension adapters** — the Pi extension adapter ships in `src/pi/`; a host whose