deepseek-web-retrieval 0.8.0 → 0.10.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 (53) hide show
  1. package/CHANGELOG.md +18 -1
  2. package/README.md +33 -16
  3. package/dist/cli.js +307 -174
  4. package/docs/adr/0001-refuse-drift-between-plan-and-apply.md +7 -3
  5. package/docs/adr/0002-shared-config-base-layer.md +5 -1
  6. package/docs/adr/0003-need-content-per-call-boolean.md +49 -0
  7. package/docs/agents/claude-code.md +16 -6
  8. package/docs/agents/codex.md +14 -8
  9. package/docs/agents/domain.md +2 -2
  10. package/docs/agents/issue-tracker.md +5 -5
  11. package/docs/agents/opencode-v2.md +29 -15
  12. package/docs/agents/pi.md +41 -12
  13. package/docs/configuration.md +24 -7
  14. package/docs/design/config-layering-design.md +10 -5
  15. package/docs/design/installer-design.md +57 -25
  16. package/docs/design/search-content-design.md +133 -0
  17. package/docs/integration.md +19 -16
  18. package/docs/research/control-matrix.md +39 -34
  19. package/docs/research/mcp-compatibility.md +3 -1
  20. package/docs/research/ori-question-sourcing.md +31 -20
  21. package/docs/research/result-quality-passthrough.md +9 -7
  22. package/docs/research/search-api-defaults.md +4 -2
  23. package/docs/research/search-inline-content.md +49 -9
  24. package/package.json +3 -2
  25. package/src/core/auth.ts +7 -0
  26. package/src/core/client.ts +98 -27
  27. package/src/core/config-summary.ts +3 -0
  28. package/src/core/constants.ts +30 -2
  29. package/src/core/descriptions.ts +17 -2
  30. package/src/core/env.ts +7 -1
  31. package/src/core/format.ts +35 -5
  32. package/src/core/options.ts +56 -0
  33. package/src/core/search.ts +106 -15
  34. package/src/core/types.ts +29 -0
  35. package/src/installer/apply.ts +4 -4
  36. package/src/installer/cli.ts +2 -2
  37. package/src/installer/config-command.ts +4 -3
  38. package/src/installer/hosts/claude-code.ts +3 -17
  39. package/src/installer/hosts/codex.ts +3 -17
  40. package/src/installer/hosts/index.ts +2 -2
  41. package/src/installer/hosts/opencode.ts +1 -1
  42. package/src/installer/hosts/pi.ts +14 -15
  43. package/src/installer/hosts/shared.ts +23 -0
  44. package/src/installer/ownership.ts +2 -2
  45. package/src/installer/paths.ts +2 -2
  46. package/src/installer/{state.ts → record.ts} +15 -15
  47. package/src/installer/types.ts +1 -1
  48. package/src/installer/uninstall.ts +4 -4
  49. package/src/mcp/serve.ts +1 -2
  50. package/src/mcp/server.ts +19 -19
  51. package/src/opencode/plugin.ts +15 -10
  52. package/src/pi/extension.ts +26 -17
  53. package/src/mcp/auth.ts +0 -11
@@ -43,8 +43,9 @@ already exists, and the installer uses three phases:
43
43
  1. **Detect.** Read the host config, the rules files, and the registered MCP servers.
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
- into rather than replace (`disabledTools`, `permissions.deny`, permissions
47
- `exclude`, `disabled_tools`); and higher layers (Claude Code managed policy — its
46
+ into rather than replace (Cline's `disabledTools`, Claude Code's `permissions.deny`,
47
+ permissions `exclude` arrays, Codex's and Devin's `disabled_tools`); and higher
48
+ layers (Claude Code managed policy — its
48
49
  existence is checked by the claude-code module; Devin enterprise team settings) that
49
50
  would outrank what the installer writes.
50
51
  2. **Merge or snapshot.** Array keys merge with existing entries, recording exactly
@@ -69,22 +70,24 @@ already exists, and the installer uses three phases:
69
70
  conditions; it does not resolve them.
70
71
  - **Forced is conditional**: the exclusion kill switches remove the built-in
71
72
  competitor, not third-party web MCP servers — those must be excluded too (the
72
- per-host keys cover them), or routing is not actually forced. Concretely: Pi's
73
- per-server `enabled` key lets the plan disable detected web-capable competitors
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,
77
- so a detected web-capable competitor makes the plan refuse the tier instead of
78
- promising non-exclusive routing; OpenCode's forced is replacement, which no
79
- third-party server overrides. Detection classifies a server web-capable from its
80
- name plus its command/args/url — a heuristic the plan notes never hide. Forced also
81
- overrides
82
- explicit user choices (OpenCode's `websearch.provider`), and its deny rules affect
83
- workflows beyond this project (a Claude Code deny cannot be carved out by allow
84
- rules), so the plan must state the blast radius before the user approves it.
73
+ per-host keys cover them), or routing is not actually forced.
74
+ - Per-host exclusion of detected web-capable MCP servers: Pi's per-server
75
+ `enabled` key lets the plan disable them (each prior value snapshotted for
76
+ uninstall); Claude Code excludes them the same way via server-level
77
+ `mcp__<server>` deny rules appended to `permissions.deny`; Codex has no
78
+ per-server kill switch, so a detected web-capable competitor makes the plan
79
+ refuse the tier instead of promising non-exclusive routing; OpenCode's forced
80
+ is replacement, which no third-party server overrides.
81
+ - Detection classifies a server web-capable from its name plus its
82
+ command/args/url — a heuristic the plan always discloses.
83
+ - Blast radius: forced also overrides explicit user choices (OpenCode's
84
+ `websearch.provider`), and its deny rules affect workflows beyond this
85
+ project (a Claude Code deny cannot be carved out by allow rules), so the plan
86
+ must state the blast radius before the user approves it.
85
87
  - **Forced is not universally available**: where hosts.jsonc records `forced: "none"`
86
88
  (Cursor's search today), the wizard does not offer the tier — the tier-level analogue
87
- of the scope rule: honor or report, never offer what cannot be delivered. Some kill
89
+ of the scope rule below (honor or report, never silently downgrade: never offer what
90
+ cannot be delivered). Some kill
88
91
  switches are session-scoped (Copilot fetch's `--deny-tool`/`--excluded-tools` flags);
89
92
  an installer cannot persist them, so the plan reports that the exclusion evaporates on
90
93
  restart and offers what persistence exists.
@@ -158,9 +161,33 @@ the artifacts below name the path where they differ.
158
161
  | 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) |
159
162
  | 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) |
160
163
  | Forced (replacement) | OpenCode plugin options (`setDefaultWebsearch` / `fetchOverrideWebfetch`) |
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) |
164
+ | Forced (exclusion) | The built-in competitor's kill switch (per-host keys in the per-host reference), plus exclusion of any other web-capable MCP servers the detect phase found — host by host below |
162
165
  | Forced (de-facto) | Nothing beyond the Optional artifacts — no competitor exists |
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 |
166
+ | Off | A host-native removal of the capability's tool — host by host below |
167
+
168
+ Forced (exclusion), host by host:
169
+
170
+ - **Pi** sets each detected web-capable server's `enabled` key to `false`.
171
+ - **Claude Code** appends server-level `mcp__<server>` deny rules to
172
+ `permissions.deny`, merging with the user's own entries.
173
+ - **Codex**, lacking a per-server kill switch, refuses the tier instead.
174
+ - **Pi's extension path** additionally refuses when a web-capable
175
+ `packages`/`extensions` source is listed in settings.json (no per-package kill
176
+ switch exists).
177
+
178
+ Off, host by host:
179
+
180
+ - **Codex** writes the capability's raw tool name into the server entry's
181
+ `disabled_tools` (host-native: the tool leaves the model's context; the user's
182
+ own entries in that array are kept).
183
+ - **Claude Code** appends the capability's full tool id (`mcp__deepseek__*`) to
184
+ `permissions.deny` — the same host-native removal.
185
+ - **The remaining MCP entry** (Pi's fallback) writes the capability's false-only
186
+ server-side switch instead (`DEEPSEEK_ENABLE_SEARCH` / `DEEPSEEK_ENABLE_FETCH`).
187
+ - **Pi's extension path** writes the same switches into the
188
+ `deepseekWebRetrieval` settings namespace the extension reads at load.
189
+
190
+ Both capabilities off refuses to install.
164
191
 
165
192
  OpenCode additionally appends `permissions` allow entries for exactly the tools it
166
193
  registers (`websearch`, `deepseek_fetch`, and `webfetch` when fetch is forced), merged
@@ -186,10 +213,10 @@ into any existing `permissions` array rather than replacing it.
186
213
  comments and formatting in `opencode.jsonc` and Pi's `mcp.json` survive apply and
187
214
  uninstall; TOML edits go through `smol-toml` and drop comments, which the plan reports
188
215
  as a note. The hand-written JSONC stripper that predated this lived in `scripts/jsonc.ts`.
189
- - Install state lives per host and scope (global under
190
- `~/.config/deepseek-web-retrieval/`, project under `.deepseek-web-retrieval/`); it
191
- records snapshots, appended entries, created files, and created path prefixes — never
192
- full file contents, so other servers' secrets stay out of the state file.
216
+ - Install records live per host and scope (global under
217
+ `~/.config/deepseek-web-retrieval/`, project under `.deepseek-web-retrieval/`); they
218
+ record snapshots, appended entries, created files, and created path prefixes — never
219
+ full file contents, so other servers' secrets stay out of the install record.
193
220
  - Re-applying over a previous init inherits the original snapshots instead of recording
194
221
  the installer's own output as the "previous" value. Pi forced re-inits keep their
195
222
  competitor-disable ops even when those servers are already disabled, so orphan
@@ -256,8 +283,13 @@ edits append arrays. Preview generation is read-only and does not reserve locks.
256
283
 
257
284
  Operation locks are directories ending in `.deepseek-web-retrieval.lock`, with
258
285
  owner PID and start time inside. Ordinary success or failure releases them. A
259
- killed process may leave a lock: after confirming no installer is running, inspect
260
- config, install record, and persistent ownership claims, restore any inconsistent
261
- state, then remove the reported lock directory and generate a new plan. Locks are
286
+ killed process may leave a lock; to recover from one:
287
+
288
+ 1. Confirm no installer is currently running.
289
+ 2. Inspect the config, install record, and persistent ownership claims.
290
+ 3. Restore any inconsistent state.
291
+ 4. Remove the reported lock directory and generate a new plan.
292
+
293
+ Locks are
262
294
  never stolen based only on age or PID. Persistent `.owner.json` claims remain a
263
295
  separate safeguard against two installations owning one shared config.
@@ -0,0 +1,133 @@
1
+ # Search content design
2
+
3
+ Design note for `need_content` — DeepSeek's inline-content search flag — across the two
4
+ model-facing adapters (the MCP server and the Pi extension). It records the parameter
5
+ split (who decides what), the envelope arithmetic that sets the defaults, the rendering
6
+ contract, and the per-adapter composition, so the design survives between conversations.
7
+ The facts it builds on are in [search-inline-content.md](../research/search-inline-content.md)
8
+ (baseline 2026-10-23); the decision record is
9
+ [ADR-0003](../adr/0003-need-content-per-call-boolean.md). This note was verified against
10
+ the code on 2026-10-24.
11
+
12
+ ## Problem
13
+
14
+ The wire parameter is a boolean the repo hardcoded `false`: search results carried a
15
+ summary digest (`summary` falling back to `snippet`) and never a page body. An agent
16
+ needing bodies had to guess which URLs to fetch from the digests — a wrong guess is a
17
+ silent miss. Turning the flag on naively is not safe either: DeepSeek documents **no**
18
+ size bound for the returned `content`, host output caps run as tight as Pi's 20 KB
19
+ MCP-path truncation (middle truncation — whole results vanish), and the 30 s default
20
+ search timeout does not budget for the server-side page crawl the flag triggers.
21
+
22
+ ## The parameter split
23
+
24
+ Two axes, two owners:
25
+
26
+ - **The agent owns the boolean.** `need_content` is a per-call, optional tool parameter
27
+ on every surface where this package defines the search tool's inputs (the MCP server
28
+ and the Pi extension), defaulting to `false`. It rides `SearchRequest.needContent`
29
+ (the `oriQuestion` pattern), not the resolved options — it changes nothing in option
30
+ state, only what that one call sends. Task type is the biggest determinant of whether
31
+ content helps, and only the model knows the task; the repo's other per-call parameters
32
+ (`count`, fetch `maxChars`) are the precedent: always exposed, never mode-gated.
33
+ - **The deployment owns the envelope.** Two deployment-level knobs — never tool
34
+ parameters — apply whenever content flows: `searchContentMaxChars` (per-result body
35
+ budget) and `searchContentTimeoutMs` (the timeout a content call runs on, replacing
36
+ `searchTimeoutMs` for that call; the per-call `timeoutMs` cap still wins if shorter).
37
+ The model cannot see which host caps its output, so it cannot own a budget.
38
+
39
+ ## The envelope
40
+
41
+ Defaults from the host-cap arithmetic in the research note:
42
+
43
+ - `searchContentMaxChars` defaults to **1,500**. At the default `count=10`, bodies plus
44
+ the ~1k chars of per-result metadata land near 16 KB — under the tightest published
45
+ cap (Pi's 20 KB MCP-path truncation) for Latin-script pages. CJK-heavy responses
46
+ measure 3 bytes per character toward that cap, so a CJK-focused deployment on a
47
+ 20 KB host should lower the budget or the count (at 1,500 chars/result, count ≈ 13 is
48
+ the 20 KB ceiling even for Latin text). The native Pi extension bypasses the MCP
49
+ layer entirely — neither its 20 KB truncation nor its 60 s timeout applies — so that
50
+ deployment can afford a larger budget.
51
+ - `searchContentTimeoutMs` defaults to **60,000** — between the 30 s search default the
52
+ digest path keeps and the 300 s ceiling Codex allows, because the API must fetch
53
+ every result page server-side before responding and documents no latency statement.
54
+
55
+ The total a response can carry is budget × count plus metadata. That product is the
56
+ deployment's arithmetic to run when tuning either knob; the core does not narrow any
57
+ parameter's range at runtime (see Decisions).
58
+
59
+ ## Per-adapter composition
60
+
61
+ - **MCP server**: `need_content` in the zod input schema, forwarded as
62
+ `needContent: need_content === true`.
63
+ - **Pi extension**: the same parameter in the TypeBox schema (`SEARCH_PARAMETERS` stays
64
+ a module-level constant on both surfaces — the schema is unconditional), forwarded the
65
+ same way.
66
+ - **OpenCode**: no change. The host fixes the `websearch` provider input to `query`
67
+ (the upstream gap that already blocks per-call `count`), so the provider stays
68
+ summary-only; the envelope knobs resolve through the shared pipeline and are dormant
69
+ there. Documented next to the count gap in
70
+ [opencode-v2.md](../agents/opencode-v2.md).
71
+ - **Installer**: no artifacts change. The knob defaults are global and the deviation
72
+ lines on every adapter already report non-default values through the config
73
+ vocabulary.
74
+
75
+ ## Processing and rendering
76
+
77
+ `processResults` is mode-agnostic: a present, non-empty `content` **replaces** the
78
+ digest (the summary is a proxy for the body it compresses), clamped to
79
+ `searchContentMaxChars` with an ellipsis marker when shortened; an empty, whitespace,
80
+ or missing body — the API documents per-result absence, and `need_content: false`
81
+ responses carry an empty string — falls back to the digest path. `searchContentMaxChars:
82
+ 0` treats the body as absent. Each result carries `contentKind: "page" | "digest"` so
83
+ the renderer knows the source: page bodies render as Markdown indented into the list
84
+ item (heading and list structure survive); digests stay compacted to one line, as
85
+ summaries always were. The `structuredContent` mirror carries the same `content`
86
+ string on hosts that read it.
87
+
88
+ ## What this design does not change
89
+
90
+ - Billing behavior: Search bills per successful request; `need_content` is not a price
91
+ modifier, so a content call costs the same as a digest call (and can replace fetch
92
+ calls at the same per-request price).
93
+ - The fetch tool, its `maxChars` parameter, and the two-tool architecture — a content
94
+ call replaces *some* fetches, not the deep-read path.
95
+ - `maxSummaryChars`, thresholds, dedup, query composition, and degradation.
96
+ - `MAX_RESPONSE_BYTES` (5 MiB) — still the outer guard, still erroring rather than
97
+ truncating; the per-result budget keeps responses far below it.
98
+
99
+ ## Testing
100
+
101
+ Wire assertions on both model-facing adapters (`need_content: true` reaching the wire;
102
+ `false` by default), client tests for the flag and the timeout selection
103
+ (`contentTimeoutMs` used only when the call asks for content), `processResults` tests
104
+ for the body-over-digest precedence, the budget clamp and marker, the empty-body
105
+ fallback, and the zero-budget drop, and format tests for the two rendering shapes.
106
+
107
+ ## Rollout
108
+
109
+ Shipped behind default-`false` behavior: with no configuration and no parameter use,
110
+ every surface behaves exactly as before (the wire still says `need_content: false`).
111
+ Deployments opt in per call by the model, per host by the two envelope knobs. No
112
+ installer or shared-file migration; the knobs join the config vocabulary and the
113
+ generated knob table.
114
+
115
+ ## Decisions (confirmed 2026-10-24)
116
+
117
+ 1. The boolean is a per-call tool parameter, always exposed on the MCP server and the
118
+ Pi extension; no config mode gates it.
119
+ 2. The envelope is deployment-only: `searchContentMaxChars` (1,500) and
120
+ `searchContentTimeoutMs` (60,000), never tool parameters.
121
+ 3. A page body replaces the digest; empty bodies fall back to it.
122
+ 4. `need_content` does not control other parameters — neither their availability nor
123
+ their value ranges. Effects may be conditional (the envelope knobs apply only when
124
+ content flows); domains are static. No surveyed vendor couples a content flag to
125
+ another parameter's range ([search-inline-content.md](../research/search-inline-content.md)),
126
+ and a silent runtime narrowing (say, of `count`) would be a decision the model
127
+ cannot see. The budget × count arithmetic is the deployment's to run.
128
+ 5. OpenCode stays summary-only until the upstream provider input widens.
129
+ 6. The descriptions state facts — mechanics, latency, price, magnitude, the fetch
130
+ alternative — and no usage policy. When content is worth it is the model's per-call
131
+ judgment (or a user's rules file per deployment), not the tool author's; if usage
132
+ data later shows the parameter systematically ignored or overused, that is the point
133
+ to add minimal steering, decided by evidence.
@@ -7,7 +7,8 @@ use it depends on that agent's extension points.
7
7
  CLI/terminal agents only; IDE and editor extension surfaces are out of scope. Versions are
8
8
  the latest stable CLI release checked at that baseline unless noted otherwise. `No` means no
9
9
  documented native provider or override was found in the checked version; several hosts still
10
- ship built-in web tools. MCP tools can add equivalent tools, but do not replace built-ins.
10
+ ship built-in web tools. MCP (Model Context Protocol) tools can add equivalent tools, but
11
+ do not replace built-ins.
11
12
 
12
13
  ## Mechanisms
13
14
 
@@ -20,8 +21,12 @@ ship built-in web tools. MCP tools can add equivalent tools, but do not replace
20
21
  | MCP server | Yes | Yes | Host MCP client | Shipped (`src/mcp/`; Pi, Codex, and Claude Code guides) |
21
22
  | CLI/HTTP wrapper | Yes | Yes | Host can call external tools | Not shipped |
22
23
 
23
- MCP is a protocol, not an agent. It is one possible transport for hosts that support MCP
24
- tools. `Custom tool plugin` means adding tools; it does not replace a host's built-in tools.
24
+ Why the mechanism distinction matters: some agents expose a native websearch provider,
25
+ some only let you add tools, and some only support MCP. A "yes" for one mechanism does
26
+ not mean the others are supported — the adapter code and docs need to match the
27
+ mechanism the host actually provides. MCP is a protocol, not an agent: one possible
28
+ transport for hosts that support MCP tools, never a host itself. `Custom tool plugin`
29
+ means adding tools; it does not replace a host's built-ins.
25
30
 
26
31
  ## Host support
27
32
 
@@ -96,12 +101,13 @@ For an installation managed by `init`:
96
101
  needed for uninstall. A global install can still claim shared configuration from a
97
102
  particular project, so keep the original working directory for upgrades.
98
103
  3. Refresh the package in the host and start or reload a session as described in its
99
- guide. For Pi's extension path, use `pi update --extensions`. To pin the host's
100
- package to the exact version being run, pass `--pin` to init (it writes
101
- `deepseek-web-retrieval@X.Y.Z` into the registration); upgrading a pinned install
102
- means re-running init with the newer CLI, since updating the CLI alone does not
103
- move an independently pinned host package. `--package` still overrides the ref
104
- entirely.
104
+ guide (for Pi's extension path, `pi update --extensions`).
105
+
106
+ Two init flags change what gets registered. `--pin` pins the host's package to the exact
107
+ version being run (`deepseek-web-retrieval@X.Y.Z` in the registration); upgrading a
108
+ pinned install therefore means re-running init with the newer CLI, since updating the
109
+ CLI alone does not move an independently pinned host package. `--package` overrides the
110
+ ref entirely.
105
111
 
106
112
  When upgrading from 0.4.0, Pi's default changes from MCP to the native extension.
107
113
  Pass `--path mcp` explicitly to retain MCP, or `--path extension` to migrate; the plan
@@ -109,12 +115,15 @@ shows the old path's removals and retains the original uninstall snapshots. Open
109
115
  native plugin and Codex's MCP path remain the same. Standalone MCP installs no longer
110
116
  require OpenCode or Pi SDKs; native adapters use the SDKs provided by their hosts.
111
117
 
112
- Keep install records and ownership sidecars: deleting them loses recovery information
118
+ Keep install records and ownership sidecars (the `.owner.json` claim files the installer
119
+ writes beside shared config files): deleting them loses recovery information
113
120
  and can cause a later init to refuse an existing registration. If an operation was
114
121
  interrupted, follow the [recovery guidance](design/installer-design.md#persistence-and-concurrent-operations)
115
122
  before retrying. For manual installations, update the package reference or checkout and
116
123
  follow the host guide rather than adopting the config through init.
117
124
 
125
+ ### Breaking changes for library consumers
126
+
118
127
  Code importing the core's removed `formatFetchContent` helper must migrate to
119
128
  `formatFetchedContent`, which returns both `content` and `truncated`.
120
129
 
@@ -178,12 +187,6 @@ the README Usage section only after the adapter ships. To request evaluation for
178
187
  host, [open an issue](https://github.com/gwokhou/deepseek-web-retrieval/issues) with links
179
188
  to its plugin, tool, or MCP documentation.
180
189
 
181
- ## Why the distinction matters
182
-
183
- Some agents expose a native websearch provider, some only let you add tools, and some only
184
- support MCP. A "yes" for one mechanism does not mean the others are supported. The adapter
185
- code and docs need to match the mechanism the host actually provides.
186
-
187
190
  ## Adding an adapter
188
191
 
189
192
  - Register the host and its version lock in [hosts.jsonc](hosts.jsonc), then regenerate
@@ -113,30 +113,31 @@ scopes; keys locked to a single scope are enumerated in the
113
113
  `web_search = "disabled"` in `~/.codex/config.toml`. Trap: the `[tools] web_search =
114
114
  <bool>` form parses but discards the value at 0.163.0 — a silent no-op
115
115
  ([config source at rust-v0.163.0](https://github.com/openai/codex/blob/rust-v0.163.0/codex-rs/config/src/config_toml.rs)).
116
- - **MCP registration** — `~/.codex/config.toml` `[mcp_servers.<name>]`: stdio
117
- `command`/`args`/`env`/`env_vars`; `startup_timeout_sec` (30 s) and `tool_timeout_sec`
118
- (300 s) defaults — the `DEFAULT_STARTUP_TIMEOUT`/`DEFAULT_TOOL_TIMEOUT` constants
119
- ([rmcp_client.rs at rust-v0.163.0](https://github.com/openai/codex/blob/rust-v0.163.0/codex-rs/codex-mcp/src/rmcp_client.rs));
120
- `enabled_tools`/`disabled_tools` (verbatim raw tool-name match, filtered before
121
- registration so the model never sees a disabled tool; an empty `enabled_tools` allow-list
122
- exposes nothing), per-tool approval modes, and output limits: MCP tool output can be
123
- capped per tool via `output_token_limit` ("Token budget for one MCP tool's output,
124
- before the standard 20% serialization allowance"; the default cap is not published
125
- there)
126
- ([mcp_types.rs at rust-v0.163.0](https://github.com/openai/codex/blob/rust-v0.163.0/codex-rs/config/src/mcp_types.rs)).
127
- stdio children do not inherit the parent environment (only `HOME`, `PATH`, and explicit
128
- `env`), so `DEEPSEEK_API_KEY` must be passed explicitly. When a tool result has
129
- `structuredContent`, Codex replaces the content blocks with the serialized JSON. No SSE.
130
- Codex identifies itself in the initialize handshake as clientInfo name `codex-mcp-client`
131
- with title `Codex`
132
- ([rmcp_client.rs at rust-v0.163.0](https://github.com/openai/codex/blob/rust-v0.163.0/codex-rs/codex-mcp/src/rmcp_client.rs)).
133
- The official docs moved to learn.chatgpt.com
134
- ([MCP](https://learn.chatgpt.com/docs/extend/mcp),
135
- [config reference](https://learn.chatgpt.com/docs/config-file/config-reference); the old
136
- developers.openai.com URLs 308-redirect there) and now document every key above — but
137
- state 10 s/60 s timeout defaults, which describe an older release line; the 30 s/300 s
138
- constants above are the facts at the locked 0.163.0 (docs discrepancy re-verified
139
- 2026-10-21).
116
+ - **MCP registration** — `~/.codex/config.toml` `[mcp_servers.<name>]`:
117
+ - **Entry keys:** stdio `command`/`args`/`env`/`env_vars`; per-tool approval modes.
118
+ - **Timeouts:** `startup_timeout_sec` (30 s) and `tool_timeout_sec` (300 s) defaults —
119
+ the `DEFAULT_STARTUP_TIMEOUT`/`DEFAULT_TOOL_TIMEOUT` constants
120
+ ([rmcp_client.rs at rust-v0.163.0](https://github.com/openai/codex/blob/rust-v0.163.0/codex-rs/codex-mcp/src/rmcp_client.rs)).
121
+ - **Tool filtering:** `enabled_tools`/`disabled_tools` — verbatim raw tool-name match,
122
+ filtered before registration so the model never sees a disabled tool; an empty
123
+ `enabled_tools` allow-list exposes nothing.
124
+ - **Output limits:** MCP tool output can be capped per tool via `output_token_limit`
125
+ ("Token budget for one MCP tool's output, before the standard 20% serialization
126
+ allowance"; the default cap is not published there)
127
+ ([mcp_types.rs at rust-v0.163.0](https://github.com/openai/codex/blob/rust-v0.163.0/codex-rs/config/src/mcp_types.rs)).
128
+ - **Environment:** stdio children do not inherit the parent environment (only `HOME`,
129
+ `PATH`, and explicit `env`), so `DEEPSEEK_API_KEY` must be passed explicitly.
130
+ - **Result shape and identity:** when a tool result has `structuredContent`, Codex
131
+ replaces the content blocks with the serialized JSON. No SSE. Codex identifies itself
132
+ in the initialize handshake as clientInfo name `codex-mcp-client` with title `Codex`
133
+ ([rmcp_client.rs at rust-v0.163.0](https://github.com/openai/codex/blob/rust-v0.163.0/codex-rs/codex-mcp/src/rmcp_client.rs)).
134
+ - **Docs drift:** the official docs moved to learn.chatgpt.com
135
+ ([MCP](https://learn.chatgpt.com/docs/extend/mcp),
136
+ [config reference](https://learn.chatgpt.com/docs/config-file/config-reference); the
137
+ old developers.openai.com URLs 308-redirect there) and now document every key above —
138
+ but state 10 s/60 s timeout defaults, which describe an older release line; the
139
+ 30 s/300 s constants above are the facts at the locked 0.163.0 (docs discrepancy
140
+ re-verified 2026-10-21).
140
141
  - **Rules** — `~/.codex/AGENTS.md` (with `AGENTS.override.md` preferred) at user scope;
141
142
  every `AGENTS.md` from the project root down to the cwd, concatenated root→cwd.
142
143
  Extensible via `project_doc_fallback_filenames` and `developer_instructions`
@@ -184,8 +185,10 @@ scopes; keys locked to a single scope are enumerated in the
184
185
  channel: the runtime config surface is the `DEEPSEEK_*` environment (the same pipeline as
185
186
  the MCP server), the shared config file
186
187
  (`~/.config/deepseek-web-retrieval/config.json`, layered under the environment), plus a
187
- `deepseekWebRetrieval` settings namespace the installer writes for
188
- the off tier — Pi preserves unknown settings keys on read and on its own rewrites
188
+ `deepseekWebRetrieval` settings namespace the installer writes for the `off` tier (a
189
+ capability switched off — the [tier-to-artifact
190
+ mapping](../design/installer-design.md#tier-to-artifact-mapping) defines it) — Pi
191
+ preserves unknown settings keys on read and on its own rewrites
189
192
  (`pi install`), verified at 1.3.0. Action methods such as `getSettings()` cannot be
190
193
  called during extension loading, so the adapter reads the settings files itself.
191
194
  - **MCP registration** — `~/.pi/agent/mcp.json` or `.pi/mcp.json`: stdio
@@ -320,11 +323,13 @@ scopes; keys locked to a single scope are enumerated in the
320
323
  `toolPolicies`). Codex's docs location and Copilot's `GEMINI.md` dating were re-verified
321
324
  2026-10-21 and are settled in their sections above. Protocol-revision evidence for every
322
325
  host lives in [mcp-compatibility.md](mcp-compatibility.md).
323
- - **Claude Code forward-compat anchors (checked at the 2.1.303 lock):** the headless docs
324
- pre-announce `--bare` becoming the default for `-p`, and bare mode never reads
325
- `.mcp.json` — the billed e2e scenario and the project-scope install story both need
326
- re-checking when that lands. `${VAR}` expansion in the `env` block of a user-scope
327
- `~/.claude.json` entry is supported but only indirectly documented (the docs spell out
328
- `.mcp.json` and `command`/`args`; the e2e's `hasCompletedOnboarding` flag and the
329
- redundant `enableAllProjectMcpServers` belt-and-suspenders are undocumented), so a
330
- baseline bump should re-verify all three against the then-current docs.
326
+ - **Claude Code forward-compat anchors (checked at the 2.1.303 lock)** — three facts a
327
+ baseline bump should re-verify against the then-current docs:
328
+ 1. The headless docs pre-announce `--bare` becoming the default for `-p`, and bare mode
329
+ never reads `.mcp.json` — the billed `claude-code` e2e scenario in this repo's test
330
+ suite and the project-scope install story both need re-checking when that lands.
331
+ 2. `${VAR}` expansion in the `env` block of a user-scope `~/.claude.json` entry is
332
+ supported but only indirectly documented (the docs spell out `.mcp.json` and
333
+ `command`/`args`).
334
+ 3. The same e2e scenario's `hasCompletedOnboarding` flag and the redundant
335
+ `enableAllProjectMcpServers` belt-and-suspenders are undocumented.
@@ -127,7 +127,9 @@ differences:
127
127
  spec says clients SHOULD support it whenever possible. Legacy SSE is never required —
128
128
  Codex and Pi reject it anyway.
129
129
  3. **Build on the official SDK v2 dual-era serving.** One build then serves the full range:
130
- the `2024-11-05` floor set by Cline (v2 legacy support spans `2024-10-07`–`2025-11-25`)
130
+ the `2024-11-05` floor set by Cline (v2 legacy support spans `2024-10-07`–`2025-11-25`;
131
+ `2024-10-07` is the pre-1.0 draft version string the SDK accepts for legacy
132
+ compatibility — it predates the spec's initial `2024-11-05` revision)
131
133
  up to the `2026-07-28` negotiators (Claude Code 2.1.274+, Devin 3000.11.92, Copilot CLI
132
134
  1.0.81+). A v1.x-built server still works today — modern clients fall back to the legacy
133
135
  handshake — but it can never offer the stateless era; v2 serves both from one entry
@@ -54,7 +54,7 @@ MCP never carries conversation context to a server. The facts, per revision:
54
54
  human-reviewed
55
55
  ([sampling](https://modelcontextprotocol.io/specification/2025-11-25/client/sampling)). A server
56
56
  could only ask a model to *guess* intent — not obtain the user's question.
57
- - **Elicititation is interactive, not derivation.** `elicitation/create` asks the user a form/choice
57
+ - **Elicitation is interactive, not derivation.** `elicitation/create` asks the user a form/choice
58
58
  question and waits ([2025-06-18+](https://modelcontextprotocol.io/specification/2025-06-18/client/elicitation));
59
59
  it requires client capability (only Codex in this matrix has it, per
60
60
  [mcp-compatibility.md](mcp-compatibility.md)) and an explicit round trip.
@@ -170,26 +170,25 @@ tag (commit `843a6c5`); the `opencode-ai/opencode` repository's tags stop at `v0
170
170
  only heuristic correlation via the session hooks (which fire per model request with the
171
171
  sessionID, but no API contract binds a provider call to a session).
172
172
 
173
+ Post-baseline drift check (2026-10-24): the interface is unchanged at 2.1.4, the latest npm
174
+ release on that date (unpkg dist of `@opencode/schema`/`@opencode/plugin` 2.1.4:
175
+ `ProviderInput = Pick<Input, "query">`, provider context `{ signal }`). Upstream requests
176
+ filed the same day to widen this surface:
177
+ [anomalyco/opencode#58635](https://github.com/anomalyco/opencode/issues/58635) (session
178
+ linkage in the provider execute context) and
179
+ [#58636](https://github.com/anomalyco/opencode/issues/58636) (optional per-call `count`
180
+ passthrough) — re-check both when the baseline or the OpenCode lock bumps.
181
+
173
182
  Verdict: **tool path (`deepseek_fetch` / the `webfetch` override): A feasible (validated against
174
183
  the declared schema; raw JSON Schema forwarded verbatim) and B feasible
175
184
  (`ctx.session.context`). Websearch provider path (the search replacement this repo ships): neither
176
185
  — the input schema is host-fixed to `query`, and the provider context has no session handle.**
177
186
 
178
- ### Cline 0.0.13
179
-
180
- Closed source; the platform binary
181
- ([@cline/cli-linux-x64 0.0.13](https://registry.npmjs.org/@cline/cli-linux-x64/0.0.13), ~119 MB)
182
- yielded the protocol facts already recorded — custom client negotiating `2024-11-05`,
183
- text-content-only results ([mcp-compatibility.md](mcp-compatibility.md)). No primary source
184
- documents whether the client validates tool arguments against `inputSchema` or mangles schemas;
185
- strings cannot prove a runtime behavior either way, so this is **undeterminable at the lock**. The
186
- 2024-11-05 revision itself puts no validation obligation on clients and defines `arguments` as a
187
- free-form object, so there is no protocol-level obstacle to channel A — merely no evidence for or
188
- against host behavior. Channel B: none (MCP only, `paths` in hosts.jsonc).
189
-
190
187
  ## Proprietary hosts
191
188
 
192
- No proprietary host documents a context channel; the question is whether anything documents
189
+ Closed-source hosts, from binary analysis (Cline) and official docs/changelogs (the
190
+ rest). No proprietary host documents a context
191
+ channel; the question is whether anything documents
193
192
  input-schema handling that would break an optional parameter.
194
193
 
195
194
  - **Claude Code 2.1.303.** The [MCP docs](https://code.claude.com/docs/en/mcp) say nothing about
@@ -203,6 +202,16 @@ input-schema handling that would break an optional parameter.
203
202
  preventing calling MCP tools that have nested references in their input schemas". Together these
204
203
  show the server's `inputSchema` is what the model provider sees and that arguments follow it;
205
204
  no host-side validation is ever mentioned. **A feasible by this evidence; B none.**
205
+ - **Cline 0.0.13.** Closed source; the platform binary
206
+ ([@cline/cli-linux-x64 0.0.13](https://registry.npmjs.org/@cline/cli-linux-x64/0.0.13), ~119 MB)
207
+ yielded the protocol facts already recorded — custom client negotiating `2024-11-05`,
208
+ text-content-only results ([mcp-compatibility.md](mcp-compatibility.md)). No primary
209
+ source documents whether the client validates tool arguments against `inputSchema` or
210
+ mangles schemas; strings cannot prove a runtime behavior either way, so this is
211
+ **undeterminable at the lock**. The 2024-11-05 revision itself puts no validation
212
+ obligation on clients and defines `arguments` as a free-form object, so there is no
213
+ protocol-level obstacle to channel A — merely no evidence for or against host behavior.
214
+ Channel B: none (MCP only, `paths` in hosts.jsonc).
206
215
  - **Cursor 2026.10.08.** The [MCP docs](https://cursor.com/docs/mcp) describe tools only as
207
216
  "Functions for the AI model to execute" and say nothing about input schemas, validation, or
208
217
  context — and the protocol revision itself is unpinnable from primary sources
@@ -256,7 +265,9 @@ negotiated revision in the matrix.
256
265
  `ctx.session.context({ sessionID })` — user messages with their `text` — at call time, so
257
266
  channel B exists here without touching schemas. The `websearch` provider replacement — the path
258
267
  this repo uses for search — has neither channel at 2.0.32: its model-facing input schema is
259
- fixed in host source to `query`, and its provider context is `{ signal }` only. Note also that
268
+ fixed in host source to `query`, and its provider context is `{ signal }` only (upstream
269
+ requests: [#58635](https://github.com/anomalyco/opencode/issues/58635),
270
+ [#58636](https://github.com/anomalyco/opencode/issues/58636)). Note also that
260
271
  `FETCH_INPUT` sets `additionalProperties: false`, which matters if a parameter is ever added on
261
272
  channel A: an undeclared field would be rejected by OpenCode's runtime validation with a
262
273
  retryable error.
@@ -282,7 +293,7 @@ negotiated revision in the matrix.
282
293
  [2025-11-25 tasks (experimental)](https://modelcontextprotocol.io/specification/2025-11-25/basic/utilities/tasks)
283
294
  - [2026-07-28 overview (`_meta`, statelessness)](https://modelcontextprotocol.io/specification/2026-07-28/basic/index)
284
295
 
285
- ### Open-source hosts
296
+ ### Open-source host sources
286
297
 
287
298
  - Codex: [rust-v0.163.0 tag](https://github.com/openai/codex/tree/rust-v0.163.0) —
288
299
  [mcp_tool_call.rs (argument parsing, no validation)](https://github.com/openai/codex/blob/rust-v0.163.0/codex-rs/core/src/mcp_tool_call.rs),
@@ -313,15 +324,15 @@ negotiated revision in the matrix.
313
324
  (Tool.Context, WebSearch.ProviderInput),
314
325
  [@opencode/client](https://registry.npmjs.org/@opencode/client/2.0.32)
315
326
  (session.context → SessionMessageInfo[], SessionMessageUser.text)
316
- - Cline: [@cline/cli-linux-x64 0.0.13](https://registry.npmjs.org/@cline/cli-linux-x64/0.0.13)
317
- (manifest only; behavior undeterminable) and prior binary analysis recorded in
318
- [mcp-compatibility.md](mcp-compatibility.md)
319
327
 
320
- ### Proprietary hosts
328
+ ### Proprietary host sources
321
329
 
322
330
  - Claude Code: [changelog](https://raw.githubusercontent.com/anthropics/claude-code/main/CHANGELOG.md)
323
331
  (entries 2.0.50, 2.1.30, 2.1.246, 2.1.300 — all ≤ the 2.1.303 lock),
324
332
  [MCP docs](https://code.claude.com/docs/en/mcp)
333
+ - Cline: [@cline/cli-linux-x64 0.0.13](https://registry.npmjs.org/@cline/cli-linux-x64/0.0.13)
334
+ (manifest only; behavior undeterminable) and prior binary analysis recorded in
335
+ [mcp-compatibility.md](mcp-compatibility.md)
325
336
  - Cursor: [MCP docs](https://cursor.com/docs/mcp) (silent on the question)
326
337
  - Devin: [MCP overview](https://docs.devin.ai/cli/extensibility/mcp/overview),
327
338
  [changelog](https://docs.devin.ai/cli/changelog) (silent on the question)
@@ -5,8 +5,8 @@ This note answers one question: through which channel can DeepSeek's per-result
5
5
  (string|null) — reach the **model** (not the human UI) on each host at its locked version? The
6
6
  repo filters on the first two in `src/core/search.ts` (thresholds default to off); at the
7
7
  research baseline it dropped all three before returning — the adapters now pass them through
8
- as a text marker plus a structured mirror (item 1 below is implemented as
9
- `formatQualityMarker`).
8
+ as a text marker plus a structured mirror (the text marker of *What this means for this
9
+ repo*, item 1, is implemented as `formatQualityMarker`).
10
10
  Related: [ori-question-sourcing.md](ori-question-sourcing.md) (input side),
11
11
  [search-inline-content.md](search-inline-content.md) (content volume).
12
12
 
@@ -47,7 +47,8 @@ The server-side validator (`@modelcontextprotocol/server` 2.3.1,
47
47
  `Output validation error: Tool <name> has an output schema but no structured content was
48
48
  provided` (JSON-RPC `InvalidParams`). Text-only results are not allowed for schema'd tools.
49
49
  - `structuredContent` is then validated against the schema; a mismatch is rejected the same way.
50
- - The v2 codec's SEP-2106 §4.3 auto-append (`appendTextFallbackForNonObject`) adds a
50
+ - The v2 codec's SEP-2106 §4.3 auto-append (so named in the installed SDK's own source;
51
+ implemented as `appendTextFallbackForNonObject`) adds a
51
52
  `JSON.stringify(structuredContent)` TextContent block **only when `structuredContent` is a
52
53
  non-object** (primitive/array/null) and no text block exists. Object-shaped
53
54
  `structuredContent` gets **no** automatic text mirror — the server must mirror it itself to
@@ -108,7 +109,8 @@ From the official repo at tag `v2.0.32` (github.com/anomalyco/opencode, where `@
108
109
  - Every tool result then passes through `ToolOutput.truncate`:
109
110
  **`MAX_LINES = 2000`, `MAX_BYTES = 50 KiB`** (full output retained to a `tool-output/`
110
111
  file for 7 days) — [`packages/core/src/tool-output.ts`](https://github.com/anomalyco/opencode/blob/v2.0.32/packages/core/src/tool-output.ts).
111
- These caps were not previously recorded in [control-matrix.md](control-matrix.md).
112
+ These caps are also recorded in [control-matrix.md](control-matrix.md#opencode)'s
113
+ OpenCode section.
112
114
 
113
115
  ## What this means for this repo
114
116
 
@@ -128,9 +130,9 @@ From the official repo at tag `v2.0.32` (github.com/anomalyco/opencode, where `@
128
130
  The Pi adapter's extension `outputSchema` is safe as-is — Pi re-declares but does not
129
131
  validate it, and its `structuredContent` is event data.
130
132
  4. **New cap facts for the design margin:** OpenCode plugin results truncate at 2000 lines /
131
- 50 KiB (new), Pi extension tools bypass the MCP 20 KB truncation (already known), Claude
132
- Code 25k tokens, Codex per-tool `output_token_limit` (default unpublished). None of these
133
- bind at search-result volumes; they matter if [inline content](search-inline-content.md) is
133
+ 50 KiB, Pi extension tools bypass the MCP 20 KB truncation, Claude Code caps output at
134
+ 25k tokens, and Codex applies its per-tool `output_token_limit` (default unpublished).
135
+ None of these bind at search-result volumes; they matter if [inline content](search-inline-content.md) is
134
136
  ever enabled.
135
137
 
136
138
  ## Sources
@@ -159,7 +159,8 @@ the current official docs** (checked 2026-10-21), nor in the November 2025 snaps
159
159
  page on the Internet Archive; the published truncation mechanism is the token-based
160
160
  `max_content_tokens` parameter above, with no published default.
161
161
 
162
- SDK layer (official repos, default branch):
162
+ SDK layer (official repos, default branch at the 2026-10-21 check — moving heads, unlike
163
+ the pinned firecrawl 4.50.0 above; re-pin to a tag before citing these constants later):
163
164
 
164
165
  | SDK | Default timeout | Default max retries | Source |
165
166
  | --- | --- | --- | --- |
@@ -180,7 +181,8 @@ From the official guide
180
181
 
181
182
  No relevance/authority score or filter is exposed on any web-search surface.
182
183
 
183
- SDK layer (official repos, default branch):
184
+ SDK layer (official repos, default branch at the 2026-10-21 check — same moving-head
185
+ caveat as the Anthropic SDK table above):
184
186
 
185
187
  | SDK | Default timeout | Default max retries | Source |
186
188
  | --- | --- | --- | --- |