ext-skills/docs/client-mcp-support.md
Ola Hungerford c1fa4f315e
docs: add skills-extension-candidates and client-mcp-support trackers (#92)
* docs: add skills-extension-candidates tracker

Scaffold a candidates list for MCP servers, dev tools, SDKs, and
skills repositories that could adopt SEP-2640. Initial entries:
FastMCP 3.0 Skills, chrome-devtools-mcp, hf-mcp-server, Hugging Face
skills library, github-mcp-server (PR #2428 demo), Azure Skills Plugin.

Cross-link from related-work.md and add a row to the README docs
table.

* docs: add Issue/PR tracker column to candidates table

* docs: add client-mcp-support survey alongside candidates tracker

* docs: link Azure MCP Server source in candidates table

* docs: remove gemini-cli from client MCP support survey

* docs: verify hermes-agent, switch source refs to main-branch links, refresh takeaways

- Promote hermes-agent from first-pass to verified; note its per-server tool
  pattern (`mcp_{server}_read_resource` + `mcp_{server}_list_resources`) and
  confirm end-user docs exist
- Drop stale line numbers throughout; convert path refs to GitHub source-file
  links on `main` (commit-pinned URLs decayed once files moved)
- Goose: PR #8989 has landed, so describe current `(extension_name, uri)`
  required signature and drop the now-closed issue/PR tracking
- Takeaways: collapse to three implementation patterns (Goose now fits the
  `(server, uri)` bucket), fix open-source count to fourteen, drop the
  smolagents name-drop (not in survey), add strands-agents alongside mastra
  for the internal-SDK-only pattern, and reframe the signature recommendation
  around server-resolution reliability rather than URI portability

* docs: verify remaining first-pass clients, flip cline/Roo-Code to Yes

Verified adk-python, agent-framework, crewAI, mastra, cline, Roo-Code
against local source clones. Two corrections to prior state:

- cline and Roo-Code both expose access_mcp_resource(server, uri) as a
  model-facing tool via McpHub.readResource. Flipped from No to Yes.
- adk-python's load_mcp_resource takes resource_names: array[string] in
  a single call (not two-shot); opt-in via use_mcp_resources flag.

Headline count updated from six to eight of fourteen open-source clients
satisfying #2527. Cross-cutting observations and takeaways adjusted for
the new (server, uri) family members and the four-pattern signature
breakdown. Trust-model list extended.

* docs: add Figma and Adobe MCP as SEP-2640 testing candidates

* docs: update FastMCP 3.0 Skills entry with detailed notes on SEP-2640 compliance

* docs: drop unconfirmed Cursor SEP-2640 entry, track latest SEP-2640 wire-contract PRs

Remove the Cursor row + deep dive (reported WIP never confirmed). Add the
in-flight PRs realigning to the 2026-06-05/06-09 SEP-2640 schema (frontmatter +
archives[] index, resources/directory/read): hf-mcp-server#174, fast-agent#825,
huggingface/skills#172.

* docs: add Slack (interested) and Bloomberg (internal use) as SEP-2640 candidates

* docs: remove archived Roo-Code from client survey, update strands repo to harness-sdk

* docs: re-verify SEP-2640 implementations against current upstream main

Both reference implementations have migrated to the July 2026 v1 shape,
verified against fresh clones rather than the prior June commit pins:

- hf-mcp-server (d794b10, v0.4.6): skills/list + skills/get, per-file
  {uri,digest} resources manifest, directoryRead:true, no index.json or
  archives. Migrated in #219 (merged 2026-08-03). Also records the
  snapshot/TTL model and HTTP-transport-only constraint.
- fast-agent (79b903c, 0.10.1): pins SEP-2640 revision d7490ec, treats
  legacy index.json skill-md/archive entries as unsupported. Requires
  --prerelease=allow (pins fastmcp-slim 4.0.0b1), per @evalstate.

Corrects the earlier note claiming implementations lagged the SEP: the
#174/#825/#172 round merged 2026-06-19 and was never in flight, and a
second realignment followed the v1 rework.

Also fixes drift found while re-checking fast-agent's resource tool:
smart_agent.py is gone (now core/harness_tools.py), get_resource is
opt-in via harness_tools (default False, was unconditional), and
server_name is now optional - the one surveyed client where the model
may omit server identity.

Runtime skill loading is still unshipped; #816 remains open.

🦉 Generated with [Claude Code](https://claude.com/claude-code)

* docs: track SEP revision 0eb05fe and add mcpkit to the candidates tracker

The SEP moved from d7490ec to 0eb05fe (2026-08-05). The delta is
editorial for the wire contract -- skills/list, skills/get, and the
per-file resources manifest are unchanged -- so d7490ec-pinned
implementations stay compatible. Records what it does change: a soft
reg-name constraint on the first skill-path segment, the skill root URI
fixed as skill://<skill-path> with no trailing slash, whether skills/get
carries ttlMs/cacheScope reopened, and normative host obligations in the
security section.

Adds mcpkit (panyam/mcpkit, Go, v0.3.0) from #118 as a candidate. It
implements the pre-v1 index.json catalog shape with per-file digests, so
the ecosystem is now split across draft revisions rather than uniformly
caught up.

🦉 Generated with [Claude Code](https://claude.com/claude-code)

* docs: refine mcpkit entry — deliberate tracking, partial v1 migration

Checked mcpkit's issues and PRs rather than inferring alignment from
code shape. Its lag is a tracked decision, not drift:

- #1185 (merged 2026-08-04) implements origin-tagging and per-origin
  resolve citing SEP L179/L183/L185 -- the host obligations 0eb05fe made
  normative -- so it is already on v1 for the security section. #1187
  adds a SEP-2640 security/conformance harness as a WG demo.
- #780 is an explicit index-schema tracking issue that has absorbed the
  archives removal and the resources:[{uri,digest}] direction but waits
  on WG confirmation; its conformance fork pins provenance at 556154c.
- The carrier change is still unbooked: nothing references skills/list
  or skills/get.
- Archives are kept as a non-spec extended surface rather than deleted,
  unlike fast-agent -- a counter-case for whether deferring a feature
  removes it from the ecosystem.

Sharpens the callout accordingly: the draft split runs within an
implementation, not only between them, so "which draft does X
implement" often has no single answer.

🦉 Generated with [Claude Code](https://claude.com/claude-code)
2026-08-28 09:49:18 -07:00

215 lines
46 KiB
Markdown
Raw Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# Client Research: Model-facing MCP Resource & SEP-2640 Support
> **Scope.** This page tracks two related areas of client support:
>
> 1. **Model-facing MCP resource loading** — does the client expose a tool that lets the model trigger an MCP `resources/read`? This is the SHOULD proposed in [modelcontextprotocol/modelcontextprotocol#2527](https://github.com/modelcontextprotocol/modelcontextprotocol/pull/2527) (which phrases it as "by URI"); for survey purposes we also count by-name variants where the framework resolves a model-supplied name to a URI before calling `resources/read` (adk-python is the one such case found). The bulk of this page is this survey.
> 2. **SEP-2640 skills extension support** — as clients ship [SEP-2640](https://github.com/modelcontextprotocol/modelcontextprotocol/pull/2640) implementations, this page tracks them. **fast-agent is the first surveyed client to ship one** (registry/install scope — see its deep dive), verified live against the Hugging Face MCP server, which is the first server-side implementation we've interoperated with. SEP-2640-related issues, PRs, and shipped implementations are tracked per-client in the "Open issues/PRs to watch" column rather than via a separate status field.
>
> ℹ️ **SEP-2640 claims here are pinned to a draft revision.** The July 8–15, 2026 v1 rework ([2026-07-16 decision record](decisions.md)) removed archives, replaced `skill://index.json` with a `skills/list` method, replaced the single per-entry digest with a per-file `resources` manifest, added `skills/get`, and gated `resources/directory/read` behind a `directoryRead` capability. **fast-agent has since realigned to that shape** — its docs pin draft revision [`d7490ec`](https://github.com/modelcontextprotocol/modelcontextprotocol/pull/2640/commits/d7490ecd1a250f7bc8c3ebb0d65450dfec274bad) and explicitly drop the older index/archive path — as has its server counterpart, both confirmed by [@evalstate](https://github.com/evalstate) in Discord (2026-08-04). Note the client side ships behind a prerelease install (`--prerelease=allow`; see its deep dive). This is the second realignment (the first was 2026-06-19); since the SEP is still a draft under review, expect further churn and re-verify against the SEP's current revision. The SEP is now at [`0eb05fe`](https://github.com/modelcontextprotocol/modelcontextprotocol/blob/0eb05fe9fbd6b4c5e3fbe7e097028dd0b68a10cf/seps/2640-skills-extension.md) (2026-08-05), but that delta leaves the wire contract intact, so `d7490ec` pins remain compatible — see [skills-extension-candidates.md](skills-extension-candidates.md) for what it does change.
>
> **Why these are tracked together.** SEP-2640 layers skills on top of MCP resources. If a host's progressive-disclosure flow involves the model itself loading L2/L3 skill content from `skill://…` or related resource URIs, then the resource-read affordance for the model becomes critical for skills. A client's resource-loading shape today is a strong predictor of how it'll fit SEP-2640 tomorrow.
>
> See also [**skills-extension-candidates.md**](skills-extension-candidates.md) for the matching server-side tracker: MCP servers, dev tools, SDKs, and skills repositories that are candidates for adopting SEP-2640.
>
## At-a-glance comparison
Category values: **Framework** = SDK/library you build agents on top of · **CLI** = end-user coding agent or CLI · **IDE** = editor-embedded chat surface.
| Client | Category | Tool exposed to model? | Tool name(s) | Tool signature | Calls `resources/read` on connected server? | Enablement gate | End-user docs? | Open issues/PRs to watch |
| :--- | :--- | :--- | :--- | :--- | :--- | :--- | :--- | :--- |
| **codex** (OpenAI) | CLI | Yes | `read_mcp_resource`, `list_mcp_resources`, `list_mcp_resource_templates` | `(server, uri)` — server explicitly named | Yes — handler calls `session.read_resource()` | Any MCP server is configured (`params.mcp_tools.is_some()`) | No — only the LLM-visible tool description; an internal steer tells the model to prefer `tool_search` | _none yet — add as found_ |
| **goose** (Block) | CLI | Yes | `read_resource`, `list_resources` | `(extension_name, uri)` on `read_resource` — `extension_name` required; `list_resources` takes an optional `extension_name` filter | Yes — `ExtensionManager::read_resource` → `client.read_resource(...)` | ≥1 enabled extension reports `ServerCapabilities::resources` | Yes — [`extension-manager-mcp.md`](https://github.com/block/goose/blob/main/documentation/docs/mcp/extension-manager-mcp.md) | _none yet — add as found_ |
| **fast-agent** | Framework | Yes (multiplexed) | `get_resource` (no model-facing `list_resources`) | `(uri, server_name=None)` — `server_name` optional; also handles bundled `internal://` URIs through the same tool | Yes for MCP URIs — `_read_resource()` → `agent.get_resource(resource_uri=uri, namespace=server_name)`; `internal://` URIs short-circuit to bundled resources | Opt-in — `config.harness_tools`, default `False` (also stripped when `tool_only`) | Partial — `skills-over-mcp.md` covers the SEP-2640 install flow; the `get_resource` tool itself has no dedicated docs section | **SEP-2640 v1 shipped** (registry/install scope) — consumes the `io.modelcontextprotocol/skills` extension to install skills via `skills/list` / `skills/get` + per-file digest-verified `resources/read` ([`skills_extension.py`](https://github.com/evalstate/fast-agent/blob/main/src/fast_agent/mcp/skills_extension.py), [`mcp_registry.py`](https://github.com/evalstate/fast-agent/blob/main/src/fast_agent/skills/mcp_registry.py), [`skills-over-mcp.md`](https://github.com/evalstate/fast-agent/blob/main/docs/docs/mcp/skills-over-mcp.md)); pins draft `d7490ec` and treats legacy `skill://index.json` `skill-md`/archive entries as unsupported; still does not read skill resources at model runtime. Verified live against [hf-mcp-server](https://github.com/huggingface/hf-mcp-server). **Watch:** [#816](https://github.com/evalstate/fast-agent/pull/816) (still open) adds MCP-served skill *runtime* loading (URI-backed manifests, `SkillReader` resource reads, untrusted-content wrapping) |
| **vscode** (GitHub Copilot) | IDE | Yes (FS-provider indirection) | `copilot_readFile`, `copilot_listDirectory` (general-purpose, not MCP-specific) | `(path)` — accepts `mcp-resource://…` URIs as paths; the FS provider routes to MCP RPC | Yes — `mcp-resource://` URIs route via `IFileService` → `McpResourceFilesystem` provider → `r.readResource(...)` MCP RPC ([`mcpResourceFilesystem.ts`](https://github.com/microsoft/vscode/blob/main/src/vs/workbench/contrib/mcp/common/mcpResourceFilesystem.ts)) | Server must advertise `McpCapability.Resources`; model needs a URI to pass (typically obtained from a user attachment — the earlier Copilot Chat `resource_link`→`mcp-resource://` pre-wrap has since been removed; see deep dive) | No explicit doc, but mechanism is operational. [Connor Peet's comment on #2527](https://github.com/modelcontextprotocol/modelcontextprotocol/pull/2527#issuecomment-4282395437) is the clearest write-up | _none yet — add as found_ |
| **opencode** | CLI | No | — (internal `readResource` only) | — | Internal only — `readResource()` exists in [`packages/opencode/src/mcp/index.ts`](https://github.com/anomalyco/opencode/blob/main/packages/opencode/src/mcp/index.ts) but is invoked only when the user attaches a resource via file picker; `mcp.tools()` in `prompt.ts` forwards MCP *tools* but never a resource-read tool | n/a | Docs (`mcp-servers.mdx`) advertise tool forwarding only | _none yet — add as found_ |
| **deepagents** (LangChain) | Framework | No | — | — | n/a — delegates MCP wiring to [`langchain-mcp-adapters`](https://github.com/langchain-ai/langchain-mcp-adapters) (via `convert_mcp_tool_to_langchain_tool()` / `_build_cached_mcp_tool()` in [`mcp_tools.py`](https://github.com/langchain-ai/deepagents/blob/main/libs/code/deepagents_code/mcp_tools.py)); that adapter only converts MCP tools, not resources | n/a | No mention in README | **Upstream-blocked**: needs a change in `langchain-mcp-adapters` (or a deepagents wrapper around `client.read_resource()`) |
| **strands-agents** (AWS) | Framework | No (internal SDK only) | — | — | Internal only — `MCPClient.read_resource_sync()` and `list_resources_sync()` in [`mcp_client.py`](https://github.com/strands-agents/harness-sdk/blob/main/strands-py/src/strands/tools/mcp/mcp_client.py) call MCP `resources/read` / `resources/list`, but `MCPAgentTool` (the only adapter) wraps `mcp.types.Tool` only and `MCPClient.load_tools()` (the `ToolProvider` interface) returns tools only | n/a | No — `docs/MCP_CLIENT_ARCHITECTURE.md` and `AGENTS.md` do not mention MCP resources | _none yet — add as found_ |
| **Claude Code** (closed source, reference) | CLI | Yes | `ReadMcpResourceTool` | `(server, uri)` per [public docs](https://code.claude.com/docs/en/tools-reference) | Documented; source not verifiable | n/a | Yes — public tools reference page | n/a |
| **adk-python** (Google ADK) | Framework | Yes (by name, not URI) | `load_mcp_resource` | `(resource_names: array[string])` — model passes catalog names; framework resolves each to a URI and calls `session.read_resource(uri=…)`. The catalog is injected into the model's context by `_append_resources_to_llm_request()` rather than via a separate list call | Yes — `McpToolset.read_resource()` calls `session.read_resource(uri=…)` ([`mcp_toolset.py`](https://github.com/google/adk-python/blob/main/src/google/adk/tools/mcp_tool/mcp_toolset.py)) | Opt-in via `use_mcp_resources=True` on `McpToolset` (default `False`) | No — no user-facing docs found | _none yet — add as found_ |
| **agent-framework** (Microsoft) | Framework | No (tools-only client bridge) | — | — | n/a — `MCPTool` / `MCPStdioTool` / `MCPStreamableHTTPTool` / `MCPWebsocketTool` in [`_mcp.py`](https://github.com/microsoft/agent-framework/blob/main/python/packages/core/agent_framework/_mcp.py) materialize MCP *tools* into the agent's tool registry; no `read_resource` callsite found in client code (only in a test) | n/a | Samples cover MCP tool integration only | _none yet — add as found_ |
| **cline** | IDE | Yes | `access_mcp_resource` | `(server_name, uri)` | Yes — [`McpHub.readResource(serverName, uri)`](https://github.com/cline/cline/blob/main/apps/vscode/src/services/mcp/McpHub.ts) calls `resources/read`; invoked from [`AccessMcpResourceHandler`](https://github.com/cline/cline/blob/main/apps/vscode/src/core/task/tools/handlers/AccessMcpResourceHandler.ts) | Server's `resources` capability surfaces in system prompt context via the [`mcp.ts` formatter](https://github.com/cline/cline/blob/main/apps/vscode/src/core/prompts/system-prompt/components/mcp.ts) | No dedicated user-facing docs found | _none yet — add as found_ |
| **crewAI** | Framework | No | — | — | n/a — `crewai-tools` MCP adapter ([`mcp_adapter.py`](https://github.com/crewAIInc/crewAI-tools/blob/main/crewai_tools/adapters/mcp_adapter.py)) is built on `mcpadapt` and converts MCP tools only — same upstream-blocked pattern as smolagents | n/a | MCP adapter docs cover tools only | **Upstream-blocked** by `mcpadapt` (shared with smolagents) |
| **hermes-agent** (Nous Research) | CLI | Yes | `mcp_{server}_read_resource`, `mcp_{server}_list_resources` (one pair per connected server) | `(uri)` — server is encoded in the tool name | Yes — `_make_read_resource_handler()` in [`tools/mcp_tool.py`](https://github.com/NousResearch/hermes-agent/blob/main/tools/mcp_tool.py) calls `session.read_resource(uri)` and registers the tool per server | Two gates: `tools.resources` config flag (default on) AND server's advertised `capabilities.resources` | Yes — [`features/mcp.md`](https://github.com/NousResearch/hermes-agent/blob/main/website/docs/user-guide/features/mcp.md) and [`guides/use-mcp-with-hermes.md`](https://github.com/NousResearch/hermes-agent/blob/main/website/docs/guides/use-mcp-with-hermes.md) | _none yet — add as found_ |
| **mastra** | Framework | No (internal SDK only) | — | — | Internal only — [`resource.ts`](https://github.com/mastra-ai/mastra/blob/main/packages/mcp/src/client/actions/resource.ts) exposes a `read(uri)` SDK method that calls `client.readResource(uri)`, but it is **not** registered as an LLM-facing tool; the model-facing tool bridge converts MCP tools only | n/a | SDK docs cover the action; no LLM-tool exposure | _none yet — add as found_ |
## Cross-cutting observations
1. **Three implementation patterns for model-facing MCP resource access.**
1. **Dedicated MCP resource tools** — Codex, Goose, Claude Code, Cline: explicit `read_mcp_resource` / `read_resource` / `access_mcp_resource` (+ optional `list_*`) registered alongside other MCP tools. Most discoverable by the model; closest literal reading of #2527. adk-python is a near-relative — its `load_mcp_resource` takes a batch of `resource_names` in one call rather than a single URI, but the affordance shape is the same.
2. **Multiplexed resource tool** — fast-agent: one `get_resource` tool handles both bundled (`internal://`) and MCP URIs behind a single name. Simpler surface for the model; relies on URI scheme to disambiguate.
3. **FS-provider indirection via namespaced URIs** — VS Code: no MCP-specific tool. A dedicated URI scheme (`mcp-resource://`) is registered with the filesystem service so the generic `readFile` / `listDirectory` tools transparently reach MCP `resources/read`. Reuses the agent's existing file-reading affordance; costs nothing in tool-count budget. Downside: no `list` equivalent — the model can only read URIs it's been handed.
2. **Dedicated-tool implementers converge on `(server, uri)`.** Codex, Goose, Claude Code, and Cline all take an explicit *required* server identifier alongside the URI. fast-agent takes the same pair but with `server_name` **optional** — the one surveyed implementation where the model may omit server identity entirely, which is the failure mode the takeaways flag below. hermes-agent is the remaining variant in pattern (1)'s family: it encodes server-in-tool-name (one tool per server) and takes only `(uri)`. adk-python is the outlier — it takes `(resource_names: array[string])` and resolves URIs via a model-visible catalog injected into context.
3. **Trust models diverge.** Per-server enablement (Goose), unconditional-when-MCP-configured (Codex), capability-gated FS provider (VS Code), capability-gated dedicated tool (Cline), or framework opt-in flag (adk-python, `use_mcp_resources=True`, default off). For skills-over-MCP this matters because the "who can read what" boundary is currently set by each host individually — a portable skill that depends on `resources/read` will work or not based on whether the host considers MCP resource access a model-grade affordance.
4. **Discoverability differs sharply between patterns — and within them.** Within pattern (1), discovery shapes diverge: Codex, Goose, hermes-agent (per server), and Claude Code pair `read_*` with a `list_*` tool so the model can enumerate resources on its own; Cline instead injects each server's `resources` and `resourceTemplates` into the system prompt via its `mcp.ts` formatter; adk-python injects an in-context catalog of resource names via `_append_resources_to_llm_request()`. Pattern (2) (fast-agent) has *no* model-facing `list_resources` on current main — of its two harness tools only `get_resource` reads resources; `list_resources` exists on the agent/aggregator but is never registered as a tool. Pattern (3) (VS Code) has *no list equivalent* — the model can only read a URI it's been handed (user attachment → chat context, or an MCP tool returning a `resource_link`). For skills this matters less than it once did: v1 moved enumeration to a `skills/list` *method*, which the host calls directly rather than routing through the model's resource-read tool — so a client's missing `list_resources` no longer blocks skill discovery. It still matters for the L2/L3 step, where the model reads specific skill files by URI.
5. **VS Code's FS-provider pattern is worth its own consideration for skills.** Because `mcp-resource://` URIs are first-class in the file service, *any* tool that takes a path argument in VS Code can transparently read MCP resources — including, in principle, a skill loader. The skills-over-MCP design could lean on this by treating `skill://server/name/SKILL.md` as just another URI scheme registered with the host's file abstraction. This is closer to a registry-style integration (cf. how VS Code surfaces skills via `ChatSessionCustomizationProvider` rather than as system-prompt content).
## Per-client deep dives
### Agent SDKs / frameworks
#### fast-agent
Verified at commit [`79b903c`](https://github.com/evalstate/fast-agent/commit/79b903c) (0.10.1, 2026-08-04). **Restructured since the prior `10a996b` (2026-06-02) check:** `smart_agent.py` is gone and the `smart` tool with it; the model-facing harness tools now live in [`core/harness_tools.py`](https://github.com/evalstate/fast-agent/blob/main/src/fast_agent/core/harness_tools.py).
- **Model-facing tool:** `get_resource`, registered via `agent.add_tool(...)` in `set_harness_tools()` alongside `slash_command` (the two members of `HARNESS_TOOL_NAMES`). `_read_resource()` routes `internal://` to bundled resources; everything else hits the connected MCP server via `agent.get_resource(resource_uri=uri, namespace=server_name)` → MCP `resources/read`.
- **Signature:** `(uri, server_name=None)` — multiplexed across bundled and MCP URIs by scheme, and `server_name` is now **optional**, so the model can omit the server identifier (see the server-identity point in the takeaways).
- **Enablement changed — now opt-in, default off.** Gated on `agent.config.harness_tools`, which defaults to `False` ([`agent_types.py:115`](https://github.com/evalstate/fast-agent/blob/main/src/fast_agent/agents/agent_types.py)); harness tools are also stripped when `tool_only` is set. This page previously recorded fast-agent as unconditional for every `SmartAgent` — that is no longer accurate, and it moves fast-agent into the same opt-in category as adk-python's `use_mcp_resources=True`.
- **No model-facing `list_resources`:** unchanged — `list_resources` exists on the agent/aggregator but is not registered as a tool, so the model can only read a URI it's been handed.
**SEP-2640 v1 skills extension (shipped — registry/install scope).** Verified at commit [`79b903c`](https://github.com/evalstate/fast-agent/commit/79b903c). fast-agent is the first surveyed client to ship a SEP-2640 implementation, and has now realigned to the v1 shape.
- **Pinned draft revision.** [`skills-over-mcp.md`](https://github.com/evalstate/fast-agent/blob/main/docs/docs/mcp/skills-over-mcp.md) pins compatibility to SEP-2640 revision [`d7490ec`](https://github.com/modelcontextprotocol/modelcontextprotocol/pull/2640/commits/d7490ecd1a250f7bc8c3ebb0d65450dfec274bad) and states plainly that this is "draft compatibility, not support for a ratified MCP standard." Legacy servers publishing `skill://index.json` with `skill-md` or archive artifacts are **explicitly unsupported** — the older path was removed, not kept alongside. Pinning the SEP revision in user-facing docs is a practice worth noting for other implementers.
- **What it implements:** the *registry and installation* portion of the SEP, not model-facing resource loading. When a connected server advertises the `io.modelcontextprotocol/skills` extension capability (`server_supports_mcp_skills()` checks `capabilities.extensions` in [`mcp_registry.py`](https://github.com/evalstate/fast-agent/blob/main/src/fast_agent/skills/mcp_registry.py)), fast-agent treats it as an MCP-backed skills registry.
- **Flow (v1):** `/skills registry` calls paginated `skills/list`; installing an entry refreshes it via `skills/get`, then fetches every file named in its `resources` manifest through `resources/read` and requires each to match its declared SHA-256 before writing the local copy. Sidecar metadata records host-assigned server identity, skill URI, and the manifest. Entries omitting `resources` (dynamically generated skills) are listed but **not installable** — no complete digest manifest means no integrity check and no update revision.
- **Exercises v1's unlisted-skill path.** `/skills add skill://acme/example/SKILL.md` installs a skill absent from the listing, with the server confirming the URI via `skills/get` — the exact gap the [2026-07-15 `skills/get` decision](decisions.md) was written to close, now with a working implementation behind it. Name-collision handling is implemented per v1 too: same-named listing entries are preserved and disambiguated by URI, though the local managed directory still admits only one skill per name.
- **Integrity ≠ trust.** The docs carry an explicit warning that a passing SHA-256 check only means the bytes match the manifest *that server supplied* — it does not authenticate server or publisher, nor vouch for the skill's instructions. Useful framing for the [threat model](threat-model.md).
- **Provisional wire models.** The pinned MCP Python SDK ships no typed SEP-2640 models, so fast-agent carries its own for `skills/list`, `skills/get`, and `resources/directory/read` ([`skills_extension.py`](https://github.com/evalstate/fast-agent/blob/main/src/fast_agent/mcp/skills_extension.py)). A concrete data point on what adopting the extension costs while SDK support lags.
- **Install requires opting into prereleases.** Per [@evalstate](https://github.com/evalstate) (fast-agent's author) in Discord, v1 support must be installed as `uv tool install -U --prerelease=allow fast-agent-mcp` — `fast-agent-mcp` 0.10.1 is published on PyPI, but it pins `fastmcp-slim[server]==4.0.0b1` (plus `mcp==2.0.0` / `mcp-types==2.0.0`), and uv refuses prerelease dependencies unless told otherwise. So the first end-to-end SEP-2640 v1 client stack is reachable only through a prerelease install path — worth knowing before treating "shipped" as "generally available."
- **Still not model-facing at runtime.** Active skills do **not** read supporting files from the MCP server during a session; [PR #816](https://github.com/evalstate/fast-agent/pull/816) ("Add MCP-served skill runtime support") remains **open**. The SEP-2640 path is still host-driven install, distinct from the model-facing `get_resource` tool above. (The `SkillReader` in main serves local skill files in non-ACP contexts, not MCP-served ones.)
- **Realignment history:** two rounds. [#825](https://github.com/evalstate/fast-agent/pull/825) (merged 2026-06-19, with [#831](https://github.com/evalstate/fast-agent/pull/831) hardening) moved it to the 2026-06-05 contract; the v1 migration landed later inside the 0.10 release series ([#892](https://github.com/evalstate/fast-agent/pull/892)/[#905](https://github.com/evalstate/fast-agent/pull/905)) rather than as a standalone skills PR.
- **Verified against a live server:** the [Hugging Face MCP server](https://github.com/huggingface/hf-mcp-server) is the server-side counterpart, which migrated to v1 in [#219](https://github.com/huggingface/hf-mcp-server/pull/219) (merged 2026-08-03); see its row in [skills-extension-candidates.md](skills-extension-candidates.md). fast-agent's docs walk through `/mcp connect --name hf https://huggingface.co/mcp` → `/skills registry hf` → `/skills add`.
---
#### adk-python (Google ADK)
Verified at commit [`61a3933`](https://github.com/google/adk-python/commit/61a39330d) (2026-06-03).
- **MCP integration:** [`src/google/adk/tools/mcp_tool/`](https://github.com/google/adk-python/tree/main/src/google/adk/tools/mcp_tool) package — `McpToolset` (lowercase c; `MCPToolset` is a deprecated alias) is the entry point used by callers to connect an ADK agent to an MCP server.
- **Resource RPC wired:** [`mcp_toolset.py`](https://github.com/google/adk-python/blob/main/src/google/adk/tools/mcp_tool/mcp_toolset.py) exposes `async def read_resource(self, name, readonly_context=None)` which calls `session.read_resource(uri=resource_info["uri"])`.
- **Model-facing tool:** [`load_mcp_resource_tool.py`](https://github.com/google/adk-python/blob/main/src/google/adk/tools/load_mcp_resource_tool.py) defines `load_mcp_resource`. **Not two-shot** — the tool's input schema is `resource_names: array[string]` (one call returns the contents of N resources). The catalog of available resources is exposed to the model via `_append_resources_to_llm_request()` injecting it into the LLM context, rather than via a separate `list` tool.
- **Enablement:** opt-in via the `use_mcp_resources=True` constructor flag on `McpToolset` (default `False`). When set, `get_tools()` appends `LoadMcpResourceTool` to the toolset alongside the dynamically discovered MCP tools.
- **End-user docs:** none found in `README.md` or the docs folder — the affordance is undocumented for end users despite being implemented.
- **Why interesting:** ADK is the client with the most rigorous skill validation in the parallel skills survey, so this is where the spec extension and the resource-tool extension converge. The batch `resource_names` shape is a distinct fourth signature among dedicated-tool implementers.
---
#### agent-framework (Microsoft)
Verified at commit [`c3901a4`](https://github.com/microsoft/agent-framework/commit/c3901a4dd) (2026-06-03).
- **MCP integration:** Multi-transport client tools — `MCPTool` plus `MCPStdioTool`, `MCPStreamableHTTPTool`, `MCPWebsocketTool` in [`_mcp.py`](https://github.com/microsoft/agent-framework/blob/main/python/packages/core/agent_framework/_mcp.py). The client-facing methods are `load_tools()`, `call_tool()`, `load_prompts()`, `get_prompt()` — no resource methods. .NET equivalents (e.g. `DefaultMcpToolHandler`) under [`dotnet/src/`](https://github.com/microsoft/agent-framework/tree/main/dotnet/src) expose `InvokeToolAsync()` only.
- **Resource RPC wired:** No MCP resource RPC at the model boundary. The only production `read_resource` callsites are in [`_skills.py`](https://github.com/microsoft/agent-framework/blob/main/python/packages/core/agent_framework/_skills.py) and refer to **skill** resources, not the model-facing MCP `resources/read`. All MCP `resources/read` references are confined to tests.
- **Update:** upstream has since added **MCP-based skills *discovery*** (recent commits use MCP resources internally for skill metadata / `skill://index.json` lookups via `_skills.py`) — but this is still not exposed to the model as a resource-read tool.
- **Why on the list:** dual-stack Python/.NET framework with rich skills metadata in the parallel skills survey. The asymmetry — skills support without a model-facing MCP resource tool — is the kind of gap a #2527-aligned PR would close.
---
#### crewAI
Verified at commit [`051fa0c`](https://github.com/crewAIInc/crewAI/commit/051fa0c1c) (2026-06) of the main `crewAI` repo, which vendors `crewai-tools` under `lib/crewai-tools/` (the adapter is now under `lib/crewai-tools/src/crewai_tools/`).
- **MCP integration:** [`mcp_adapter.py`](https://github.com/crewAIInc/crewAI/blob/main/lib/crewai-tools/src/crewai_tools/adapters/mcp_adapter.py) imports `mcp` (`StdioServerParameters`, `CallToolResult`, `TextContent`, `Tool`) and `mcpadapt.core` (`MCPAdapt`, `ToolAdapter`); `CrewAIToolAdapter.adapt(func, mcp_tool: Tool)` converts MCP tools to CrewAI `BaseTool` objects.
- **Resource RPC wired:** No — `adapt()` is typed to accept only `mcp_tool: Tool`; zero matches for `resource`/`Resource` anywhere in the adapter file or directory. `_run()` extracts `CallToolResult` content only.
- **Model-facing tool:** None.
- **Why on the list:** Same upstream block as **smolagents** — both depend on [`mcpadapt`](https://github.com/grll/mcpadapt) (`mcpadapt>=0.1.9` declared as optional dep in `lib/crewai-tools/pyproject.toml`), which is tools-only. A single upstream change in `mcpadapt` flips both clients. This is the highest-leverage adapter-library target identified so far.
---
#### deepagents (LangChain)
Verified at commit [`436409f`](https://github.com/langchain-ai/deepagents/commit/436409fc) (2026-06-03). Note the repo has since been restructured into a monorepo — the MCP code now lives under `libs/code/deepagents_code/`.
- **MCP wiring is outsourced** to `langchain-mcp-adapters` — [`mcp_tools.py`](https://github.com/langchain-ai/deepagents/blob/main/libs/code/deepagents_code/mcp_tools.py) calls `convert_mcp_tool_to_langchain_tool()` / `_build_cached_mcp_tool()` (the older `load_mcp_tools()` entry point is no longer used). The adapter does not expose `resources/read` as a tool — zero hits for resource-read in deepagents source; sessions only call `list_tools()` / `call_tool()`.
- **What it would take:** either upstream adds a resource-read tool, or deepagents wraps `client.read_resource()` outside the adapter.
---
#### mastra
Verified at commit [`f82cc72`](https://github.com/mastra-ai/mastra/commit/f82cc72edc) (2026-06).
- **MCP integration:** [`packages/mcp/src/client/client.ts`](https://github.com/mastra-ai/mastra/blob/main/packages/mcp/src/client/client.ts) imports `Client` from `@modelcontextprotocol/sdk/client/index.js`. [`actions/resource.ts`](https://github.com/mastra-ai/mastra/blob/main/packages/mcp/src/client/actions/resource.ts) exposes `public async read(uri: string)` which directly returns `this.client.readResource(uri)`.
- **Resource RPC wired:** Yes, but **internal SDK only**. `ResourceClientActions` is mounted as `this.resources` on the client — an SDK surface for callers, not registered as an LLM-facing tool.
- **Model-facing tool:** No. The model-facing bridge is `tools()` at [`client.ts`](https://github.com/mastra-ai/mastra/blob/main/packages/mcp/src/client/client.ts) which calls `this.client.listTools()` and iterates only the returned tools array — resources are never traversed.
- **Why on the list:** Mastra's skill versioning architecture (content-addressable BlobStore, draft→publish lifecycle) is the most sophisticated. Unlocking resource-read at the model boundary would be a small change relative to the rest of the framework — the SDK plumbing already exists.
---
#### strands-agents
Verified at commit [`0e0035f`](https://github.com/strands-agents/harness-sdk/commit/0e0035fb) (2026-06). Strands matches the **mastra pattern**: resource RPC wired in the SDK, but no model-facing tool. The Python SDK lives in the `strands-py/` package of the `strands-agents/harness-sdk` monorepo.
- **Resource RPC wired but unbridged:** `MCPClient.read_resource_sync()`, `list_resources_sync()`, and `list_resource_templates_sync()` in [`mcp_client.py`](https://github.com/strands-agents/harness-sdk/blob/main/strands-py/src/strands/tools/mcp/mcp_client.py) are SDK-public methods, but [`MCPAgentTool`](https://github.com/strands-agents/harness-sdk/blob/main/strands-py/src/strands/tools/mcp/mcp_agent_tool.py) only wraps `mcp.types.Tool` and delegates to `call_tool_async(...)`. Outside tests, nothing in the repo invokes the resource methods.
- **Why on the list:** Strands has its own local-filesystem skills implementation at [`agent_skills.py`](https://github.com/strands-agents/harness-sdk/blob/main/strands-py/src/strands/vended_plugins/skills/agent_skills.py), so the framework already does progressive disclosure. Wiring resource-read to the model is the obvious next step for an MCP-backed skill story.
---
### Coding agents / CLIs
#### codex (OpenAI)
Verified at commit [`ad2012d`](https://github.com/openai/codex/commit/ad2012d645b7146d31bb03f98e2bd9371635d11a). (The handlers were reorganized from the single `codex-rs/tools/src/mcp_resource_tool.rs` file into a per-tool module directory since the prior `67849d9` check.)
- **Model-facing tools:** `read_mcp_resource`, `list_mcp_resources`, `list_mcp_resource_templates`, each a handler module under [`codex-rs/core/src/tools/handlers/mcp_resource/`](https://github.com/openai/codex/tree/main/codex-rs/core/src/tools/handlers/mcp_resource); registered unconditionally via `add_mcp_resource_tools` in [`spec_plan.rs`](https://github.com/openai/codex/blob/main/codex-rs/core/src/tools/spec_plan.rs), gated on `context.mcp_tools.is_some()`. The read handler [`read_mcp_resource.rs`](https://github.com/openai/codex/blob/main/codex-rs/core/src/tools/handlers/mcp_resource/read_mcp_resource.rs) calls `session.read_resource(&server, …)`.
- **Signature:** `(server, uri)` — model names the server explicitly. No user-facing doc; an internal steer (in `apps_instructions.rs`) tells the model to prefer `tool_search`.
---
#### goose (AAIF)
Verified at commit [`45d8bf8`](https://github.com/aaif-goose/goose/commit/45d8bf81d09d478ceedba8f6d1f0ad906123a981).
- **Model-facing tools:** `read_resource`, `list_resources` registered in [`ext_manager.rs`](https://github.com/aaif-goose/goose/blob/main/crates/goose/src/agents/platform_extensions/ext_manager.rs) when at least one extension reports `ServerCapabilities::resources`. Dispatch flows through [`extension_manager.rs`](https://github.com/aaif-goose/goose/blob/main/crates/goose/src/agents/extension_manager.rs) → `client.read_resource(...)` → MCP `resources/read`. Documented at [`extension-manager-mcp.md`](https://github.com/block/goose/blob/main/documentation/docs/mcp/extension-manager-mcp.md).
- **Signature:** `(extension_name, uri)` on `read_resource` — `extension_name` is required. `list_resources` takes an optional `extension_name` filter; the tool description directs the model to call `list_resources` first when ownership is unknown.
---
#### hermes-agent (Nous Research)
Verified at commit [`03ba06e`](https://github.com/NousResearch/hermes-agent/commit/03ba06ebf) (2026-06). No drift across the 376 commits since the prior `72ff3e9` check.
- **MCP integration:** Full integration in [`tools/mcp_tool.py`](https://github.com/NousResearch/hermes-agent/blob/main/tools/mcp_tool.py).
- **Resource RPC wired:** Yes — `_make_read_resource_handler()` calls `await server.session.read_resource(uri)`; the per-server schemas are built in `_build_utility_schemas()`.
- **Model-facing tools:** Yes — both `mcp_{safe_name}_read_resource` (taking `uri`) and `mcp_{safe_name}_list_resources` (no parameters) registered per connected server via `_build_utility_schemas()`. Conditionally registered when the server advertises `capabilities.resources` AND the per-server `tools.resources` config flag is on (default).
- **Signature:** `(uri)` — server is encoded in the tool name (one tool per server), so the model implicitly selects the server by selecting the tool. **Distinct from every other pattern surveyed**: not `(server, uri)`, not `(resource_names: array[string])` batch, not `(uri)`-with-probing, not URI-virtualized — it's *one tool per server*. The matching `list_resources` per server gives the model a discovery affordance without cross-server probing.
- **End-user docs:** [`features/mcp.md`](https://github.com/NousResearch/hermes-agent/blob/main/website/docs/user-guide/features/mcp.md) and [`guides/use-mcp-with-hermes.md`](https://github.com/NousResearch/hermes-agent/blob/main/website/docs/guides/use-mcp-with-hermes.md) both document the utility wrappers. (Minor doc/code mismatch: the user-facing docs name the tools as bare `read_resource` / `list_resources`, but the actual model-facing names include the `mcp_{server}_` prefix.)
- **Why interesting:** The per-server-tool naming is a candidate disambiguation pattern for skills-over-MCP — the model can't pick the wrong server because each tool only binds to one.
---
#### Claude Code (closed source — reference only)
Claude Code is closed source so we cannot verify the loader, but the [public tools reference](https://code.claude.com/docs/en/tools-reference) documents `ReadMcpResourceTool` accepting `server` and `uri` parameters. Listed here as a reference data point for the `(server, uri)` signature shape also adopted by Codex, Goose, fast-agent, and Cline.
---
#### opencode
Verified at commit [`2a33add`](https://github.com/anomalyco/opencode/commit/2a33addd2) on the canonical `dev` default branch.
- **Tools forwarded to the model** in [`packages/opencode/src/mcp/index.ts`](https://github.com/anomalyco/opencode/blob/main/packages/opencode/src/mcp/index.ts) via `client.callTool()`; tool naming convention `{server_name}_{tool_name}` (sanitized, underscore-separated — `sanitize(clientName) + "_" + sanitize(mcpTool.name)`). The model-tool bridge in [`packages/opencode/src/session/tools.ts`](https://github.com/anomalyco/opencode/blob/main/packages/opencode/src/session/tools.ts) forwards only `mcp.tools()`.
- **Resource-read is UI-only:** `readResource()` in the same file is invoked only from the file picker (`session/prompt.ts` attachment path), never registered as an LLM tool. Docs ([`mcp-servers.mdx`](https://github.com/anomalyco/opencode/blob/main/packages/web/src/content/docs/mcp-servers.mdx)) advertise tool forwarding only.
---
### IDE extensions
#### vscode (GitHub Copilot)
Verified at commit [`ebb335f`](https://github.com/microsoft/vscode/commit/ebb335fad02) (vscode core, 2026-06-03); the Copilot Chat extension was re-checked against current `microsoft/vscode-copilot-chat` main. VS Code is the unique pattern here: no MCP-specific tool — generic `copilot_readFile` / `copilot_listDirectory` tools transparently reach MCP servers via filesystem-provider indirection on a custom URI scheme.
- **URI scheme + FS provider:** [`McpResourceURI`](https://github.com/microsoft/vscode/blob/main/src/vs/workbench/contrib/mcp/common/mcpTypes.ts) defines `mcp-resource://` URIs that encode the MCP server's definition ID in the authority — self-routing. [`McpResourceFilesystem`](https://github.com/microsoft/vscode/blob/main/src/vs/workbench/contrib/mcp/common/mcpResourceFilesystem.ts) is registered for that scheme; `_readURIInner` decodes the URI, looks up the `McpServer`, and calls `r.readResource(...)` — the MCP `resources/read` RPC. Capability-gated on `McpCapability.Resources`.
- **`resource_link` pre-wrap — removed:** the doc previously described `mcpLanguageModelToolContribution.ts` converting an MCP tool's `resource_link` into an `mcp-resource://` URI for later re-reading via `copilot_readFile`. As of current main that file is gone and `microsoft/vscode-copilot-chat` no longer references the `mcp-resource://` scheme at all. The core scheme + FS provider in vscode itself remain intact, but the extension no longer auto-wraps `resource_link`s — so the model now has fewer built-in ways to obtain such a URI (the live forwarding path observed in the local clone renders `resource_link`s as plain text). *Worth re-confirming the current intended mechanism upstream.*
- **Caveat — discoverability:** the model can only read a URI it has been handed (e.g. a user attachment). No `list_mcp_resources`-equivalent **for the model**. Note vscode core *does* now expose `IMcpServer.resources()` / `resourceTemplates()` ([`mcpTypes.ts`](https://github.com/microsoft/vscode/blob/main/src/vs/workbench/contrib/mcp/common/mcpTypes.ts)), but these feed a *user-facing* resource quick-pick (`mcpResourceQuickAccess.ts`), not a model tool. [Connor Peet on PR #2527](https://github.com/modelcontextprotocol/modelcontextprotocol/pull/2527#issuecomment-4282395437) is the clearest write-up of this pattern.
---
#### cline
Verified at commit [`81792d2`](https://github.com/cline/cline/commit/81792d20c) (2026-06-04). Note the repo restructured into a monorepo — the VS Code extension source now lives under `apps/vscode/src/` (was `src/`).
- **MCP integration:** Depends on `@modelcontextprotocol/sdk` `^1.25.1` per `apps/vscode/package.json`.
- **Resource RPC wired:** Yes. [`McpHub.readResource(serverName, uri)`](https://github.com/cline/cline/blob/main/apps/vscode/src/services/mcp/McpHub.ts) issues a raw `resources/read` request against the named connection's client and decodes with `ReadResourceResultSchema`.
- **Model-facing tool:** Yes. [`AccessMcpResourceHandler`](https://github.com/cline/cline/blob/main/apps/vscode/src/core/task/tools/handlers/AccessMcpResourceHandler.ts) calls `mcpHub.readResource(...)` from the model-tool execution pipeline. The model sees `access_mcp_resource` as a registered tool taking `(server_name, uri)`.
- **Discoverability:** The [system-prompt formatter](https://github.com/cline/cline/blob/main/apps/vscode/src/core/prompts/system-prompt/components/mcp.ts) enumerates each connected server's `resources` and `resourceTemplates` in `formatMcpServersList()`. Populated by `fetchResourcesList()` and `fetchResourceTemplatesList()` in `McpHub.ts`.
- **End-user docs:** none found in `README.md`; no `docs/` directory in the repo root.
- **Why interesting:** Cline has the most extensive skills implementation in the parallel survey (incl. the admin-locked enterprise `globalSkills` primitive), so the *presence* of full resource-read support, in-prompt enumeration, and dedicated handler is the natural complement.
## Takeaways for SEP-2640 (skills extension)
- **v1's protocol surface has a working client/server pair, and realignment is fast but not free.** fast-agent and hf-mcp-server have both migrated to the July v1 shape (fast-agent pinning revision `d7490ec`; hf-mcp-server via [#219](https://github.com/huggingface/hf-mcp-server/pull/219), 2026-08-03), so `skills/list`, `skills/get`, per-file digest verification, and `directoryRead` gating are all exercised end-to-end — including v1-specific paths like installing an unlisted skill by URI through `skills/get`. Two observations for the WG: (a) this pair has now realigned **twice in seven weeks** (2026-06-19, then early August), and each round was a breaking change — both implementations dropped the old shape rather than supporting both, and fast-agent's archive download/extract hardening was discarded outright when archives became a deferred feature; (b) neither can lean on the SDK — fast-agent carries provisional local wire models because the pinned MCP Python SDK has no typed SEP-2640 models, which is a standing cost for every adopter until the SDKs catch up. Implementation experience is real evidence for the SEP's readiness here, but it is evidence about a pinned revision, so it should be re-dated whenever the draft moves — and about a prerelease install path on the client side, not a generally-available one.
- **First shipped client + server pair.** **fast-agent** is the first surveyed client to ship a SEP-2640 implementation — but scoped to *registry/install*: it consumes the `io.modelcontextprotocol/skills` extension to install digest-verified skills into its managed skills directory, and does not (yet) read skill resources at model runtime ([#816](https://github.com/evalstate/fast-agent/pull/816) still open). It was verified live against the **Hugging Face MCP server**, the first server-side implementation we've interoperated with (advertises the extension with `directoryRead: true` plus `resources: { subscribe: false, listChanged: false }`; tracked in [skills-extension-candidates.md](skills-extension-candidates.md)). One deployment detail worth carrying into the SEP discussion: HF serves skills **over HTTP transports only**, not over long-lived STDIO. Notably, the first real-world wrinkle was operational, not protocol-level: a client that retry-loops `resources/subscribe` (cursor-vscode) flooded the HF server, prompting a client-denylist mitigation ([hf-mcp-server#164](https://github.com/huggingface/hf-mcp-server/pull/164)) — a reminder that serving skills as resources inherits the resource subsystem's failure modes.
- **Seven of thirteen open-source clients surveyed** (Codex, Goose, fast-agent, VS Code, hermes-agent, adk-python, Cline) satisfy #2527's SHOULD; closed-source Claude Code's public docs confirm it does too. Goose and hermes-agent also satisfy the implicit expectation that this be documented for end users; the other implementers ship the affordance with no user-facing documentation.
- **Four implementation patterns** for model-facing MCP resource access — all support the layering Peter sketches in his #2640 comment, but with different costs:
- **Dedicated tools, `(server, uri)`** (Codex, Goose, Claude Code, fast-agent, Cline — fast-agent is also multiplexed across `internal://`): explicit `read_resource` / `access_mcp_resource` registered alongside other MCP tools. Most discoverable; closest literal reading of #2527. By volume, this is the dominant pattern.
- **Dedicated tool, `(resource_names: array[string])`** (adk-python): batch read; the available resources are surfaced to the model via context injection rather than a separate list tool. Same affordance shape as the `(server, uri)` family but with the server identifier embedded in the catalogued resource name rather than passed as a parameter.
- **One tool per server, `(uri)`** (hermes-agent): server is encoded in the tool name (`mcp_{server}_read_resource`), so the model selects the server by selecting the tool. Trades catalog tokens for unambiguity. Pairs with `mcp_{server}_list_resources` so the model has a per-server discovery affordance.
- **FS-provider indirection via namespaced URIs** (VS Code): no MCP-specific tool. `mcp-resource://` URIs are registered with the file service; generic `readFile` / `listDirectory` tools transparently reach `resources/read`. Costs nothing in tool-count budget; loses `list` affordance.
Reliability implication: each pattern enforces server identity differently — explicit parameter (`(server, uri)`), tool choice (per-server tool), catalog-name lookup (adk-python), or URI authority (`mcp-resource://<server-id>/...`). **The spec should nudge implementations to keep server identity mandatory** so a skill always resolves to its owning server. Otherwise the host has to probe every connected server when the model omits it — either silently picking the wrong one, or iterating through all of them at the cost of latency and tokens. fast-agent's optional `server_name` is the live instance of this: notably, its SEP-2640 install path does *not* inherit the ambiguity, since `/skills registry <server>` binds a specific server and skill provenance is recorded per install — a useful signal that skill-scoped operations want stronger server binding than a general resource-read tool.
- **Adapter libraries block two "no" rows.** **crewAI** depends on [`mcpadapt`](https://github.com/grll/mcpadapt); **deepagents** depends on [`langchain-mcp-adapters`](https://github.com/langchain-ai/langchain-mcp-adapters). Both adapters are tools-only — the unlock for either client lives in the adapter, not the client repo.
- **Internal SDK without LLM-tool exposure** (mastra, strands-agents) is its own pattern: the resource RPC is wired in the SDK but not bridged to the model. Closing this gap is typically smaller-scope than wiring the RPC from scratch.