Skip to content

McpClientSession/McpServerSession: pending response entries leak when a request times out or is cancelled #1133

Description

@Lubaoshuai

Bug description

McpClientSession.sendRequest and McpServerSession.sendRequest put an entry into pendingResponses when a request is sent, but only remove it when a response arrives or the send itself fails. If the downstream .timeout(...) fires — or the caller cancels the Mono — no cleanup hook runs, so the entry stays in pendingResponses forever (request IDs are unique per request, so it is never overwritten).

The streamable variant already cleans up after its timeout (McpStreamableServerSession.McpStreamableServerSessionStream.sendRequest calls this.pendingResponses.remove(requestId) in a doOnError after .timeout(requestTimeout)), which suggests the legacy paths missing this is an oversight.

Amplifier

KeepAliveScheduler pings every session periodically via sendRequest(PING, ...). A dead or hung session therefore accumulates one leaked entry per ping interval on both the client and the server side; long-lived sessions grow unboundedly until close().

Suggested fix

Mirror the streamable pattern in both sendRequest methods:

.timeout(this.requestTimeout)
.doOnError(e -> this.pendingResponses.remove(requestId))
.doOnCancel(() -> this.pendingResponses.remove(requestId))
.handle(...)

Removal is idempotent, and the late-response path already tolerates a missing key.

I have the fix plus a regression test (testRequestTimeoutRemovesPendingResponse in McpClientSessionTests, asserting the map is empty after a timeout) ready and will open a PR referencing this issue.

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    P2Moderate issues affecting some users, edge cases, potentially valuable featurearea/clientarea/serverarea/transportbugSomething isn't working

    Type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions