mcp: tool discovery livelocks against a server that notifies list_changed after every tools/list
#2981 aperta il 13 ago 2026
Metriche repository
- Star
- (1 stella)
- Metriche merge PR
- (Metriche PR in attesa)
Descrizione
Context
A server that emits tools/list_changed after every tools/list response livelocks tool discovery. Each notification schedules another discovery pass, whose response triggers the next notification, with no bound on the loop.
Measured with a probe server that notifies on every list: ~2000 tools/list round trips in 4 seconds, sync() never settling, and the manager unable to remove that server from the configuration — the removal sync queues behind a discovery that never finishes.
This is pre-existing, not a regression. I found it while reviewing #2663 and initially took it for a regression from installing the change handler before initial discovery; it is not. The same probe against origin/main produces the same ~2000-iteration loop. Noted in that review as out of scope there.
Severity
P2. Unlike most items in this class, something on main genuinely misbehaves rather than merely lacking protection: discovery hangs and configuration reconciliation stalls behind it. Held at P2 because triggering requires a server that notifies on every list — real servers notify on actual change — and it self-recovers the moment the server stops notifying. It has been in place a long time with no report.
Fix
Bound the rediscovery loop. Options, roughly in order of preference:
- Debounce
list_changed— collapse notifications that arrive while a discovery for the same server is in flight or within a short window after it completes. - Cap consecutive rediscoveries per connection generation, then stop honouring further notifications and surface the server as degraded.
- At minimum, let a pending configuration sync preempt an in-flight discovery, so a misbehaving server can always be removed.
(3) is worth having regardless of (1) and (2): the manager should never be unable to drop a server.
Verification
A probe server whose tools/list handler schedules sendToolListChanged() on every call. Assert that list round trips stay bounded, that sync() settles, and that a follow-up sync() removing the server completes.