(
userId: string,
serverId: string,
workspaceId: string,
forceRefresh: boolean
)
| 627 | } |
| 628 | |
| 629 | private async discoverServerToolsImpl( |
| 630 | userId: string, |
| 631 | serverId: string, |
| 632 | workspaceId: string, |
| 633 | forceRefresh: boolean |
| 634 | ): Promise<McpTool[]> { |
| 635 | const requestId = generateRequestId() |
| 636 | const maxRetries = 2 |
| 637 | |
| 638 | if (!forceRefresh) { |
| 639 | try { |
| 640 | const cached = await this.cacheAdapter.get(serverCacheKey(workspaceId, serverId)) |
| 641 | if (cached) { |
| 642 | logger.debug(`[${requestId}] Cache hit for server ${serverId}`) |
| 643 | return cached.tools |
| 644 | } |
| 645 | } catch (error) { |
| 646 | logger.warn(`[${requestId}] Cache read failed for server ${serverId}:`, error) |
| 647 | } |
| 648 | if (await this.isServerUnhealthy(workspaceId, serverId)) { |
| 649 | logger.info(`[${requestId}] Skipping recently-failed server ${serverId} (negative-cache)`) |
| 650 | throw new McpConnectionError( |
| 651 | 'Server recently failed and is in cooldown — try again shortly.', |
| 652 | serverId |
| 653 | ) |
| 654 | } |
| 655 | } |
| 656 | |
| 657 | for (let attempt = 0; attempt < maxRetries; attempt++) { |
| 658 | try { |
| 659 | logger.info( |
| 660 | `[${requestId}] Discovering tools from server ${serverId} for user ${userId}${attempt > 0 ? ` (attempt ${attempt + 1})` : ''}` |
| 661 | ) |
| 662 | |
| 663 | const config = await this.getServerConfig(serverId, workspaceId) |
| 664 | if (!config) { |
| 665 | throw new Error(`Server ${serverId} not found or not accessible`) |
| 666 | } |
| 667 | |
| 668 | const { config: resolvedConfig, resolvedIP } = await this.resolveConfigEnvVars( |
| 669 | config, |
| 670 | userId, |
| 671 | workspaceId |
| 672 | ) |
| 673 | const client = await this.createClient(resolvedConfig, resolvedIP, userId) |
| 674 | |
| 675 | try { |
| 676 | const tools = await client.listTools() |
| 677 | logger.info(`[${requestId}] Discovered ${tools.length} tools from server ${config.name}`) |
| 678 | await Promise.allSettled([ |
| 679 | this.cacheAdapter |
| 680 | .set(serverCacheKey(workspaceId, serverId), tools, this.cacheTimeout) |
| 681 | .catch((err) => |
| 682 | logger.warn(`[${requestId}] Cache write failed for ${config.name}:`, err) |
| 683 | ), |
| 684 | this.clearServerFailure(workspaceId, serverId), |
| 685 | this.updateServerStatus(serverId, workspaceId, true, undefined, tools.length), |
| 686 | ]) |
nothing calls this directly
no test coverage detected