mirror of
https://github.com/QwenLM/qwen-code.git
synced 2026-07-21 23:14:02 +00:00
* feat(mcp): support MCP resources and reliably surface prompts
Prompts were silently hidden for MCP servers that implement `prompts/list`
but under-declare the `prompts` capability in their initialize response.
Drop the capability gate in `listMcpPrompts` (and apply the same leniency to
resources): always attempt the list call and swallow `Method not found`, so
those prompts now appear as `/` slash commands like in other clients.
Add first-class MCP resource support:
- core: `listMcpResources` / `discoverResources`, a `DiscoveredMCPResource`
type, a `ResourceRegistry`, and `Config.getResourceRegistry()`. Discovery
is wired into the standalone `McpClient.discover()` path and the
connection-pool path (snapshot -> `SessionMcpView.applyResources` -> the
session's registry, with a `resourcesChanged` event on reconnect). A
resource-only server now counts as a successful discovery.
- cli: the `/mcp` dialog shows per-server Resources (and Prompts) counts.
- cli: `@server:uri` reads an MCP resource and injects its contents into the
message (text inline, blobs as inlineData); `@server:` autocompletes the
server's resource URIs. The `server` prefix must match a configured MCP
server, so existing `@path` file references are unaffected.
Docs updated; unit tests added across core and cli.
* fix(mcp): reload commands on discovery + harden resource ref parsing
Address review feedback and complete the prompt UX:
- Slash commands now rebuild when an MCP server finishes connecting.
Discovery is progressive (runs after the UI is interactive), so prompts
a server exposes via prompts/list were registered too late to appear in
the `/` menu — the `/mcp` dialog showed the count while the slash menu
stayed empty. A debounced MCP-status listener now reloads the command
tree on connect. Verified end-to-end in a real TUI against a mock server
that under-declares the prompts capability: `/greet` now lists.
- `isMethodNotFound` keys off the JSON-RPC `-32601` code rather than the
server-supplied message text (which may be localized or worded
differently); applied to both `listMcpPrompts` and `listMcpResources`.
- `useAtCompletion` uses `Object.hasOwn` so `@__proto__:` / `@constructor:`
and other inherited keys are not mistaken for configured servers.
* fix(mcp): lenient resource read to match discovery + review polish
- `McpClient.readResource` no longer prechecks `getServerCapabilities()
?.resources`. This PR is the first caller to make the read path
reachable, and the strict precheck meant a server that answers
`resources/read` but under-declares the `resources` capability — exactly
the servers the lenient `listMcpResources` discovery targets — would get
its resources discovered, listed in `/mcp`, and autocompleted, yet fail
every `@server:uri` with a misleading "does not support resources". The
read now matches discovery; a server that truly lacks resources answers
`-32601`, surfaced as the existing error card. Added a regression test.
- `@server:uri` success cards now report what was injected ("Injected N
chars" / "N attachments") or "(no readable content)" when a read yields
no text/blob parts, so a partially-empty multi-ref read isn't hidden.
- `useAtCompletion` resource filter reduced to a single `includes` match
(`startsWith` was subsumed; empty partial matches all via `includes('')`);
the overstated "prefers prefix" comment is corrected.
- Tests: mixed `@file` + `@server:uri` injection (both parts + both cards),
empty-content card, and the pool restart fan-out now asserts
`applyResources` alongside `applyTools`.
* perf+security(mcp): parallelize discovery/reads, cap & frame resources
Review round 3 fold-ins:
- Discovery now runs `listMcpPrompts` / `listMcpResources` / `discoverTools`
concurrently in `discoverAndReturn` (independent reads; the SDK client
multiplexes by JSON-RPC id), saving per-server round-trips at startup.
- `@server:uri` resource reads run in parallel (`Promise.allSettled`) instead
of serially, matching how the file path batches via `readManyFiles`; order
is preserved so cards/labels line up with refs.
- Resource injection is now capped and framed: text is bounded by
`MAX_MCP_RESOURCE_TEXT_CHARS` (100k) and oversized blobs are skipped, so a
misbehaving/hostile server can't overflow the context window or OOM; the
content is fenced with `--- Content from MCP resource <label> ---` /
`--- End ---` delimiters so the model can separate untrusted server output
from the user's prompt. The success card reports truncation.
- File-read error path now merges `resourceLabels` into `filesRead` /
recording, so a resource read that succeeded before a file read failed is
not dropped from the audit trail.
- `isMethodNotFound` (JSON-RPC -32601) now also covers `discoverTools` and
`invokeMcpPrompt`, replacing the remaining message-substring checks.
- `PoolEntry.markActive` `initialResources` is now required (no `= []`
default), removing a footgun where an omitted arg would wipe a server's
resources via `applyResources([])`.
- `useAtCompletion` resource suggestions rank prefix matches above mid-string
matches.
- `'Resources:'` added to the remaining 6 locales (ca, de, fr, ja, pt, ru)
for parity with `'Prompts:'`.
Tests: attribution framing, text truncation, completion prefix ranking.
* test(mcp): cover resource registration in discover() and resource-only discovery
Closes the two coverage gaps flagged in review: assert discover() registers
discovered resources into the Config ResourceRegistry, and that a resource-only
server (no tools/prompts) is a successful discovery rather than throwing.
* fix(mcp): idempotent resource re-discovery + cumulative blob cap
Review round 4 (all Suggestions):
- discover() now clears a server's resources (removeResourcesByServer) before
re-registering, so reconnect / incremental re-discovery is idempotent and a
resource the server dropped doesn't linger in the registry (matches the
pool path's SessionMcpView.applyResources).
- @server:uri injection now caps CUMULATIVE blob size per resource, not just
each blob, so many sub-limit blobs in one response can't inject unbounded
data. Added a test for the oversized-blob skip + card.
- Documented that file/resource content parts are grouped by type (model
correlates by delimiter labels, not position).
* test(mcp): cover the MCP-status command reload (prompts surfacing in /)
Adds the missing coverage for the discovery-driven reload: a CONNECTED status
fires the listener and rebuilds the command tree (so progressively-discovered
MCP prompts appear as / commands), and a non-CONNECTED status does not.
* fix(mcp): don't wipe resources when resources/list transiently fails
- discover() only clears + replaces a server's resources when listMcpResources
returns a non-empty set. Because that helper swallows all errors (including
transient network failures) and returns [], an unconditional clear-then-
register would silently purge a server's resources on a transient list
failure while tools/prompts succeed. Guarding on length>0 keeps the existing
set on failure; a real partial drop still re-registers the fresh set.
- Resource success card now shows '(truncated)' for capped/skipped blobs too,
not just text. Added a cumulative-blob-cap test (two sub-limit blobs whose
sum exceeds the cap).
* fix(mcp): guard pool applyResources against transient-failure wipe too
The non-pool discover() guard (resources.length > 0) left the pool path
exposed: on a restart, doRestart -> discoverAndReturn swallows a transient
resources/list failure to [], and applyResources([]) then wiped the session's
resources. applyResources is now a no-op on an empty snapshot (mirrors the
discover() guard; applyTools/applyPrompts keep their pre-existing clear-on-empty
behavior, out of scope). Added tests: applyResources([]) does not clear, and
discover() with an empty resource list does not call removeResourcesByServer.
* fix(mcp): preserve pool resource snapshot on transient restart failure
The applyResources([]) no-op only protected already-attached sessions; doRestart
still overwrote the pool entry's resourcesSnapshot with [] when the restart's
resources/list transiently failed, so any session attaching AFTER the restart
got zero resources. doRestart now only updates resourcesSnapshot when the
re-read is non-empty, preserving it for new and existing subscribers alike.
Tests: applyResources([]) preserves a pre-populated set; a restart whose
resources/list comes back empty still serves the prior resource to a new
session.
* fix(mcp): trust-gate resource completion, colon server names, narrower method-not-found
- useAtCompletion no longer surfaces resource URIs in an untrusted folder
(the read path is already blocked there); avoids leaking resource existence.
- parseMcpResourceRef / getMcpResourceSuggestions match the LONGEST configured
server name as a '<name>:' prefix instead of splitting on the first colon,
so a server whose name contains ':' (a valid settings.json key) resolves.
- isMethodNotFound's message fallback is back to the case-sensitive exact
'Method not found' substring (the -32601 code is the primary check), not a
broad /method not found/i that would swallow unrelated errors.
Tests: @my:server:uri resolution, untrusted-folder completion.
* refactor(mcp): extract shared longest-prefix server matcher + doc/test fixes
- Extract matchMcpServerPrefix (new mcpResourceRef.ts) and use it from both
parseMcpResourceRef (injection) and getMcpResourceSuggestions (completion),
removing the duplicated longest-prefix logic and its drift risk.
- Update parseMcpResourceRef JSDoc to describe longest-prefix matching.
- Tests: shared-helper unit tests; the @my:server colon test now configures
both 'my' and 'my:server' to exercise disambiguation; a colon completion
test; isMethodNotFound message-casing tests (exact 'Method not found'
swallowed, 'method not found handler' not swallowed).
|
||
|---|---|---|
| .. | ||
| design | ||
| developers | ||
| e2e-tests | ||
| plans | ||
| superpowers | ||
| users | ||
| verification/abort-controller-refactor | ||
| _meta.ts | ||
| declarative-agents-port.md | ||
| index.md | ||
| yaml-parser-replacement.md | ||