qwen-code/docs
Shaojin Wen e5c01aa353
feat(mcp): support MCP resources and reliably surface prompts (#5544)
* 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).
2026-06-21 19:04:52 +08:00
..
design feat(config): add settings file change detection via chokidar watcher (#3696) (#4933) 2026-06-19 15:00:44 +08:00
developers fix(core): validate grep result limits (#5389) 2026-06-19 13:47:13 +08:00
e2e-tests feat(worktree): Phase D — startup --worktree flag + symlinkDirectories + PR refs (#4381) 2026-05-27 17:04:51 +08:00
plans feat(core)!: redesign auto-compaction thresholds with three-tier ladder (#4345) 2026-05-25 21:11:08 +08:00
superpowers feat(serve): add daemon idle detection to GET /health?deep=true (#4934) 2026-06-18 06:55:03 +00:00
users feat(mcp): support MCP resources and reliably surface prompts (#5544) 2026-06-21 19:04:52 +08:00
verification/abort-controller-refactor fix(core): stop AbortSignal listener leak in long sessions (MaxListenersExceededWarning) (#4366) 2026-05-26 14:21:49 +08:00
_meta.ts feat: refactor docs 2025-12-05 10:51:57 +08:00
declarative-agents-port.md feat(core): port declarative-agent mcpServers + hooks (CC 2.1.168 parity follow-up) (#4996) 2026-06-12 14:15:51 +08:00
index.md fix: lint issues 2025-12-19 15:52:11 +08:00
yaml-parser-replacement.md feat(core): port declarative-agent mcpServers + hooks (CC 2.1.168 parity follow-up) (#4996) 2026-06-12 14:15:51 +08:00