Skip to content

Commit 4633cb1

Browse files
fix(server): convert mid-poll task errors and align config-error handling
Addresses review from @bhosmer-ant on #1769: - handleAutomaticTaskPolling: wrap getTask in try/catch and convert ProtocolError(InvalidParams) to InternalError when a task vanishes mid-poll. A task going missing during automatic polling is a server-side issue — the client didn't request a task. - handleAutomaticTaskPolling: change plain Error('No task store...') to ProtocolError(InternalError) for consistency with the analogous taskSupport config check at L177. - Update three tests to assert error codes (not just messages) using toMatchObject, matching the pattern at L1824. Follow-up (non-blocking): test coverage for the auto-polling output validation path added in 3f29f35.
1 parent 2502215 commit 4633cb1

2 files changed

Lines changed: 28 additions & 5 deletions

File tree

packages/server/src/server/mcp.ts

Lines changed: 16 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -310,7 +310,7 @@ export class McpServer {
310310
ctx: ServerContext
311311
): Promise<CallToolResult> {
312312
if (!ctx.task?.store) {
313-
throw new Error('No task store provided for task-capable tool.');
313+
throw new ProtocolError(ProtocolErrorCode.InternalError, 'No task store provided for task-capable tool.');
314314
}
315315

316316
// Validate input and create task using the executor
@@ -324,7 +324,21 @@ export class McpServer {
324324

325325
while (task.status !== 'completed' && task.status !== 'failed' && task.status !== 'cancelled') {
326326
await new Promise(resolve => setTimeout(resolve, pollInterval));
327-
const updatedTask = await ctx.task.store.getTask(taskId);
327+
let updatedTask;
328+
try {
329+
updatedTask = await ctx.task.store.getTask(taskId);
330+
} catch (error) {
331+
// RequestTaskStore.getTask throws InvalidParams when the task is
332+
// missing, but a task vanishing mid-poll is a server-side issue
333+
// (the client didn't even ask for a task) — surface as InternalError.
334+
if (error instanceof ProtocolError && error.code === ProtocolErrorCode.InvalidParams) {
335+
throw new ProtocolError(
336+
ProtocolErrorCode.InternalError,
337+
`Task ${taskId} vanished during automatic polling`
338+
);
339+
}
340+
throw error;
341+
}
328342
if (!updatedTask) {
329343
throw new ProtocolError(ProtocolErrorCode.InternalError, `Task ${taskId} not found during polling`);
330344
}

test/integration/test/server/mcp.test.ts

Lines changed: 12 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1426,7 +1426,10 @@ describe('Zod v4', () => {
14261426
input: 'hello'
14271427
}
14281428
})
1429-
).rejects.toThrow('Output validation error: Tool test has an output schema but no structured content was provided');
1429+
).rejects.toMatchObject({
1430+
code: ProtocolErrorCode.InternalError,
1431+
message: expect.stringContaining('has an output schema but no structured content')
1432+
});
14301433
});
14311434
/***
14321435
* Test: Tool with Output Schema Must Provide Structured Content
@@ -1548,7 +1551,10 @@ describe('Zod v4', () => {
15481551
input: 'hello'
15491552
}
15501553
})
1551-
).rejects.toThrow(/Output validation error: Invalid structured content for tool test/);
1554+
).rejects.toMatchObject({
1555+
code: ProtocolErrorCode.InternalError,
1556+
message: expect.stringMatching(/Invalid structured content for tool test/)
1557+
});
15521558
});
15531559

15541560
/***
@@ -6522,7 +6528,10 @@ describe('Zod v4', () => {
65226528
name: 'long-running-task',
65236529
arguments: { input: 'test data' }
65246530
})
6525-
).rejects.toThrow(/requires task augmentation/);
6531+
).rejects.toMatchObject({
6532+
code: ProtocolErrorCode.InvalidParams,
6533+
message: expect.stringMatching(/requires task augmentation/)
6534+
});
65266535

65276536
taskStore.cleanup();
65286537
});

0 commit comments

Comments
 (0)