Skip to content

Commit 973aed9

Browse files
committed
feat: 60 second timeout
1 parent 7268b17 commit 973aed9

File tree

1 file changed

+46
-3
lines changed

1 file changed

+46
-3
lines changed

packages/agents/src/mcp/rpc.ts

Lines changed: 46 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -319,6 +319,21 @@ export interface RPCServerTransportOptions {
319319
* ```
320320
*/
321321
onsessionclosed?: (sessionId: string) => void | Promise<void>;
322+
323+
/**
324+
* Timeout in milliseconds for waiting for a response from the onmessage handler.
325+
* If the handler doesn't call send() within this time, the request will fail with a timeout error.
326+
*
327+
* @default 60000 (60 seconds)
328+
*
329+
* @example
330+
* ```typescript
331+
* const transport = new RPCServerTransport({
332+
* timeout: 30000 // 30 seconds
333+
* });
334+
* ```
335+
*/
336+
timeout?: number;
322337
}
323338

324339
export class RPCServerTransport implements Transport {
@@ -331,6 +346,7 @@ export class RPCServerTransport implements Transport {
331346
private _initialized = false;
332347
private _onsessioninitialized?: (sessionId: string) => void | Promise<void>;
333348
private _onsessionclosed?: (sessionId: string) => void | Promise<void>;
349+
private _timeout: number;
334350

335351
sessionId?: string;
336352
onclose?: () => void;
@@ -341,6 +357,7 @@ export class RPCServerTransport implements Transport {
341357
this._sessionIdGenerator = options?.sessionIdGenerator;
342358
this._onsessioninitialized = options?.onsessioninitialized;
343359
this._onsessionclosed = options?.onsessionclosed;
360+
this._timeout = options?.timeout ?? 60000; // Default 60 seconds
344361
}
345362

346363
setProtocolVersion(version: string): void {
@@ -516,14 +533,40 @@ export class RPCServerTransport implements Transport {
516533
this._currentRequestId = (message as { id: string | number | null }).id;
517534

518535
// Set up the promise before calling onmessage to handle race conditions
519-
const responsePromise = new Promise<void>((resolve) => {
520-
this._responseResolver = resolve;
536+
let timeoutId: ReturnType<typeof setTimeout> | null = null;
537+
const responsePromise = new Promise<void>((resolve, reject) => {
538+
// Set up timeout
539+
timeoutId = setTimeout(() => {
540+
this._responseResolver = null;
541+
reject(
542+
new Error(
543+
`Request timeout: No response received within ${this._timeout}ms for request ID ${this._currentRequestId}`
544+
)
545+
);
546+
}, this._timeout);
547+
548+
// Wrap the resolver to clear timeout when response is received
549+
this._responseResolver = () => {
550+
if (timeoutId) {
551+
clearTimeout(timeoutId);
552+
timeoutId = null;
553+
}
554+
resolve();
555+
};
521556
});
522557

523558
this.onmessage?.(message);
524559

525560
// Wait for a response using a promise that resolves when send() is called
526-
await responsePromise;
561+
try {
562+
await responsePromise;
563+
} catch (error) {
564+
// Clean up on timeout
565+
this._pendingResponse = null;
566+
this._currentRequestId = null;
567+
this._responseResolver = null;
568+
throw error;
569+
}
527570

528571
const response = this._pendingResponse;
529572
this._pendingResponse = null;

0 commit comments

Comments
 (0)