Repository navigation
Conversation
There was a problem hiding this comment.
The fix looks good. I ran the two new tests against the base: both fail there (9 catalog reads sent 9 list requests of each kind), and they pass with this change. The rest of the MCP tests in packages/core, packages/server/test/client-mcp.test.ts and the core type check (typecheck:tsc) also pass. One unrelated test, "terminates MCP descendants after the wrapper exits successfully", fails on the base too.
I checked the concurrency: a load that finishes after list_changed or stopServer only resolves into a handle that later readers no longer use, so it can't bring back an old catalog. Session recovery goes through stopServer, and a reader that stops waiting doesn't cancel the shared load.
Public API: unchanged.
One small point is noted inline.
There was a problem hiding this comment.
The new commit addresses my earlier point: template listing now returns an empty list only on MethodNotFound, and any other template failure fails the load, so it isn't cached and the next read tries again. packages/core/test/mcp.test.ts passes except "terminates MCP descendants after the wrapper exits successfully", which also fails on the base, and the core type check (typecheck:tsc) passes.
Public API: unchanged.
A side effect to be aware of: a server that reports an unimplemented resources/templates/list with some other error code (e.g. -32603) used to show its resources with no templates. Now resourceCatalog() drops that server, and resources() fails for it on every read. If you want to keep those resources visible, you could fall back to an empty template list on other errors without caching that result. That's your call, not a blocker.
There was a problem hiding this comment.
The new commits address my last point: if template listing fails with an error other than MethodNotFound, the resources are still returned and that result isn't cached. Servers that don't advertise resources.listChanged are also listed again after 30 seconds, so they can't stay stale until a reconnect.
packages/core/test/mcp.test.ts passes except "terminates MCP descendants after the wrapper exits successfully", which also fails on the base. The resource tests passed on each of 3 runs, the other core MCP test files pass, and the core type check (typecheck:tsc) passes. The 30-second expiry test moves a clock forward instead of sleeping.
Public API: unchanged. resourceListChanged is added only to the internal MCP connection type in packages/core.
No further concerns.
There was a problem hiding this comment.
The new commit is sound. The cached catalog now belongs to the connection that loaded it, so a replacement connection never reads an old one's catalog. Failed loads, and loads where template listing failed, are kept for 5 seconds instead of being retried on every read, which stops a rate-limited server from being asked again immediately.
packages/core/test/mcp.test.ts passes except "terminates MCP descendants after the wrapper exits successfully", which also fails on the base. The resource tests passed on each of 3 runs, the other core MCP test files pass, and the core type check (typecheck:tsc) passes.
Public API: unchanged.
No further concerns.
There was a problem hiding this comment.
Expiry is now counted from when a load finishes, which covers the slow-failure case I mentioned last time. While a load is running the cache stays valid, so readers arriving at the same time still share one request.
packages/core/test/mcp.test.ts passes except "terminates MCP descendants after the wrapper exits successfully", which also fails on the base. The resource tests passed on each of 3 runs, the new slow-load tests use a fake clock instead of sleeping, and the core type check (typecheck:tsc) passes.
Public API: unchanged.
No further concerns.
d04ec1b to
904490e
Compare
904490e to
2aac31b
Compare
There was a problem hiding this comment.
The force-push removes the time limits from the last two rounds. The cache now matches tools and prompts: it belongs to the connection that filled it, it is cleared on list_changed and when the connection stops, and failed or incomplete loads aren't cached. Template listing still returns an empty list only on MethodNotFound.
packages/core/test/mcp.test.ts passes except "terminates MCP descendants after the wrapper exits successfully", which also fails on the base. The resource tests passed on each of 3 runs, the other core MCP test files pass, and the core type check (typecheck:tsc) passes. The tests no longer depend on timing.
Public API: unchanged.
One tradeoff is left for maintainers to decide, noted inline: servers that don't advertise resources.listChanged now keep their catalog until they reconnect.
Issue for this PR
Closes #52852
Type of change
What does this PR do?
Every resource catalog read sent new
resources/listandresources/templates/listrequests to every connected server. Clients re-read the catalog on eachmcp.resources.changed, and every connect or disconnect emits that event, so reconnecting a few servers multiplied traffic. Rate-limited remote servers answered 429 and ended up stuck as failed.This caches the catalog per connection, the same way tools and prompts are already cached, as suggested on #52852:
notifications/resources/list_changedand when the connection stops (reconnect and session recovery both go throughstopServer).MethodNotFoundfrom template listing means "no templates". If template listing fails any other way, the resources are still returned but not cached.resources.listChangedkeeps its catalog until it reconnects. Before this change, every read picked up changes.How did you verify your code works?
New tests in
packages/core/test/mcp.test.ts:lists MCP resources once per connection until the server reports a change: 9 catalog reads send 1 list request of each kind; a change notification triggers one reload, and so does a reconnect. Before the fix, the 9 reads sent 9 of each.lists MCP resources again after a failed resource listkeeps MCP resources when template listing fails, without caching themcaches MCP resources from a server that does not implement template listingTwo existing tests changed resources on the server without telling the client. They now send
list_changedfirst.Ran
bun run script/test.tson alltest/mcp*.test.tsfiles (134 pass), the resource tests with--rerun-each 10,packages/server/test/client-mcp.test.ts, andbun typecheckinpackages/core.Checklist
Added by the opencode bot (AI-generated).
Related: #52854, #51645