Skip to content
This repository was archived by the owner on Feb 23, 2026. It is now read-only.

Commit aecfe1c

Browse files
authored
Merge pull request #62 from runbasehq/dev
refactor: enhance anthropic and openai chunk normalizers
2 parents 3976701 + e4c5810 commit aecfe1c

File tree

7 files changed

+270
-219
lines changed

7 files changed

+270
-219
lines changed

packages/mcp-check/CHANGELOG.md

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,11 @@
11
# mcp-testing-library
22

3+
## 0.4.4
4+
5+
### Patch Changes
6+
7+
- Fix how openai and anthropic handlers were normalizing chunks
8+
39
## 0.4.3
410

511
### Patch Changes

packages/mcp-check/package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
{
22
"name": "mcp-check",
33
"module": "dist/src/index.js",
4-
"version": "0.4.3",
4+
"version": "0.4.4",
55
"type": "module",
66
"main": "dist/src/index.js",
77
"types": "dist/src/index.d.ts",

packages/mcp-check/src/chunks/normalizer.ts

Lines changed: 11 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,11 @@ export class ChunkNormalizer {
2323
}
2424

2525
switch (normalizedChunk.type) {
26+
case "text_start":
27+
if (handlers.onTextStart) {
28+
await handlers.onTextStart(normalizedChunk.data);
29+
}
30+
break;
2631
case "text_delta":
2732
if (handlers.onTextDelta) {
2833
await handlers.onTextDelta(normalizedChunk.data);
@@ -33,29 +38,19 @@ export class ChunkNormalizer {
3338
await handlers.onToolCallStart(normalizedChunk.data);
3439
}
3540
break;
36-
case "tool_call_done":
37-
if (handlers.onToolCallDone) {
38-
await handlers.onToolCallDone(normalizedChunk.data);
41+
case "tool_call_delta":
42+
if (handlers.onToolCallDelta) {
43+
await handlers.onToolCallDelta(normalizedChunk.data);
3944
}
4045
break;
4146
case "tool_result":
4247
if (handlers.onToolResult) {
4348
await handlers.onToolResult(normalizedChunk.data);
4449
}
4550
break;
46-
case "message_start":
47-
if (handlers.onMessageStart) {
48-
await handlers.onMessageStart(normalizedChunk.data);
49-
}
50-
break;
51-
case "message_done":
52-
if (handlers.onMessageDone) {
53-
await handlers.onMessageDone(normalizedChunk.data);
54-
}
55-
break;
56-
case "thinking_delta":
57-
if (handlers.onThinkingDelta) {
58-
await handlers.onThinkingDelta(normalizedChunk.data);
51+
case "block_stop":
52+
if (handlers.onBlockStop) {
53+
await handlers.onBlockStop(normalizedChunk.data);
5954
}
6055
break;
6156
case "error":

packages/mcp-check/src/chunks/types.ts

Lines changed: 101 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,11 @@
11
import type {
2+
BetaRateLimitError,
23
BetaRawContentBlockDeltaEvent,
34
BetaRawContentBlockStartEvent,
45
BetaRawContentBlockStopEvent,
56
BetaRawMessageStreamEvent,
67
} from "@anthropic-ai/sdk/resources/beta.js";
7-
import type OpenAI from "openai";
8+
import type { Responses } from "openai/resources";
89

910
export interface ToolCall {
1011
/** Arguments passed to the tool function */
@@ -214,13 +215,12 @@ export interface ToolCallStats {
214215
* ```
215216
*/
216217
export type NormalizedChunkType =
218+
| "text_start"
217219
| "text_delta"
218220
| "tool_call_start"
219-
| "tool_call_done"
221+
| "tool_call_delta"
220222
| "tool_result"
221-
| "message_start"
222-
| "message_done"
223-
| "thinking_delta"
223+
| "block_stop"
224224
| "error";
225225

226226
/**
@@ -246,23 +246,76 @@ export interface BaseNormalizedChunk {
246246
type: NormalizedChunkType;
247247
/** Timestamp when the chunk was received */
248248
timestamp: number;
249+
/** Index of the chunk within the stream */
250+
index: number;
249251
/** The normalized data payload for this chunk */
250252
data: {
251-
/** Text content for text-related chunks */
252-
text?: string;
253-
/** Name of the tool for tool-related chunks */
254-
toolName?: string;
255-
/** Arguments for tool calls */
256-
toolArgs?: Record<string, any>;
257-
/** Result from tool execution */
258-
toolResult?: any;
259253
/** Error message for error chunks */
260254
error?: string;
261255
/** Additional data properties */
262256
[key: string]: any;
263257
};
264258
}
265259

260+
export interface NormalizedTextDeltaChunk extends BaseNormalizedChunk {
261+
type: "text_delta";
262+
data: {
263+
/** Text content for text-related chunks */
264+
textDelta: string;
265+
266+
}
267+
}
268+
269+
export interface NormalizedToolCallStartChunk extends BaseNormalizedChunk {
270+
type: "tool_call_start";
271+
data: {
272+
/** ID of the tool for tool-related chunks */
273+
toolId: string;
274+
/** Name of the tool for tool-related chunks */
275+
toolName: string;
276+
}
277+
}
278+
279+
export interface NormalizedToolCallDeltaChunk extends BaseNormalizedChunk {
280+
type: "tool_call_delta";
281+
data: {
282+
/** Text content for tool-related chunks */
283+
toolDelta: string;
284+
}
285+
}
286+
287+
export interface NormalizedTextStartChunk extends BaseNormalizedChunk {
288+
type: "text_start";
289+
data: {
290+
/** Initial text content */
291+
text?: string;
292+
}
293+
}
294+
295+
export interface NormalizedToolResultChunk extends BaseNormalizedChunk {
296+
type: "tool_result";
297+
data: {
298+
toolId: string;
299+
isError: boolean;
300+
toolResult: any;
301+
}
302+
}
303+
304+
export interface NormalizedBlockStopChunk extends BaseNormalizedChunk {
305+
type: "block_stop";
306+
data: {}
307+
}
308+
309+
export interface NormalizedErrorChunk extends BaseNormalizedChunk {
310+
type: "error";
311+
data: {
312+
/** Error message */
313+
error: string;
314+
/** Error code if available */
315+
errorCode?: string;
316+
}
317+
}
318+
266319
/**
267320
* Union type of all possible normalized chunk types.
268321
*
@@ -271,16 +324,26 @@ export interface BaseNormalizedChunk {
271324
*
272325
*
273326
**/
274-
export type NormalizedChunk = NormalizedChunkAnthropic | NormalizedChunkOpenAI;
275-
export interface NormalizedChunkAnthropic extends BaseNormalizedChunk {
327+
export type AgnosticNormalizedChunk =
328+
| NormalizedTextStartChunk
329+
| NormalizedTextDeltaChunk
330+
| NormalizedToolCallStartChunk
331+
| NormalizedToolCallDeltaChunk
332+
| NormalizedToolResultChunk
333+
| NormalizedBlockStopChunk
334+
| NormalizedErrorChunk;
335+
336+
export type NormalizedChunkAnthropic = (AgnosticNormalizedChunk & {
276337
provider: "anthropic";
277-
originalChunk: BetaRawMessageStreamEvent;
278-
}
338+
originalChunk: BetaRawMessageStreamEvent | BetaRateLimitError;
339+
});
279340

280-
export interface NormalizedChunkOpenAI extends BaseNormalizedChunk {
341+
export type NormalizedChunkOpenAI = (AgnosticNormalizedChunk & {
281342
provider: "openai" | "openrouter";
282-
originalChunk: OpenAI.Responses.ResponseStreamEvent;
283-
}
343+
originalChunk: Responses.ResponseOutputItemAddedEvent | Responses.ResponseOutputItemDoneEvent | Responses.ResponseStreamEvent | Responses.ResponseOutputItem | Responses.ResponseOutputItemDoneEvent;
344+
});
345+
346+
export type NormalizedChunk = NormalizedChunkAnthropic | NormalizedChunkOpenAI;
284347

285348
/**
286349
* Generic callback function for handling any normalized chunk.
@@ -319,7 +382,7 @@ export type ChunkCallback = (chunk: NormalizedChunk) => void | Promise<void>;
319382
* };
320383
* ```
321384
*/
322-
export type ChunkTypeCallback = (data: NormalizedChunk["data"]) => void | Promise<void>;
385+
export type ChunkTypeCallback<T extends AgnosticNormalizedChunk = AgnosticNormalizedChunk> = (data: T["data"]) => void | Promise<void>;
323386

324387
/**
325388
* Configuration object for chunk handlers.
@@ -331,9 +394,12 @@ export type ChunkTypeCallback = (data: NormalizedChunk["data"]) => void | Promis
331394
* @example
332395
* ```typescript
333396
* const handlers: ChunkHandlerConfig = {
397+
* onTextStart: (data) => console.log("Text start:", data.text),
334398
* onTextDelta: (data) => console.log("Text:", data.text),
335399
* onToolCallStart: (data) => console.log("Tool started:", data.toolName),
336-
* onToolCallDone: (data) => console.log("Tool finished:", data.toolName),
400+
* onToolCallDelta: (data) => console.log("Tool delta:", data.toolName),
401+
* onToolResultStart: (data) => console.log("Tool result:", data.toolResult),
402+
* onBlockStop: (data) => console.log("Block stopped"),
337403
* onError: (data) => console.error("Error:", data.error),
338404
* onAnyChunk: (chunk) => console.log("Any chunk:", chunk.type),
339405
* anthropic: {
@@ -346,22 +412,20 @@ export type ChunkTypeCallback = (data: NormalizedChunk["data"]) => void | Promis
346412
* ```
347413
*/
348414
export interface ChunkHandlerConfig {
415+
/** Callback for text start chunks */
416+
onTextStart?: ChunkTypeCallback<NormalizedTextStartChunk>;
349417
/** Callback for text delta chunks */
350-
onTextDelta?: ChunkTypeCallback;
418+
onTextDelta?: ChunkTypeCallback<NormalizedTextDeltaChunk>;
351419
/** Callback for tool call start chunks */
352-
onToolCallStart?: ChunkTypeCallback;
353-
/** Callback for tool call done chunks */
354-
onToolCallDone?: ChunkTypeCallback;
355-
/** Callback for tool result chunks */
356-
onToolResult?: ChunkTypeCallback;
357-
/** Callback for message start chunks */
358-
onMessageStart?: ChunkTypeCallback;
359-
/** Callback for message done chunks */
360-
onMessageDone?: ChunkTypeCallback;
361-
/** Callback for thinking delta chunks */
362-
onThinkingDelta?: ChunkTypeCallback;
420+
onToolCallStart?: ChunkTypeCallback<NormalizedToolCallStartChunk>;
421+
/** Callback for tool call delta chunks */
422+
onToolCallDelta?: ChunkTypeCallback<NormalizedToolCallDeltaChunk>;
423+
/** Callback for tool result start chunks */
424+
onToolResult?: ChunkTypeCallback<NormalizedToolResultChunk>;
425+
/** Callback for block stop chunks */
426+
onBlockStop?: ChunkTypeCallback<NormalizedBlockStopChunk>;
363427
/** Callback for error chunks */
364-
onError?: ChunkTypeCallback;
428+
onError?: ChunkTypeCallback<NormalizedErrorChunk>;
365429
/** Callback for any chunk type */
366430
onAnyChunk?: ChunkCallback;
367431

@@ -384,11 +448,11 @@ export interface ChunkHandlerConfig {
384448
openai?: {
385449
/** Handler for OpenAI response output item added chunks */
386450
onResponseOutputItemAdded?: (
387-
chunk: OpenAI.Responses.ResponseOutputItemAddedEvent,
451+
chunk: Responses.ResponseOutputItemAddedEvent,
388452
) => void | Promise<void>;
389453
/** Handler for OpenAI response output item done chunks */
390454
onResponseOutputItemDone?: (
391-
chunk: OpenAI.Responses.ResponseOutputItemDoneEvent,
455+
chunk: Responses.ResponseOutputItemDoneEvent,
392456
) => void | Promise<void>;
393457
};
394458
}

0 commit comments

Comments
 (0)