* Execute a tool on a specific server with retry logic for session errors. * Retries once on session-related errors (400, 404, session ID issues).
(
userId: string,
serverId: string,
toolCall: McpToolCall,
workspaceId: string,
extraHeaders?: Record<string, string>
)
| 247 | * Retries once on session-related errors (400, 404, session ID issues). |
| 248 | */ |
| 249 | async executeTool( |
| 250 | userId: string, |
| 251 | serverId: string, |
| 252 | toolCall: McpToolCall, |
| 253 | workspaceId: string, |
| 254 | extraHeaders?: Record<string, string> |
| 255 | ): Promise<McpToolResult> { |
| 256 | const requestId = generateRequestId() |
| 257 | const maxRetries = 2 |
| 258 | |
| 259 | for (let attempt = 0; attempt < maxRetries; attempt++) { |
| 260 | try { |
| 261 | logger.info( |
| 262 | `[${requestId}] Executing MCP tool ${toolCall.name} on server ${serverId} for user ${userId}${attempt > 0 ? ` (attempt ${attempt + 1})` : ''}` |
| 263 | ) |
| 264 | |
| 265 | const config = await this.getServerConfig(serverId, workspaceId) |
| 266 | if (!config) { |
| 267 | throw new Error(`Server ${serverId} not found or not accessible`) |
| 268 | } |
| 269 | |
| 270 | const { config: resolvedConfig, resolvedIP } = await this.resolveConfigEnvVars( |
| 271 | config, |
| 272 | userId, |
| 273 | workspaceId |
| 274 | ) |
| 275 | if (extraHeaders && Object.keys(extraHeaders).length > 0) { |
| 276 | resolvedConfig.headers = { ...resolvedConfig.headers, ...extraHeaders } |
| 277 | } |
| 278 | const client = await this.createClient(resolvedConfig, resolvedIP, userId) |
| 279 | |
| 280 | try { |
| 281 | const result = await client.callTool(toolCall) |
| 282 | logger.info(`[${requestId}] Successfully executed tool ${toolCall.name}`) |
| 283 | return result |
| 284 | } finally { |
| 285 | await client.disconnect() |
| 286 | } |
| 287 | } catch (error) { |
| 288 | if (this.isSessionError(error) && attempt < maxRetries - 1) { |
| 289 | logger.warn( |
| 290 | `[${requestId}] Session error executing tool ${toolCall.name}, retrying (attempt ${attempt + 1}):`, |
| 291 | error |
| 292 | ) |
| 293 | await sleep(100) |
| 294 | continue |
| 295 | } |
| 296 | throw error |
| 297 | } |
| 298 | } |
| 299 | |
| 300 | throw new Error(`Failed to execute tool ${toolCall.name} after ${maxRetries} attempts`) |
| 301 | } |
| 302 | |
| 303 | /** MCP spec: server returns 404 for unknown session id, 400 for malformed header. */ |
| 304 | private isSessionError(error: unknown): boolean { |
no test coverage detected