Update MCP client to connect to servers with only prompts (#5290)

This commit is contained in:
Harold Mciver 2025-08-04 17:38:23 -04:00 committed by GitHub
parent 93f8fe3671
commit 99ba2f6424
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
3 changed files with 65 additions and 27 deletions

View File

@ -212,9 +212,9 @@ describe('mcpCommand', () => {
); );
expect(message).toContain('server2_tool1'); expect(message).toContain('server2_tool1');
// Server 3 - Disconnected // Server 3 - Disconnected but with cached tools, so shows as Ready
expect(message).toContain( expect(message).toContain(
'🔴 \u001b[1mserver3\u001b[0m - Disconnected (1 tools cached)', '🟢 \u001b[1mserver3\u001b[0m - Ready (1 tool)',
); );
expect(message).toContain('server3_tool1'); expect(message).toContain('server3_tool1');

View File

@ -94,7 +94,15 @@ const getMcpStatus = async (
const promptRegistry = await config.getPromptRegistry(); const promptRegistry = await config.getPromptRegistry();
const serverPrompts = promptRegistry.getPromptsByServer(serverName) || []; const serverPrompts = promptRegistry.getPromptsByServer(serverName) || [];
const status = getMCPServerStatus(serverName); const originalStatus = getMCPServerStatus(serverName);
const hasCachedItems = serverTools.length > 0 || serverPrompts.length > 0;
// If the server is "disconnected" but has prompts or cached tools, display it as Ready
// by using CONNECTED as the display status.
const status =
originalStatus === MCPServerStatus.DISCONNECTED && hasCachedItems
? MCPServerStatus.CONNECTED
: originalStatus;
// Add status indicator with descriptive text // Add status indicator with descriptive text
let statusIndicator = ''; let statusIndicator = '';
@ -260,11 +268,14 @@ const getMcpStatus = async (
message += ' No tools or prompts available\n'; message += ' No tools or prompts available\n';
} else if (serverTools.length === 0) { } else if (serverTools.length === 0) {
message += ' No tools available'; message += ' No tools available';
if (status === MCPServerStatus.DISCONNECTED && needsAuthHint) { if (originalStatus === MCPServerStatus.DISCONNECTED && needsAuthHint) {
message += ` ${COLOR_GREY}(type: "/mcp auth ${serverName}" to authenticate this server)${RESET_COLOR}`; message += ` ${COLOR_GREY}(type: "/mcp auth ${serverName}" to authenticate this server)${RESET_COLOR}`;
} }
message += '\n'; message += '\n';
} else if (status === MCPServerStatus.DISCONNECTED && needsAuthHint) { } else if (
originalStatus === MCPServerStatus.DISCONNECTED &&
needsAuthHint
) {
// This case is for when serverTools.length > 0 // This case is for when serverTools.length > 0
message += ` ${COLOR_GREY}(type: "/mcp auth ${serverName}" to authenticate this server)${RESET_COLOR}\n`; message += ` ${COLOR_GREY}(type: "/mcp auth ${serverName}" to authenticate this server)${RESET_COLOR}\n`;
} }

View File

@ -366,33 +366,47 @@ export async function connectAndDiscover(
): Promise<void> { ): Promise<void> {
updateMCPServerStatus(mcpServerName, MCPServerStatus.CONNECTING); updateMCPServerStatus(mcpServerName, MCPServerStatus.CONNECTING);
let mcpClient: Client | undefined;
try { try {
const mcpClient = await connectToMcpServer( mcpClient = await connectToMcpServer(
mcpServerName, mcpServerName,
mcpServerConfig, mcpServerConfig,
debugMode, debugMode,
); );
try {
updateMCPServerStatus(mcpServerName, MCPServerStatus.CONNECTED);
mcpClient.onerror = (error) => {
console.error(`MCP ERROR (${mcpServerName}):`, error.toString());
updateMCPServerStatus(mcpServerName, MCPServerStatus.DISCONNECTED);
};
await discoverPrompts(mcpServerName, mcpClient, promptRegistry);
const tools = await discoverTools( mcpClient.onerror = (error) => {
mcpServerName, console.error(`MCP ERROR (${mcpServerName}):`, error.toString());
mcpServerConfig, updateMCPServerStatus(mcpServerName, MCPServerStatus.DISCONNECTED);
mcpClient, };
);
for (const tool of tools) { // Attempt to discover both prompts and tools
toolRegistry.registerTool(tool); const prompts = await discoverPrompts(
} mcpServerName,
} catch (error) { mcpClient,
mcpClient.close(); promptRegistry,
throw error; );
const tools = await discoverTools(
mcpServerName,
mcpServerConfig,
mcpClient,
);
// If we have neither prompts nor tools, it's a failed discovery
if (prompts.length === 0 && tools.length === 0) {
throw new Error('No prompts or tools found on the server.');
}
// If we found anything, the server is connected
updateMCPServerStatus(mcpServerName, MCPServerStatus.CONNECTED);
// Register any discovered tools
for (const tool of tools) {
toolRegistry.registerTool(tool);
} }
} catch (error) { } catch (error) {
if (mcpClient) {
mcpClient.close();
}
console.error( console.error(
`Error connecting to MCP server '${mcpServerName}': ${getErrorMessage( `Error connecting to MCP server '${mcpServerName}': ${getErrorMessage(
error, error,
@ -423,7 +437,8 @@ export async function discoverTools(
const tool = await mcpCallableTool.tool(); const tool = await mcpCallableTool.tool();
if (!Array.isArray(tool.functionDeclarations)) { if (!Array.isArray(tool.functionDeclarations)) {
throw new Error(`Server did not return valid function declarations.`); // This is a valid case for a prompt-only server
return [];
} }
const discoveredTools: DiscoveredMCPTool[] = []; const discoveredTools: DiscoveredMCPTool[] = [];
@ -454,7 +469,17 @@ export async function discoverTools(
} }
return discoveredTools; return discoveredTools;
} catch (error) { } catch (error) {
throw new Error(`Error discovering tools: ${error}`); if (
error instanceof Error &&
!error.message?.includes('Method not found')
) {
console.error(
`Error discovering tools from ${mcpServerName}: ${getErrorMessage(
error,
)}`,
);
}
return [];
} }
} }
@ -469,7 +494,7 @@ export async function discoverPrompts(
mcpServerName: string, mcpServerName: string,
mcpClient: Client, mcpClient: Client,
promptRegistry: PromptRegistry, promptRegistry: PromptRegistry,
): Promise<void> { ): Promise<Prompt[]> {
try { try {
const response = await mcpClient.request( const response = await mcpClient.request(
{ method: 'prompts/list', params: {} }, { method: 'prompts/list', params: {} },
@ -484,6 +509,7 @@ export async function discoverPrompts(
invokeMcpPrompt(mcpServerName, mcpClient, prompt.name, params), invokeMcpPrompt(mcpServerName, mcpClient, prompt.name, params),
}); });
} }
return response.prompts;
} catch (error) { } catch (error) {
// It's okay if this fails, not all servers will have prompts. // It's okay if this fails, not all servers will have prompts.
// Don't log an error if the method is not found, which is a common case. // Don't log an error if the method is not found, which is a common case.
@ -497,6 +523,7 @@ export async function discoverPrompts(
)}`, )}`,
); );
} }
return [];
} }
} }