Update MCP client to connect to servers with only prompts (#5290)
This commit is contained in:
parent
93f8fe3671
commit
99ba2f6424
|
@ -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');
|
||||||
|
|
||||||
|
|
|
@ -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`;
|
||||||
}
|
}
|
||||||
|
|
|
@ -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 [];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue