mirror of
https://github.com/QwenLM/qwen-code.git
synced 2026-07-10 01:29:17 +00:00
* 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). |
||
|---|---|---|
| .. | ||
| __mocks__/fs | ||
| agents | ||
| config | ||
| confirmation-bus | ||
| core | ||
| extension | ||
| followup | ||
| goals | ||
| hooks | ||
| ide | ||
| lsp | ||
| mcp | ||
| memory | ||
| mocks | ||
| models | ||
| output | ||
| permissions | ||
| plan-gate | ||
| prompts | ||
| providers | ||
| qwen | ||
| resources | ||
| services | ||
| skills | ||
| subagents | ||
| telemetry | ||
| test-utils | ||
| tools | ||
| utils | ||
| index.test.ts | ||
| index.ts | ||