mirror of
https://github.com/QwenLM/qwen-code.git
synced 2025-12-19 09:33:53 +00:00
Added /mcp command support and cleaned up broken tests (#817)
This commit is contained in:
@@ -56,7 +56,11 @@ import {
|
||||
type SlashCommandActionReturn,
|
||||
} from './slashCommandProcessor.js';
|
||||
import { MessageType } from '../types.js';
|
||||
import { type Config } from '@gemini-code/core';
|
||||
import {
|
||||
type Config,
|
||||
MCPServerStatus,
|
||||
getMCPServerStatus,
|
||||
} from '@gemini-code/core';
|
||||
|
||||
import * as ShowMemoryCommandModule from './useShowMemoryCommand.js';
|
||||
import { GIT_COMMIT_INFO } from '../../generated/git-commit.js';
|
||||
@@ -378,39 +382,54 @@ Add any other context about the problem here.
|
||||
expect(commandResult).toBe(true);
|
||||
});
|
||||
|
||||
it('should display available tools when tools are found', async () => {
|
||||
const mockTools = [{ name: 'tool1' }, { name: 'tool2' }];
|
||||
it('should display only Gemini CLI tools (filtering out MCP tools)', async () => {
|
||||
// Create mock tools - some with serverName property (MCP tools) and some without (Gemini CLI tools)
|
||||
const mockTools = [
|
||||
{ name: 'tool1' },
|
||||
{ name: 'tool2' },
|
||||
{ name: 'mcp_tool1', serverName: 'mcp-server1' },
|
||||
{ name: 'mcp_tool2', serverName: 'mcp-server1' },
|
||||
];
|
||||
|
||||
mockConfig = {
|
||||
...mockConfig,
|
||||
getToolRegistry: vi.fn().mockResolvedValue({
|
||||
getAllTools: vi.fn().mockReturnValue(mockTools),
|
||||
}),
|
||||
} as unknown as Config;
|
||||
|
||||
const { handleSlashCommand } = getProcessor();
|
||||
let commandResult: SlashCommandActionReturn | boolean = false;
|
||||
await act(async () => {
|
||||
commandResult = handleSlashCommand('/tools');
|
||||
});
|
||||
|
||||
// Should only show tool1 and tool2, not the MCP tools
|
||||
expect(mockAddItem).toHaveBeenNthCalledWith(
|
||||
2,
|
||||
expect.objectContaining({
|
||||
type: MessageType.INFO,
|
||||
text: 'Available tools:\n\ntool1\ntool2',
|
||||
text: 'Available Gemini CLI tools:\n\ntool1\ntool2',
|
||||
}),
|
||||
expect.any(Number),
|
||||
);
|
||||
expect(commandResult).toBe(true);
|
||||
});
|
||||
|
||||
it('should display a message when no tools are available', async () => {
|
||||
const mockTools: unknown[] = [];
|
||||
it('should display a message when no Gemini CLI tools are available', async () => {
|
||||
// Only MCP tools available
|
||||
const mockTools = [
|
||||
{ name: 'mcp_tool1', serverName: 'mcp-server1' },
|
||||
{ name: 'mcp_tool2', serverName: 'mcp-server1' },
|
||||
];
|
||||
|
||||
mockConfig = {
|
||||
...mockConfig,
|
||||
getToolRegistry: vi.fn().mockResolvedValue({
|
||||
getAllTools: vi.fn().mockReturnValue(mockTools),
|
||||
}),
|
||||
} as unknown as Config;
|
||||
|
||||
const { handleSlashCommand } = getProcessor();
|
||||
let commandResult: SlashCommandActionReturn | boolean = false;
|
||||
await act(async () => {
|
||||
@@ -421,11 +440,208 @@ Add any other context about the problem here.
|
||||
2,
|
||||
expect.objectContaining({
|
||||
type: MessageType.INFO,
|
||||
text: 'Available tools:\n\n',
|
||||
text: 'Available Gemini CLI tools:\n\n',
|
||||
}),
|
||||
expect.any(Number),
|
||||
);
|
||||
expect(commandResult).toBe(true);
|
||||
});
|
||||
});
|
||||
|
||||
describe('/mcp command', () => {
|
||||
beforeEach(() => {
|
||||
// Mock the core module with getMCPServerStatus
|
||||
vi.mock('@gemini-code/core', async (importOriginal) => {
|
||||
const actual = await importOriginal();
|
||||
return {
|
||||
...actual,
|
||||
MCPServerStatus: {
|
||||
CONNECTED: 'connected',
|
||||
CONNECTING: 'connecting',
|
||||
DISCONNECTED: 'disconnected',
|
||||
},
|
||||
getMCPServerStatus: vi.fn(),
|
||||
};
|
||||
});
|
||||
});
|
||||
|
||||
it('should show an error if tool registry is not available', async () => {
|
||||
mockConfig = {
|
||||
...mockConfig,
|
||||
getToolRegistry: vi.fn().mockResolvedValue(undefined),
|
||||
} as unknown as Config;
|
||||
const { handleSlashCommand } = getProcessor();
|
||||
let commandResult: SlashCommandActionReturn | boolean = false;
|
||||
await act(async () => {
|
||||
commandResult = handleSlashCommand('/mcp');
|
||||
});
|
||||
|
||||
expect(mockAddItem).toHaveBeenNthCalledWith(
|
||||
2,
|
||||
expect.objectContaining({
|
||||
type: MessageType.ERROR,
|
||||
text: 'Could not retrieve tool registry.',
|
||||
}),
|
||||
expect.any(Number),
|
||||
);
|
||||
expect(commandResult).toBe(true);
|
||||
});
|
||||
|
||||
it('should display a message when no MCP servers are configured', async () => {
|
||||
mockConfig = {
|
||||
...mockConfig,
|
||||
getToolRegistry: vi.fn().mockResolvedValue({
|
||||
getToolsByServer: vi.fn().mockReturnValue([]),
|
||||
}),
|
||||
getMcpServers: vi.fn().mockReturnValue({}),
|
||||
} as unknown as Config;
|
||||
|
||||
const { handleSlashCommand } = getProcessor();
|
||||
let commandResult: SlashCommandActionReturn | boolean = false;
|
||||
await act(async () => {
|
||||
commandResult = handleSlashCommand('/mcp');
|
||||
});
|
||||
|
||||
expect(mockAddItem).toHaveBeenNthCalledWith(
|
||||
2,
|
||||
expect.objectContaining({
|
||||
type: MessageType.INFO,
|
||||
text: 'No MCP servers configured.',
|
||||
}),
|
||||
expect.any(Number),
|
||||
);
|
||||
expect(commandResult).toBe(true);
|
||||
});
|
||||
|
||||
it('should display configured MCP servers with status indicators and their tools', async () => {
|
||||
// Mock MCP servers configuration
|
||||
const mockMcpServers = {
|
||||
server1: { command: 'cmd1' },
|
||||
server2: { command: 'cmd2' },
|
||||
server3: { command: 'cmd3' },
|
||||
};
|
||||
|
||||
// Setup getMCPServerStatus mock implementation
|
||||
vi.mocked(getMCPServerStatus).mockImplementation((serverName) => {
|
||||
if (serverName === 'server1') return MCPServerStatus.CONNECTED;
|
||||
if (serverName === 'server2') return MCPServerStatus.CONNECTING;
|
||||
return MCPServerStatus.DISCONNECTED; // Default for server3 and others
|
||||
});
|
||||
|
||||
// Mock tools from each server
|
||||
const mockServer1Tools = [
|
||||
{ name: 'server1_tool1' },
|
||||
{ name: 'server1_tool2' },
|
||||
];
|
||||
|
||||
const mockServer2Tools = [{ name: 'server2_tool1' }];
|
||||
|
||||
const mockServer3Tools = [{ name: 'server3_tool1' }];
|
||||
|
||||
const mockGetToolsByServer = vi.fn().mockImplementation((serverName) => {
|
||||
if (serverName === 'server1') return mockServer1Tools;
|
||||
if (serverName === 'server2') return mockServer2Tools;
|
||||
if (serverName === 'server3') return mockServer3Tools;
|
||||
return [];
|
||||
});
|
||||
|
||||
mockConfig = {
|
||||
...mockConfig,
|
||||
getToolRegistry: vi.fn().mockResolvedValue({
|
||||
getToolsByServer: mockGetToolsByServer,
|
||||
}),
|
||||
getMcpServers: vi.fn().mockReturnValue(mockMcpServers),
|
||||
} as unknown as Config;
|
||||
|
||||
const { handleSlashCommand } = getProcessor();
|
||||
let commandResult: SlashCommandActionReturn | boolean = false;
|
||||
await act(async () => {
|
||||
commandResult = handleSlashCommand('/mcp');
|
||||
});
|
||||
|
||||
expect(mockAddItem).toHaveBeenNthCalledWith(
|
||||
2,
|
||||
expect.objectContaining({
|
||||
type: MessageType.INFO,
|
||||
text: expect.stringContaining('Configured MCP servers and tools:'),
|
||||
}),
|
||||
expect.any(Number),
|
||||
);
|
||||
|
||||
// Check that the message contains details about both servers and their tools
|
||||
const message = mockAddItem.mock.calls[1][0].text;
|
||||
// Server 1 - Connected (green dot)
|
||||
expect(message).toContain('🟢 server1 (2 tools):');
|
||||
expect(message).toContain('server1_tool1');
|
||||
expect(message).toContain('server1_tool2');
|
||||
|
||||
// Server 2 - Connecting (yellow dot)
|
||||
expect(message).toContain('🟡 server2 (1 tools):');
|
||||
expect(message).toContain('server2_tool1');
|
||||
|
||||
// Server 3 - No status, should default to Disconnected (red dot)
|
||||
expect(message).toContain('🔴 server3 (1 tools):');
|
||||
expect(message).toContain('server3_tool1');
|
||||
|
||||
expect(commandResult).toBe(true);
|
||||
});
|
||||
|
||||
it('should indicate when a server has no tools', async () => {
|
||||
// Mock MCP servers configuration
|
||||
const mockMcpServers = {
|
||||
server1: { command: 'cmd1' },
|
||||
server2: { command: 'cmd2' },
|
||||
};
|
||||
|
||||
// Setup getMCPServerStatus mock implementation
|
||||
vi.mocked(getMCPServerStatus).mockImplementation((serverName) => {
|
||||
if (serverName === 'server1') return MCPServerStatus.CONNECTED;
|
||||
if (serverName === 'server2') return MCPServerStatus.DISCONNECTED;
|
||||
return MCPServerStatus.DISCONNECTED;
|
||||
});
|
||||
|
||||
// Mock tools from each server - server2 has no tools
|
||||
const mockServer1Tools = [{ name: 'server1_tool1' }];
|
||||
|
||||
const mockServer2Tools = [];
|
||||
|
||||
const mockGetToolsByServer = vi.fn().mockImplementation((serverName) => {
|
||||
if (serverName === 'server1') return mockServer1Tools;
|
||||
if (serverName === 'server2') return mockServer2Tools;
|
||||
return [];
|
||||
});
|
||||
|
||||
mockConfig = {
|
||||
...mockConfig,
|
||||
getToolRegistry: vi.fn().mockResolvedValue({
|
||||
getToolsByServer: mockGetToolsByServer,
|
||||
}),
|
||||
getMcpServers: vi.fn().mockReturnValue(mockMcpServers),
|
||||
} as unknown as Config;
|
||||
|
||||
const { handleSlashCommand } = getProcessor();
|
||||
let commandResult: SlashCommandActionReturn | boolean = false;
|
||||
await act(async () => {
|
||||
commandResult = handleSlashCommand('/mcp');
|
||||
});
|
||||
|
||||
expect(mockAddItem).toHaveBeenNthCalledWith(
|
||||
2,
|
||||
expect.objectContaining({
|
||||
type: MessageType.INFO,
|
||||
text: expect.stringContaining('Configured MCP servers and tools:'),
|
||||
}),
|
||||
expect.any(Number),
|
||||
);
|
||||
|
||||
// Check that the message contains details about both servers and their tools
|
||||
const message = mockAddItem.mock.calls[1][0].text;
|
||||
expect(message).toContain('🟢 server1 (1 tools):');
|
||||
expect(message).toContain('server1_tool1');
|
||||
expect(message).toContain('🔴 server2 (0 tools):');
|
||||
expect(message).toContain('No tools available');
|
||||
|
||||
expect(commandResult).toBe(true);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user