Skip to content

Commit 563b0de

Browse files
fix: HTTP Stream Transport Buffering (#91)
* fix: HTTP Stream Transport Buffering * test: add progress update test to fix buffering issue * desc * add tool for testing progress
1 parent 7dc34e1 commit 563b0de

File tree

3 files changed

+103
-0
lines changed

3 files changed

+103
-0
lines changed

src/FastMCP.test.ts

Lines changed: 70 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -514,6 +514,76 @@ test("tracks tool progress", async () => {
514514
});
515515
});
516516

517+
test("reports multiple progress updates without buffering", async () => {
518+
await runWithTestServer({
519+
run: async ({ client }) => {
520+
const progressCalls: Array<{ progress: number; total: number }> = [];
521+
const onProgress = vi.fn((data) => {
522+
progressCalls.push(data);
523+
});
524+
525+
await client.callTool(
526+
{
527+
arguments: {
528+
steps: 3,
529+
},
530+
name: "progress-test",
531+
},
532+
undefined,
533+
{
534+
onprogress: onProgress,
535+
},
536+
);
537+
538+
expect(onProgress).toHaveBeenCalledTimes(4);
539+
expect(progressCalls).toEqual([
540+
{ progress: 0, total: 100 },
541+
{ progress: 50, total: 100 },
542+
{ progress: 90, total: 100 },
543+
{ progress: 100, total: 100 }, // This was previously lost due to buffering
544+
]);
545+
},
546+
server: async () => {
547+
const server = new FastMCP({
548+
name: "Test",
549+
version: "1.0.0",
550+
});
551+
552+
server.addTool({
553+
description: "Test tool for progress buffering fix",
554+
execute: async (args, { reportProgress }) => {
555+
const { steps } = args;
556+
557+
// Initial
558+
await reportProgress({ progress: 0, total: 100 });
559+
560+
for (let i = 1; i <= steps; i++) {
561+
await delay(50); // Small delay to simulate work
562+
563+
if (i === 1) {
564+
await reportProgress({ progress: 50, total: 100 });
565+
} else if (i === 2) {
566+
await reportProgress({ progress: 90, total: 100 });
567+
}
568+
}
569+
570+
// This was the critical test case that failed before the fix
571+
// because there's no await after it, causing it to be buffered
572+
await reportProgress({ progress: 100, total: 100 });
573+
574+
return "Progress test completed";
575+
},
576+
name: "progress-test",
577+
parameters: z.object({
578+
steps: z.number(),
579+
}),
580+
});
581+
582+
return server;
583+
},
584+
});
585+
});
586+
517587
test("sets logging levels", async () => {
518588
await runWithTestServer({
519589
run: async ({ client, session }) => {

src/FastMCP.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1369,6 +1369,8 @@ export class FastMCPSession<
13691369
progressToken,
13701370
},
13711371
});
1372+
1373+
await new Promise((resolve) => setImmediate(resolve));
13721374
};
13731375

13741376
const log = {
@@ -1423,6 +1425,8 @@ export class FastMCPSession<
14231425
toolName: request.params.name,
14241426
},
14251427
});
1428+
1429+
await new Promise((resolve) => setImmediate(resolve));
14261430
};
14271431

14281432
const executeToolPromise = tool.execute(args, {

src/examples/addition.ts

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -155,6 +155,35 @@ server.addTool({
155155
}),
156156
});
157157

158+
server.addTool({
159+
annotations: {
160+
openWorldHint: false,
161+
readOnlyHint: false,
162+
},
163+
description: "Test progress reporting without buffering delays",
164+
execute: async (args, { reportProgress }) => {
165+
console.log("Testing progress reporting fix for HTTP Stream buffering...");
166+
167+
await reportProgress({ progress: 0, total: 100 });
168+
await new Promise((resolve) => setTimeout(resolve, 500));
169+
170+
await reportProgress({ progress: 25, total: 100 });
171+
await new Promise((resolve) => setTimeout(resolve, 500));
172+
173+
await reportProgress({ progress: 75, total: 100 });
174+
await new Promise((resolve) => setTimeout(resolve, 500));
175+
176+
// This progress should be received immediately
177+
await reportProgress({ progress: 100, total: 100 });
178+
179+
return `Buffering test completed for ${args.testCase}`;
180+
},
181+
name: "test-buffering-fix",
182+
parameters: z.object({
183+
testCase: z.string().describe("Test case description"),
184+
}),
185+
});
186+
158187
server.addPrompt({
159188
arguments: [
160189
{

0 commit comments

Comments
 (0)