Initial Checks
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));
// ...
}
Initial Checks
Description
In
packages/server/src/server/mcp.ts,handleAutomaticTaskPollingcontains a while loop (lines 328-335) that polls a task store until completion. The loop never checksctx.mcpReq.signal.aborted. If the client cancels the request, the poll loop continues consuming server resources indefinitely.The
taskManager.tsimplementation of the same pattern correctly checks the signal (line 856):Impact
On multi-tenant servers, a single cancelled long-running tool leaks a polling loop per cancelled request. Over time this accumulates.
Suggested fix