Skip to content

Commit 2cc3bc8

Browse files
committed
Add RequestOptions.timeout
1 parent 53f55b8 commit 2cc3bc8

File tree

2 files changed

+46
-3
lines changed

2 files changed

+46
-3
lines changed

src/shared/protocol.ts

Lines changed: 45 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,11 @@ export type ProtocolOptions = {
3737
enforceStrictCapabilities?: boolean;
3838
};
3939

40+
/**
41+
* The default request timeout, in miliseconds.
42+
*/
43+
export const DEFAULT_REQUEST_TIMEOUT_MSEC = 60000;
44+
4045
/**
4146
* Options that can be given per request.
4247
*/
@@ -50,6 +55,13 @@ export type RequestOptions = {
5055
* Can be used to cancel an in-flight request. This will cause an AbortError to be raised from request().
5156
*/
5257
signal?: AbortSignal;
58+
59+
/**
60+
* A timeout (in milliseconds) for this request. If exceeded, an McpError with code `RequestTimeout` will be raised from request().
61+
*
62+
* If not specified, `DEFAULT_REQUEST_TIMEOUT_MSEC` will be used as the timeout.
63+
*/
64+
timeout?: number;
5365
};
5466

5567
/**
@@ -379,7 +391,13 @@ export abstract class Protocol<
379391
};
380392
}
381393

394+
let timeoutId: ReturnType<typeof setTimeout> | undefined = undefined;
395+
382396
this._responseHandlers.set(messageId, (response) => {
397+
if (timeoutId !== undefined) {
398+
clearTimeout(timeoutId);
399+
}
400+
383401
if (options?.signal?.aborted) {
384402
return;
385403
}
@@ -396,8 +414,7 @@ export abstract class Protocol<
396414
}
397415
});
398416

399-
options?.signal?.addEventListener("abort", () => {
400-
const reason = options?.signal?.reason;
417+
const cancel = (reason: unknown) => {
401418
this._responseHandlers.delete(messageId);
402419
this._progressHandlers.delete(messageId);
403420

@@ -411,9 +428,34 @@ export abstract class Protocol<
411428
});
412429

413430
reject(reason);
431+
};
432+
433+
options?.signal?.addEventListener("abort", () => {
434+
if (timeoutId !== undefined) {
435+
clearTimeout(timeoutId);
436+
}
437+
438+
cancel(options?.signal?.reason);
414439
});
415440

416-
this._transport.send(jsonrpcRequest).catch(reject);
441+
const timeout = options?.timeout ?? DEFAULT_REQUEST_TIMEOUT_MSEC;
442+
timeoutId = setTimeout(
443+
() =>
444+
cancel(
445+
new McpError(ErrorCode.RequestTimeout, "Request timed out", {
446+
timeout,
447+
}),
448+
),
449+
timeout,
450+
);
451+
452+
this._transport.send(jsonrpcRequest).catch((error) => {
453+
if (timeoutId !== undefined) {
454+
clearTimeout(timeoutId);
455+
}
456+
457+
reject(error);
458+
});
417459
});
418460
}
419461

src/types.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -105,6 +105,7 @@ export const JSONRPCResponseSchema = z
105105
export enum ErrorCode {
106106
// SDK error codes
107107
ConnectionClosed = -1,
108+
RequestTimeout = -2,
108109

109110
// Standard JSON-RPC error codes
110111
ParseError = -32700,

0 commit comments

Comments
 (0)