8000 fix(serve): answer tools/list from cache during background refresh by ozkanerbatuhan · Pull Request #124 · avelino/mcp · GitHub
[go: up one dir, main page]

Skip to content

fix(serve): answer tools/list from cache during background refresh - #124

Merged
avelino merged 1 commit into
avelino:mainfrom
ozkanerbatuhan:fix/tools-list-cache-refresh
Nov 10, 2026
Merged

avelino merged 1 commit into
avelino:mainfrom
ozkanerbatuhan:fix/tools-list-cache-refresh

Conversation

@ozkanerbatuhan
Copy link
Copy Markdown
Contributor

Part of #121 (item 3); affects all platforms.

After startup, reset_cache_loaded_for_refresh drops cache-loaded backends from discovered_backends, so a tools/list in that window blocked on discovering every backend. tools/list now uses has_backends_without_tools(), which ignores backends whose tools are already registered from the cache; backends with no cache entry still trigger discovery (the existing partial-cache test is unchanged and passes).

New test test_tools_list_serves_cache_during_background_refresh fails without the fix and passes with it. Measured with 9 stdio backends over HTTP: first tools/list after a restart 5.4 s → 0.1–0.7 s.

🤖 Generated with Claude Code

@avelino avelino left a comment
Copy link
Copy Markdown
Owner 10000

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nice catch, @ozkanerbatuhan. Blocking tools/list on rediscovery right after a restart throws away the whole point of the persistent cache, and has_backends_without_tools keeps the partial-cache case going through discovery, which is exactly the line I'd want. The test reproducing the window is great, 5.4 s down to under a second is a real win.

One mechanical thing: main grew an extra field on protocol::Tool since you branched, so the new test no longer compiles on top of main (E0063: missing field extra, checked locally merging this branch into current main). Could you rebase and add it? After that I'm happy to merge.

Comment thread src/serve/dispatch.rs
max_idle_timeout: None,
},
);
let tool = Tool {
Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Tool has an extra: ExtraFields field on main now, so this initializer fails to compile after a rebase. Something like this should do it:

let tool = Tool {
    name: "lookup".to_string(),
    description: Some("Lookup".to_string()),
    input_schema: None,
    annotations: None,
    extra: Default::default(),
};

@ozkanerbatuhan
ozkanerbatuhan force-pushed the fix/tools-list-cache-refresh branch from d752243 to f659fbf Compare November 10, 2026 02:48
After startup the proxy loads tools from the persistent cache, then the
background refresh calls `reset_cache_loaded_for_refresh`, which removes every
cache-loaded backend from `discovered_backends`. A `tools/list` arriving in
that window saw `has_undiscovered_backends() == true` and blocked on
`discover_pending_backends` until every backend had been spawned and listed -
exactly the wait the cache exists to avoid. With 9 backends that is 5-12 s per
proxy restart, long enough for a client's tools fetch to time out.

`tools/list` now asks `has_backends_without_tools()`: like
`has_undiscovered_backends`, but a backend whose tools are already registered
from the cache (Disconnected with non-empty `cached_tools`) does not force a
blocking discovery. Backends with no cache entry still do, so the
partial-cache case keeps working (`tools_list_discovers_backends_missing_
from_a_partial_cache` unchanged and passing). The refresh itself still runs in
the background and updates the list.

Measured (HTTP mode, 9 stdio backends): tools/list right after a restart went
from 5.4 s to 0.1-0.7 s.
@avelino avelino left a comment
Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Rebased and the extra field is in, thanks @ozkanerbatuhan. I ran it on top of main together with #122 and the full suite passes locally (1139), CI is green too. Going from a 5s first tools/list after a restart to under a second is the kind of fix people feel without knowing why, and the test pins exactly the window that was hurting. Merging.

@avelino
avelino merged commit 060fad6 into avelino:main Nov 10, 2026
3 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants

0