(
tools: Record<string, Tool>,
options?: { onActivity?: () => void; onClosed?: () => void }
)
| 134 | * This ensures image content is properly converted to media type. |
| 135 | */ |
| 136 | export function wrapMCPTools( |
| 137 | tools: Record<string, Tool>, |
| 138 | options?: { onActivity?: () => void; onClosed?: () => void } |
| 139 | ): Record<string, Tool> { |
| 140 | const { onActivity, onClosed } = options ?? {}; |
| 141 | const wrapped: Record<string, Tool> = {}; |
| 142 | for (const [toolName, tool] of Object.entries(tools)) { |
| 143 | // Only wrap tools that have an execute function |
| 144 | if (!tool.execute) { |
| 145 | wrapped[toolName] = tool; |
| 146 | continue; |
| 147 | } |
| 148 | |
| 149 | const originalExecute = tool.execute; |
| 150 | wrapped[toolName] = { |
| 151 | ...tool, |
| 152 | execute: async (args: Parameters<typeof originalExecute>[0], context) => { |
| 153 | // Mark the MCP server set as active *before* execution, so failed tool |
| 154 | // calls (including closed-client races) still count as activity. |
| 155 | onActivity?.(); |
| 156 | |
| 157 | try { |
| 158 | const abortSignal = |
| 159 | context && typeof context === "object" && "abortSignal" in context |
| 160 | ? (context as { abortSignal?: AbortSignal }).abortSignal |
| 161 | : undefined; |
| 162 | |
| 163 | const result: unknown = await runMCPToolWithDeadline( |
| 164 | () => Promise.resolve(originalExecute(args, context)) as Promise<unknown>, |
| 165 | { toolName, timeoutMs: MCP_TOOL_CALL_TIMEOUT_MS, signal: abortSignal } |
| 166 | ); |
| 167 | return transformMCPResult(result as MCPCallToolResult); |
| 168 | } catch (error) { |
| 169 | if (shouldRecycleClientAfterToolError(error)) { |
| 170 | try { |
| 171 | onClosed?.(); |
| 172 | } catch { |
| 173 | // Swallow — original tool error takes priority. |
| 174 | } |
| 175 | } |
| 176 | throw error; |
| 177 | } |
| 178 | }, |
| 179 | }; |
| 180 | } |
| 181 | return wrapped; |
| 182 | } |
| 183 | |
| 184 | type ResolvedHeaders = Record<string, string> | undefined; |
| 185 |
no test coverage detected