MCPcopy
hub / github.com/coder/mux / runMCPToolWithDeadline

Function runMCPToolWithDeadline

src/node/services/mcpServerManager.ts:74–126  ·  view source on GitHub ↗
(
  start: () => Promise<T>,
  opts: { toolName: string; timeoutMs: number; signal?: AbortSignal }
)

Source from the content-addressed store, hash-verified

72 * so abort cannot leave orphaned timers or dangling promises.
73 */
74export async function runMCPToolWithDeadline<T>(
75 start: () => Promise<T>,
76 opts: { toolName: string; timeoutMs: number; signal?: AbortSignal }
77): Promise<T> {
78 const { signal, timeoutMs, toolName } = opts;
79
80 // Pre-abort short-circuit: skip all async work if already canceled.
81 if (signal?.aborted) {
82 throw new MCPDeadlineError("Interrupted");
83 }
84
85 let timeoutHandle: ReturnType<typeof setTimeout> | undefined;
86 let cleanupAbort: (() => void) | undefined;
87
88 // Lazy start: tool execution begins only after pre-abort check passes.
89 const op = Promise.resolve().then(start);
90
91 const timeout = new Promise<never>((_resolve, reject) => {
92 timeoutHandle = setTimeout(
93 () => reject(new MCPDeadlineError(`MCP tool '${toolName}' timed out after ${timeoutMs}ms`)),
94 timeoutMs
95 );
96 if (
97 timeoutHandle !== undefined &&
98 typeof timeoutHandle === "object" &&
99 "unref" in timeoutHandle &&
100 typeof timeoutHandle.unref === "function"
101 ) {
102 timeoutHandle.unref();
103 }
104 });
105
106 const aborted = signal
107 ? new Promise<never>((_resolve, reject) => {
108 const onAbort = () => reject(new MCPDeadlineError("Interrupted"));
109 signal.addEventListener("abort", onAbort, { once: true });
110 cleanupAbort = () => signal.removeEventListener("abort", onAbort);
111 })
112 : undefined;
113
114 try {
115 const racers: Array<Promise<T>> = [op, timeout];
116 if (aborted) {
117 racers.push(aborted);
118 }
119 return await Promise.race(racers);
120 } finally {
121 if (timeoutHandle) {
122 clearTimeout(timeoutHandle);
123 }
124 cleanupAbort?.();
125 }
126}
127
128function shouldRecycleClientAfterToolError(error: unknown): boolean {
129 return isClosedClientError(error) || error instanceof MCPDeadlineError;

Callers 2

wrapMCPToolsFunction · 0.85

Calls 5

rejectFunction · 0.85
resolveMethod · 0.80
removeEventListenerMethod · 0.80
pushMethod · 0.65
addEventListenerMethod · 0.45

Tested by

no test coverage detected