Skip to content

Commit 733ad36

Browse files
committed
test: add unit tests for list_indexes and discovery vs fixed mode
Agent-Id: agent-5dbe4a24-0e78-4075-b85c-d6ff632c88c7
1 parent 3220fcf commit 733ad36

File tree

1 file changed

+210
-0
lines changed

1 file changed

+210
-0
lines changed

src/clients/mcp-server.test.ts

Lines changed: 210 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,10 @@ import { describe, it, expect, vi, beforeEach } from "vitest";
66
import type { IndexState } from "../core/types.js";
77
import type { IndexStoreReader } from "../stores/types.js";
88
import type { Source } from "../sources/types.js";
9+
import {
10+
ListToolsRequestSchema,
11+
CallToolRequestSchema,
12+
} from "@modelcontextprotocol/sdk/types.js";
913

1014
// Try to import SDK-dependent modules
1115
let createMCPServer: typeof import("./mcp-server.js").createMCPServer;
@@ -134,3 +138,209 @@ describe.skipIf(sdkLoadError !== null)("MCP Server Unit Tests", () => {
134138
});
135139
});
136140

141+
// Tests for list_indexes tool and discovery vs fixed mode
142+
describe.skipIf(sdkLoadError !== null || !hasApiCredentials)(
143+
"list_indexes tool and discovery mode",
144+
() => {
145+
describe("list_indexes tool", () => {
146+
it("returns available indexes with metadata", async () => {
147+
const mockState = createMockState();
148+
const store = createMockStore(mockState);
149+
150+
const server = await createMCPServer({
151+
store,
152+
indexNames: ["test-key"],
153+
});
154+
155+
// Get the ListToolsRequestSchema handler
156+
const listToolsHandler = (server as any).requestHandlers.get(
157+
ListToolsRequestSchema
158+
);
159+
expect(listToolsHandler).toBeDefined();
160+
161+
// Call the handler to get tools
162+
const result = await listToolsHandler();
163+
const listIndexesTool = result.tools.find(
164+
(t: any) => t.name === "list_indexes"
165+
);
166+
expect(listIndexesTool).toBeDefined();
167+
expect(listIndexesTool.description).toContain("available indexes");
168+
});
169+
170+
it("returns 'No indexes available' message when empty in discovery mode", async () => {
171+
const store = createMockStore(null);
172+
// Mock store.list() to return empty array for discovery mode
173+
store.list = vi.fn().mockResolvedValue([]);
174+
175+
const server = await createMCPServer({
176+
store,
177+
indexNames: [],
178+
discovery: true,
179+
});
180+
181+
// Get the CallToolRequestSchema handler
182+
const callToolHandler = (server as any).requestHandlers.get(
183+
CallToolRequestSchema
184+
);
185+
expect(callToolHandler).toBeDefined();
186+
187+
// Call list_indexes
188+
const result = await callToolHandler({
189+
params: {
190+
name: "list_indexes",
191+
arguments: {},
192+
},
193+
});
194+
195+
expect(result.content[0].text).toContain("No indexes available");
196+
});
197+
198+
it("handles errors gracefully and returns isError: true", async () => {
199+
const store = createMockStore(createMockState());
200+
// Mock store.list() to throw an error
201+
store.list = vi.fn().mockRejectedValue(new Error("Store error"));
202+
203+
const server = await createMCPServer({
204+
store,
205+
indexNames: ["test-key"],
206+
discovery: true,
207+
});
208+
209+
// Get the CallToolRequestSchema handler
210+
const callToolHandler = (server as any).requestHandlers.get(
211+
CallToolRequestSchema
212+
);
213+
214+
// Call list_indexes
215+
const result = await callToolHandler({
216+
params: {
217+
name: "list_indexes",
218+
arguments: {},
219+
},
220+
});
221+
222+
expect(result.isError).toBe(true);
223+
expect(result.content[0].text).toContain("Error listing indexes");
224+
});
225+
});
226+
227+
describe("discovery vs fixed mode", () => {
228+
it("fixed mode includes enum in tool schemas for index_name", async () => {
229+
const mockState = createMockState();
230+
const store = createMockStore(mockState);
231+
232+
const server = await createMCPServer({
233+
store,
234+
indexNames: ["test-key"],
235+
discovery: false, // Fixed mode
236+
});
237+
238+
// Get the ListToolsRequestSchema handler
239+
const listToolsHandler = (server as any).requestHandlers.get(
240+
ListToolsRequestSchema
241+
);
242+
const result = await listToolsHandler();
243+
244+
// Check search tool has enum
245+
const searchTool = result.tools.find((t: any) => t.name === "search");
246+
expect(searchTool.inputSchema.properties.index_name.enum).toBeDefined();
247+
expect(searchTool.inputSchema.properties.index_name.enum).toContain(
248+
"test-key"
249+
);
250+
251+
// Check list_files tool has enum (if present)
252+
const listFilesTool = result.tools.find(
253+
(t: any) => t.name === "list_files"
254+
);
255+
if (listFilesTool) {
256+
expect(listFilesTool.inputSchema.properties.index_name.enum).toBeDefined();
257+
expect(listFilesTool.inputSchema.properties.index_name.enum).toContain(
258+
"test-key"
259+
);
260+
}
261+
262+
// Check read_file tool has enum (if present)
263+
const readFileTool = result.tools.find((t: any) => t.name === "read_file");
264+
if (readFileTool) {
265+
expect(readFileTool.inputSchema.properties.index_name.enum).toBeDefined();
266+
expect(readFileTool.inputSchema.properties.index_name.enum).toContain(
267+
"test-key"
268+
);
269+
}
270+
});
271+
272+
it("discovery mode does NOT include enum in tool schemas", async () => {
273+
const mockState = createMockState();
274+
const store = createMockStore(mockState);
275+
276+
const server = await createMCPServer({
277+
store,
278+
indexNames: ["test-key"],
279+
discovery: true, // Discovery mode
280+
});
281+
282+
// Get the ListToolsRequestSchema handler
283+
const listToolsHandler = (server as any).requestHandlers.get(
284+
ListToolsRequestSchema
285+
);
286+
const result = await listToolsHandler();
287+
288+
// Check search tool does NOT have enum
289+
const searchTool = result.tools.find((t: any) => t.name === "search");
290+
expect(searchTool.inputSchema.properties.index_name.enum).toBeUndefined();
291+
292+
// Check list_files tool does NOT have enum (if present)
293+
const listFilesTool = result.tools.find(
294+
(t: any) => t.name === "list_files"
295+
);
296+
if (listFilesTool) {
297+
expect(listFilesTool.inputSchema.properties.index_name.enum).toBeUndefined();
298+
}
299+
300+
// Check read_file tool does NOT have enum (if present)
301+
const readFileTool = result.tools.find((t: any) => t.name === "read_file");
302+
if (readFileTool) {
303+
expect(readFileTool.inputSchema.properties.index_name.enum).toBeUndefined();
304+
}
305+
});
306+
307+
it("list_indexes tool is available in both fixed and discovery modes", async () => {
308+
const mockState = createMockState();
309+
const store = createMockStore(mockState);
310+
311+
// Test fixed mode
312+
const fixedServer = await createMCPServer({
313+
store,
314+
indexNames: ["test-key"],
315+
discovery: false,
316+
});
317+
318+
const fixedListToolsHandler = (fixedServer as any).requestHandlers.get(
319+
ListToolsRequestSchema
320+
);
321+
const fixedResult = await fixedListToolsHandler();
322+
const fixedListIndexesTool = fixedResult.tools.find(
323+
(t: any) => t.name === "list_indexes"
324+
);
325+
expect(fixedListIndexesTool).toBeDefined();
326+
327+
// Test discovery mode
328+
const discoveryServer = await createMCPServer({
329+
store,
330+
indexNames: ["test-key"],
331+
discovery: true,
332+
});
333+
334+
const discoveryListToolsHandler = (discoveryServer as any).requestHandlers.get(
335+
ListToolsRequestSchema
336+
);
337+
const discoveryResult = await discoveryListToolsHandler();
338+
const discoveryListIndexesTool = discoveryResult.tools.find(
339+
(t: any) => t.name === "list_indexes"
340+
);
341+
expect(discoveryListIndexesTool).toBeDefined();
342+
});
343+
});
344+
}
345+
);
346+

0 commit comments

Comments
 (0)