Skip to content

handleAutomaticTaskPolling ignores AbortSignal; cancelled requests poll indefinitely #2018

Description

@blackwell-systems

Initial Checks

  • Using the latest version of MCP TypeScript SDK
  • Searched existing issues

Description

In packages/server/src/server/mcp.ts, handleAutomaticTaskPolling contains a while loop (lines 328-335) that polls a task store until completion. The loop never checks ctx.mcpReq.signal.aborted. If the client cancels the request, the poll loop continues consuming server resources indefinitely.

while (task.status !== 'completed' && task.status !== 'failed' && task.status !== 'cancelled') {
    await new Promise(resolve => setTimeout(resolve, pollInterval));
    const updatedTask = await ctx.task.store.getTask(taskId);
    // ...
}

The taskManager.ts implementation of the same pattern correctly checks the signal (line 856):

if (signal.aborted) {
    resolver(new ProtocolError(ProtocolErrorCode.InternalError, 'Task cancelled or completed'));
    break;
}

Impact

On multi-tenant servers, a single cancelled long-running tool leaks a polling loop per cancelled request. Over time this accumulates.

Suggested fix

while (task.status !== 'completed' && task.status !== 'failed' && task.status !== 'cancelled') {
    if (ctx.mcpReq.signal.aborted) {
        throw new ProtocolError(ProtocolErrorCode.RequestCancelled, 'Request cancelled during task polling');
    }
    await new Promise(resolve => setTimeout(resolve, pollInterval));
    // ...
}

Metadata

Metadata

Assignees

No one assigned

    Labels

    potentially closeBot recommends closing — needs maintainer review

    Type

    No type

    Fields

    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions