modelcontextprotocol / modelcontextprotocol/go-sdk
Proposal: dynamic/lazy resources/list (and directory-read) backing for gateway/proxy and template-served catalogs
Nobody has claimed this yet.
- Dominant language
- Go
- Stars
- 5.1k
- Forks
- 543
- Avg merge
- 1d 17h
- Merged PRs (30d)
- 37
Description
Problem
resources/list can only be served from a static, in-memory set. Resources are materialized up front via AddResource (removed via RemoveResources), and (*Server).listResources enumerates that featureSet directly — there is no handler/callback that computes the list on demand. For two important classes of server this isn't just inconvenient, it's a structural mismatch.
1. Gateway / proxy servers (the important case)
A skill (or resource) gateway fronts one or more upstream catalogs it does not own — another registry, a remote index, or several backend MCP servers. The authoritative list lives elsewhere and changes independently of this process. To serve resources/list from a static set, the gateway would have to mirror the entire upstream catalog into memory at startup and keep it continuously in sync — which defeats the purpose of being a gateway, doesn't scale to large/unbounded upstreams, and is racy against upstream changes.
What a gateway actually needs is to answer resources/list by querying upstream on demand, passing the client's pagination cursor through. There is no first-class way to do that today.
2. Template-served assets are non-enumerable by construction
Skills are served as a directory of files. A server typically exposes a skill's files via a resource template (skill://<skill>/{+path}) plus one read handler, so it doesn't have to AddResource every individual file. But a URI template matches infinitely many URIs — you cannot derive the file list from the template definition. So those assets are fully readable yet absent from any static set: resources/list won't show them, and resources/directory/read (below) backed by the static set returns empty.
Relationship to resources/directory/read (#956 / SEP-2640)
The Skills-over-MCP WG is adding a resources/directory/read method (SEP-2640) so hosts can enumerate the files within a skill directory — e.g. to materialize a skill to disk. Implementing that verb in this SDK depends on custom-method support (#956). But even once the verb exists, it hits exactly this problem: for a template-served or gateway-proxied skill, the directory's children cannot come from the static set — resources/directory/read must be backed by a server-provided dynamic listing.
So the two are a pair:
- #956 — adds the
resources/directory/readverb (custom method). - this issue — gives
resources/list/ directory-read a dynamic backing so template-served and gateway-proxied entries can actually be enumerated.
Neither alone lets a Go skills/gateway server both serve and enumerate a catalog whose contents aren't pre-materialized.
What already works (to scope precisely)
resources/read is already dynamic — content comes from a ResourceHandler, and resource templates (AddResourceTemplate + ResourceTemplate.Matches) let one handler serve an unbounded URI space. The asymmetry is the whole point: templates make reads unbounded, but nothing makes listing/enumeration unbounded or on-demand.
Workarounds today (and why they fall short)
- Receiving middleware.
MiddlewarewrapsMethodHandler func(ctx, method string, req Request)(Result, error), so middleware can interceptresources/listand synthesize a*ListResourcesResult, short-circuiting the static handler. It works, but it's untyped (switch on a string method, type-assertreq/Result), bypasses the SDK's built-in pagination, and sits awkwardly beside the otherwise-typed registration model. - Per-session server via the
getServercallback (as suggested in #216). Good for per-connection configuration, but it still requires materializing a concrete set per server — it doesn't help a catalog that is upstream, generated, or too large to enumerate eagerly.
Proposal
Add a first-class way to serve resources/list (and back resources/directory/read) from a handler, with cursor-based pagination handled by the SDK — analogous to how ResourceHandler/templates already make reads dynamic. Illustrative shape (not prescriptive):
// Serves resources/list on demand; receives the request (incl. pagination cursor)
// and returns a page of resources plus the next cursor.
type ListResourcesHandler func(context.Context, *ListResourcesRequest) (*ListResourcesResult, error)
opts := &mcp.ServerOptions{
ListResourcesHandler: func(ctx context.Context, req *mcp.ListResourcesRequest) (*mcp.ListResourcesResult, error) {
// e.g. proxy upstream using req.Params.Cursor, or enumerate a template's namespace ...
},
}
When set, the handler would take precedence over (or compose with) the registered static set, and the same mechanism would back directory enumeration for a directory URI.
Questions for maintainers
- Was the static-set model for
resources/lista deliberate choice (uniformity with tools/prompts feature sets), and would a dynamic list handler be acceptable — or is middleware / per-session-server the intended long-term answer for gateway and unbounded catalogs? - If a handler is acceptable: should it replace or merge with the registered set, and should the same backing apply to
resources/directory/read(#956) and, for consistency,prompts/list/tools/list?
Happy to prototype if there's interest in the direction.
Contributor guide
First steps
- Read the whole issue, then the project's contributing guide.
- Comment on the issue to say you are picking it up — it saves two people doing the same work.
- Fork the repository and make your change on a branch.
- Open a pull request that references the issue number.
Research direction
Start with (*Server).listResources and the ServerOptions registration path, then compare the existing ResourceHandler, AddResourceTemplate, and pagination behavior. Review the Middleware workaround and the relationship to custom-method support for resources/directory/read; done means the agreed dynamic listing design is implemented with clear precedence or composition semantics.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- go
- Domain
- api, backend
- Issue type
- Feature
- Difficulty
- 5/5
- Estimated time
- Over a week
- Activity status
- Quiet
- Clarity
- Mostly clear
- Newbie friendliness
- 35/100