qwen-code/packages/core/src
易良 18373fb963
Expose MCP resource read tool (#5781)
* feat(core): expose MCP resource read tool

* refactor(core): share MCP resource content formatter between @ path and read tool

read_mcp_resource returned the raw `resources/read` JSON envelope as llmContent:
no text/blob size caps, base64 blobs inlined as raw text, and no attribution
boundary — re-introducing the uncapped behaviour the `@server:uri` path was
specifically hardened against.

Extract that path's formatter into a shared core helper
(formatMcpResourceContents / summarizeMcpResource): caps text and CUMULATIVE
blob payload, promotes blobs to inlineData media parts, and frames content with
attribution delimiters. Both the `@` injection path and the tool now use it, so
they can't drift.

Also switch the tool to Kind.Fetch (Kind.Other is excluded from
CONCURRENCY_SAFE_KINDS, needlessly serializing parallel reads), drop a dead
`?? ''`, and add blob/empty/cap and error-path tests.

* fix(core): address review feedback on read_mcp_resource tool

Fix red CI and close the review gaps vs. the web_fetch precedent.

- i18n: add ReadMcpResource toolDisplayName to zh/zh-TW/en (fixes the
  failing "zh translation for every core tool" test)
- defer the tool like web_fetch (shouldDefer + search hint) so its schema
  is not shipped on every turn; discovered via tool_search
- expose server_name/uri to the AUTO-mode classifier; add debug breadcrumbs
- override maxOutputChars so the scheduler's ~25K truncation can't slice
  the framed output mid-frame (parity with the @ path and ReadFile)
- per-turn cumulative blob budget shared across parallel calls (WeakMap
  keyed by the turn's AbortSignal) to bound total injected base64
- nonce-attributed frame delimiters so a hostile server can't forge the
  closing marker; surface truncation in the closing delimiter
- unify the empty-read diagnostic across the @ path and the tool path
- register read_mcp_resource in COMPACTABLE_TOOLS and permission rule
  aliases (TOOL_NAME_ALIASES + CANONICAL_TO_RULE_DISPLAY)
- bound uri length; align copyright headers; expand formatter test coverage

* fix(core): gate read_mcp_resource behind per-server trust confirmation

Address a follow-up /qreview pass on the read tool.

- [Critical] the tool inherited the base getDefaultPermission() -> 'allow',
  so a model-initiated read auto-executed against any configured MCP server
  with no confirmation. Override getDefaultPermission()/getConfirmationDetails
  on the invocation to mirror DiscoveredMCPTool: 'allow' only when the target
  server is trust:true in a trusted folder, otherwise 'ask' (with a
  persistable ReadMcpResource rule). The user-initiated @server:uri path is
  unaffected.
- fix the truncation notice: it hardcoded the text-char cap but `truncated`
  is also set by a skipped blob, so a blob-only truncation showed a wrong
  "truncated at N chars" message. Make the notice cause-agnostic.
- bound server_name length (symmetric with uri); both are reflected into the
  frame delimiters.
- test the empty @-read injects its diagnostic into processedQuery.

* fix(core): scope read_mcp_resource permission per server; exempt output truncation

Two follow-up /qreview Criticals:

- The "always allow" rule was a blanket `ReadMcpResource`, so approving one
  read authorized every configured server. Scope it per server:
  `ReadMcpResource(<server_name>)`, and extract `server_name` as the literal
  permission specifier in buildPermissionCheckContext (same mechanism Skill /
  subagent_type already use), so the persisted rule matches that server only.
- maxOutputChars was MAX_MCP_RESOURCE_TEXT_CHARS + 2_000, but the delimiters
  embed the label (server_name:uri, each up to ~5K), so a long label could
  exceed the budget and let the scheduler slice the nonce-framed output
  mid-content. Return Infinity — the formatter already self-caps text and
  blobs, like ReadFile.

* test(core): harden read_mcp_resource coverage

- assert empty-read returnDisplay carries the 'Read resource' prefix
- add unconfigured server_name -> getDefaultPermission 'ask' (per-server trust gate)
- assert the budget-exhausted call surfaces '(content too large — skipped)'
  in llmContent + returnDisplay, not silent-empty content

* fix(core): exempt read_mcp_resource from tool-output persistence gate

The maybePersistLargeToolResult gate fires above ~28k chars and is keyed by
tool name (GATE_EXEMPT_TOOLS), running before read_mcp_resource's
maxOutputChars=Infinity self-cap. A 28k-100k resource was spilled to a stub
and the model never saw the body. Add read_mcp_resource to the exempt set
(and switch the set to ToolNames constants to avoid drift).
2026-06-24 10:11:51 +00:00
..
__mocks__/fs pre-release commit 2025-07-22 23:26:01 +08:00
agents fix(agent): cap fork turns and bubble fork permission prompts (#5737) 2026-06-23 19:18:01 +08:00
config Expose MCP resource read tool (#5781) 2026-06-24 10:11:51 +00:00
confirmation-bus fix(core): stop AbortSignal listener leak in long sessions (MaxListenersExceededWarning) (#4366) 2026-05-26 14:21:49 +08:00
core Expose MCP resource read tool (#5781) 2026-06-24 10:11:51 +00:00
extension fix(cli): correctly map Claude MCP server transport types on import and in .mcp.json (#5812) 2026-06-24 17:49:02 +08:00
followup fix(core): remove redundant reportSuggestionUsage causing double-counted stats (#5684) 2026-06-23 10:42:46 +08:00
goals fix(goal): persist iteration count across resume so MAX_GOAL_ITERATIONS bounds the whole session (#5000) 2026-06-12 13:10:06 +08:00
hooks fix(core): require integer stop hook cap (#5667) 2026-06-24 07:00:35 +08:00
ide feat(daemon): merge daemon-mode feature batch into main (#4490) 2026-06-12 00:34:49 +08:00
lsp fix(core): validate lsp socket ports (#5493) 2026-06-21 01:09:57 +08:00
mcp fix(core): Align MCP OAuth guidance and docs (#5589) 2026-06-24 07:09:53 +08:00
memory refactor(cli): rename serve files to kebab-case (#5592) 2026-06-22 13:13:07 +08:00
mocks chore(usage telemetry): Freshen up Clearcut logging (#6013) 2025-08-12 18:31:59 +00:00
models fix(core): Disambiguate duplicate model display names (#5769) 2026-06-23 20:01:49 +08:00
output chore(core): drop tool token usage tracking (#3727) 2026-04-30 15:35:01 +08:00
permissions Expose MCP resource read tool (#5781) 2026-06-24 10:11:51 +00:00
plan-gate fix(core): require confirmation when user manually enters plan mode (#5595) 2026-06-22 15:05:54 +08:00
prompts test: add tests for confirmation-bus, prompt-registry, and cli/core modules (#2272) 2026-04-09 13:54:59 +08:00
providers fix(cli): restore saved custom model IDs when re-entering the auth wizard (#5654) 2026-06-24 10:42:17 +08:00
qwen fix: accept uppercase endpoint URL schemes (#5443) 2026-06-21 02:09:29 +08:00
resources feat(mcp): support MCP resources and reliably surface prompts (#5544) 2026-06-21 19:04:52 +08:00
services Expose MCP resource read tool (#5781) 2026-06-24 10:11:51 +00:00
skills docs: fix config/command/auth drift and surface the model-providers page (#5735) 2026-06-24 06:06:01 +08:00
subagents revert(core): revert Protocol enum & model-identity decoupling (#5089) (#5745) 2026-06-23 16:32:38 +08:00
telemetry feat(workflows): finish Dynamic Workflows port — resume, saved workflows, keyword trigger, notifications (#4721) (#5600) 2026-06-22 19:13:14 +08:00
test-utils feat(core): layered tool-output truncation, per-message budget, per-tool limits (#4880) 2026-06-12 07:40:58 +08:00
tools Expose MCP resource read tool (#5781) 2026-06-24 10:11:51 +00:00
utils feat(vision-bridge): transcribe images to text for text-only models (#5126) 2026-06-23 19:38:25 +08:00
index.test.ts pre-release commit 2025-07-22 23:26:01 +08:00
index.ts Expose MCP resource read tool (#5781) 2026-06-24 10:11:51 +00:00