Fix MCP server tools/list infinite loop caused by notification race condition#14376
Open
Fix MCP server tools/list infinite loop caused by notification race condition#14376
Conversation
…handler and adding change detection The MCP server was entering an infinite tools/list loop because: 1. HandleListToolsAsync sent tools/list_changed notification after refreshing 2. The client responded with another tools/list request 3. This created a feedback loop: list → changed → list → changed Fix: - Remove SendToolsListChangedNotificationAsync from HandleListToolsAsync (the client already gets the fresh list since it requested it) - Add change detection to RefreshResourceToolMapAsync (returns bool Changed) - Only send tools/list_changed in HandleCallToolAsync when tools actually changed - RefreshToolsTool always sends notification (explicit user action) Co-authored-by: maddymontaquila <12660687+maddymontaquila@users.noreply.github.com>
Co-authored-by: maddymontaquila <12660687+maddymontaquila@users.noreply.github.com>
Copilot
AI
changed the title
[WIP] Fix infinite loop in tools/list on db-mcp tools register
Fix MCP server tools/list infinite loop caused by notification race condition
Feb 6, 2026
Contributor
|
🚀 Dogfood this PR with:
curl -fsSL https://raw.githubusercontent.com/dotnet/aspire/main/eng/scripts/get-aspire-cli-pr.sh | bash -s -- 14376Or
iex "& { $(irm https://raw.githubusercontent.com/dotnet/aspire/main/eng/scripts/get-aspire-cli-pr.ps1) } 14376" |
Member
|
@maddymontaquila release/13.2 instead? I can handle it for you |
Contributor
There was a problem hiding this comment.
Pull request overview
Fixes an infinite tools/list ↔ notifications/tools/list_changed feedback loop in the Aspire MCP server by removing notifications from the tools/list request path and adding tool-set change detection to gate when notifications are emitted.
Changes:
- Stop sending
tools/list_changedas a side effect oftools/listhandling. - Add “changed” detection to resource tool refresh (
RefreshResourceToolMapAsync) and only notify on actual tool-set changes during tool calls. - Add a regression test ensuring
tools/listdoes not emittools/list_changed.
Reviewed changes
Copilot reviewed 5 out of 5 changed files in this pull request and generated 2 comments.
Show a summary per file
| File | Description |
|---|---|
| tests/Aspire.Cli.Tests/Commands/AgentMcpCommandTests.cs | Adds coverage to ensure tools/list does not trigger tools/list_changed. |
| src/Aspire.Cli/Commands/AgentMcpCommand.cs | Removes notification from list handler; gates notification in call handler based on change detection. |
| src/Aspire.Cli/Mcp/McpResourceToolRefreshService.cs | Returns (ToolMap, Changed) and computes tool-key set differences. |
| src/Aspire.Cli/Mcp/IMcpResourceToolRefreshService.cs | Updates interface contract to return map + change flag. |
| src/Aspire.Cli/Mcp/Tools/RefreshToolsTool.cs | Adapts to new tuple return while preserving explicit refresh behavior. |
Member
|
@copilot apply changes based on the comments in this thread |
… channel wait in test - Replace HashSet.SetEquals with count check + iterate keys + ContainsKey to avoid allocation under lock in McpResourceToolRefreshService. - Replace Task.Delay(200) in test with Channel.ReadAsync + CancellationTokenSource timeout for more deterministic negative assertion. Co-authored-by: sebastienros <1165805+sebastienros@users.noreply.github.com>
Contributor
Author
Applied both review comments in
|
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Description
HandleListToolsAsyncwas sendingtools/list_changednotifications as a side effect of handlingtools/listrequests. When container MCP tools (e.g., db-mcp) oscillated between available/unavailable during enumeration, this created a tight feedback loop:tools/list→tools/list_changed→tools/list→ … (~1,742 calls in ~2m44s until the client killed the server).Changes:
HandleListToolsAsyncno longer sendstools/list_changed. The client already receives the fresh list since it initiated the request.RefreshResourceToolMapAsync— Returns(ToolMap, Changed)tuple. Compares old vs new tool key sets via zero-allocation iteration (count check +ContainsKey) before reporting a change.HandleCallToolAsync— Only sendstools/list_changedwhen the tool set actually changed during a refresh triggered by a tool call.RefreshToolsToolunchanged semantically — Still always notifies since it's an explicit user action.Checklist
Original prompt
💬 We'd love your input! Share your thoughts on Copilot coding agent in our 2 minute survey.