Skip to content

Commit effaac6

Browse files
chore: use common logic from createIndex
1 parent 764115a commit effaac6

File tree

7 files changed

+71
-59
lines changed

7 files changed

+71
-59
lines changed

src/common/config.ts

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -204,7 +204,6 @@ export const defaultUserConfig: UserConfig = {
204204
"drop-collection",
205205
"delete-many",
206206
"drop-index",
207-
"drop-search-index",
208207
],
209208
transport: "stdio",
210209
httpPort: 3000,

src/helpers/searchErrorHandler.ts

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
import type { CallToolResult } from "@modelcontextprotocol/sdk/types.js";
2+
import { type DbOperationArgs, MongoDBToolBase } from "../tools/mongodb/mongodbTool.js";
3+
import type { ToolArgs } from "../tools/tool.js";
4+
5+
export abstract class MongoDBToolWithSearchErrorHandler extends MongoDBToolBase {
6+
protected handleError(
7+
error: unknown,
8+
args: ToolArgs<typeof DbOperationArgs>
9+
): Promise<CallToolResult> | CallToolResult {
10+
const CTA = this.server?.areLocalAtlasToolsAvailable() ? "`atlas-local` tools" : "Atlas CLI";
11+
if (error instanceof Error && "codeName" in error && error.codeName === "SearchNotEnabled") {
12+
return {
13+
content: [
14+
{
15+
text: `The connected MongoDB deployment does not support vector search indexes. Either connect to a MongoDB Atlas cluster or use the ${CTA} to create and manage a local Atlas deployment.`,
16+
type: "text",
17+
},
18+
],
19+
isError: true,
20+
};
21+
}
22+
return super.handleError(error, args);
23+
}
24+
}

src/server.ts

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ import {
1818
UnsubscribeRequestSchema,
1919
} from "@modelcontextprotocol/sdk/types.js";
2020
import assert from "assert";
21-
import type { ToolBase, ToolConstructorParams } from "./tools/tool.js";
21+
import type { ToolBase, ToolCategory, ToolConstructorParams } from "./tools/tool.js";
2222
import { validateConnectionString } from "./helpers/connectionOptions.js";
2323
import { packageInfo } from "./common/packageInfo.js";
2424
import { type ConnectionErrorHandler } from "./common/connectionErrorHandler.js";
@@ -174,6 +174,12 @@ export class Server {
174174
this.mcpServer.sendResourceListChanged();
175175
}
176176

177+
public areLocalAtlasToolsAvailable(): boolean {
178+
// TODO: remove hacky casts once we merge the local dev tools
179+
const atlasLocalCategory = "atlas-local" as unknown as ToolCategory;
180+
return !!this.tools.filter((tool) => tool.category === atlasLocalCategory).length;
181+
}
182+
177183
public sendResourceUpdated(uri: string): void {
178184
this.session.logger.info({
179185
id: LogId.resourceUpdateFailure,

src/tools/mongodb/create/createIndex.ts

Lines changed: 3 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,11 @@
11
import { z } from "zod";
22
import type { CallToolResult } from "@modelcontextprotocol/sdk/types.js";
3-
import { DbOperationArgs, MongoDBToolBase } from "../mongodbTool.js";
4-
import type { ToolCategory } from "../../tool.js";
3+
import { DbOperationArgs } from "../mongodbTool.js";
54
import { type ToolArgs, type OperationType, FeatureFlags } from "../../tool.js";
65
import type { IndexDirection } from "mongodb";
6+
import { MongoDBToolWithSearchErrorHandler } from "../../../helpers/searchErrorHandler.js";
77

8-
export class CreateIndexTool extends MongoDBToolBase {
8+
export class CreateIndexTool extends MongoDBToolWithSearchErrorHandler {
99
private vectorSearchIndexDefinition = z.object({
1010
type: z.literal("vectorSearch"),
1111
fields: z
@@ -113,25 +113,6 @@ export class CreateIndexTool extends MongoDBToolBase {
113113
break;
114114
case "vectorSearch":
115115
{
116-
const isVectorSearchSupported = await this.session.isSearchSupported();
117-
if (!isVectorSearchSupported) {
118-
// TODO: remove hacky casts once we merge the local dev tools
119-
const isLocalAtlasAvailable =
120-
(this.server?.tools.filter((t) => t.category === ("atlas-local" as unknown as ToolCategory))
121-
.length ?? 0) > 0;
122-
123-
const CTA = isLocalAtlasAvailable ? "`atlas-local` tools" : "Atlas CLI";
124-
return {
125-
content: [
126-
{
127-
text: `The connected MongoDB deployment does not support vector search indexes. Either connect to a MongoDB Atlas cluster or use the ${CTA} to create and manage a local Atlas deployment.`,
128-
type: "text",
129-
},
130-
],
131-
isError: true,
132-
};
133-
}
134-
135116
indexes = await provider.createSearchIndexes(database, collection, [
136117
{
137118
name,

src/tools/mongodb/delete/dropIndex.ts

Lines changed: 3 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,12 @@
11
import z from "zod";
22
import type { CallToolResult } from "@modelcontextprotocol/sdk/types.js";
33
import type { NodeDriverServiceProvider } from "@mongosh/service-provider-node-driver";
4-
import { DbOperationArgs, MongoDBToolBase } from "../mongodbTool.js";
4+
import { DbOperationArgs } from "../mongodbTool.js";
55
import { type ToolArgs, type OperationType, formatUntrustedData, FeatureFlags } from "../../tool.js";
66
import { ListSearchIndexesTool } from "../search/listSearchIndexes.js";
7+
import { MongoDBToolWithSearchErrorHandler } from "../../../helpers/searchErrorHandler.js";
78

8-
export class DropIndexTool extends MongoDBToolBase {
9+
export class DropIndexTool extends MongoDBToolWithSearchErrorHandler {
910
public name = "drop-index";
1011
protected description = "Drop an index for the provided database and collection.";
1112
protected argsShape = {
@@ -90,22 +91,4 @@ export class DropIndexTool extends MongoDBToolBase {
9091
"**Do you confirm the execution of the action?**"
9192
);
9293
}
93-
94-
protected handleError(
95-
error: unknown,
96-
args: ToolArgs<typeof DbOperationArgs>
97-
): Promise<CallToolResult> | CallToolResult {
98-
if (error instanceof Error && "codeName" in error && error.codeName === "SearchNotEnabled") {
99-
return {
100-
content: [
101-
{
102-
text: "This MongoDB cluster does not support Search Indexes. Make sure you are using an Atlas Cluster, either remotely in Atlas or using the Atlas Local image, or your cluster supports MongoDB Search.",
103-
type: "text",
104-
},
105-
],
106-
isError: true,
107-
};
108-
}
109-
return super.handleError(error, args);
110-
}
11194
}

src/tools/mongodb/tools.ts

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,6 @@ import { LogsTool } from "./metadata/logs.js";
2121
import { ExportTool } from "./read/export.js";
2222
import { ListSearchIndexesTool } from "./search/listSearchIndexes.js";
2323
import { DropIndexTool } from "./delete/dropIndex.js";
24-
import { DropSearchIndexTool } from "./delete/dropSearchIndex.js";
2524

2625
export const MongoDbTools = [
2726
ConnectTool,
@@ -47,5 +46,4 @@ export const MongoDbTools = [
4746
LogsTool,
4847
ExportTool,
4948
ListSearchIndexesTool,
50-
DropSearchIndexTool,
5149
];

tests/integration/tools/mongodb/delete/dropIndex.test.ts

Lines changed: 34 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -88,7 +88,7 @@ function setupForVectorSearchIndexes(integration: MongoDBIntegrationTestCase): {
8888
describe.each([{ vectorSearchEnabled: false }, { vectorSearchEnabled: true }])(
8989
"drop-index tool",
9090
({ vectorSearchEnabled }) => {
91-
describe(`when vector search is ${vectorSearchEnabled ? "enabled" : "disabled"}`, () => {
91+
describe(`when vector search feature flag is ${vectorSearchEnabled ? "enabled" : "disabled"}`, () => {
9292
describeWithMongoDB(
9393
"tool metadata and parameters",
9494
(integration) => {
@@ -322,13 +322,13 @@ describe.each([{ vectorSearchEnabled: false }, { vectorSearchEnabled: true }])(
322322
it("should fail with appropriate error when invoked", async () => {
323323
await integration.connectMcpClient();
324324
const response = await integration.mcpClient().callTool({
325-
name: "drop-search-index",
326-
arguments: { database: "any", collection: "foo", indexName: "default" },
325+
name: "drop-index",
326+
arguments: { database: "any", collection: "foo", indexName: "default", type: "search" },
327327
});
328328
const content = getResponseContent(response.content);
329329
expect(response.isError).toBe(true);
330-
expect(content).toEqual(
331-
"This MongoDB cluster does not support Search Indexes. Make sure you are using an Atlas Cluster, either remotely in Atlas or using the Atlas Local image, or your cluster supports MongoDB Search."
330+
expect(content).toContain(
331+
"The connected MongoDB deployment does not support vector search indexes"
332332
);
333333
});
334334
},
@@ -345,8 +345,13 @@ describe.each([{ vectorSearchEnabled: false }, { vectorSearchEnabled: true }])(
345345
describe("and attempting to delete a non-existent index", () => {
346346
it("should fail with appropriate error", async () => {
347347
const response = await integration.mcpClient().callTool({
348-
name: "drop-search-index",
349-
arguments: { database: "any", collection: "foo", indexName: "non-existent" },
348+
name: "drop-index",
349+
arguments: {
350+
database: "any",
351+
collection: "foo",
352+
indexName: "non-existent",
353+
type: "search",
354+
},
350355
});
351356
expect(response.isError).toBe(true);
352357
const content = getResponseContent(response.content);
@@ -363,8 +368,13 @@ describe.each([{ vectorSearchEnabled: false }, { vectorSearchEnabled: true }])(
363368
describe("and attempting to delete an existing index", () => {
364369
it("should succeed in deleting the index", { timeout: SEARCH_TIMEOUT }, async () => {
365370
const response = await integration.mcpClient().callTool({
366-
name: "drop-search-index",
367-
arguments: { database: "mflix", collection: "movies", indexName: getIndexName() },
371+
name: "drop-index",
372+
arguments: {
373+
database: "mflix",
374+
collection: "movies",
375+
indexName: getIndexName(),
376+
type: "search",
377+
},
368378
});
369379
const content = getResponseContent(response.content);
370380
expect(content).toContain(
@@ -409,8 +419,13 @@ describe.each([{ vectorSearchEnabled: false }, { vectorSearchEnabled: true }])(
409419
it("should ask for confirmation before proceeding with tool call", async () => {
410420
mockElicitInput.confirmYes();
411421
await integration.mcpClient().callTool({
412-
name: "drop-search-index",
413-
arguments: { database: "mflix", collection: "movies", indexName: getIndexName() },
422+
name: "drop-index",
423+
arguments: {
424+
database: "mflix",
425+
collection: "movies",
426+
indexName: getIndexName(),
427+
type: "search",
428+
},
414429
});
415430
expect(mockElicitInput.mock).toHaveBeenCalledTimes(1);
416431
expect(mockElicitInput.mock).toHaveBeenCalledWith({
@@ -431,8 +446,13 @@ describe.each([{ vectorSearchEnabled: false }, { vectorSearchEnabled: true }])(
431446
it("should not drop the index if the confirmation was not provided", async () => {
432447
mockElicitInput.confirmNo();
433448
await integration.mcpClient().callTool({
434-
name: "drop-search-index",
435-
arguments: { database: "mflix", collection: "movies", indexName: getIndexName() },
449+
name: "drop-index",
450+
arguments: {
451+
database: "mflix",
452+
collection: "movies",
453+
indexName: getIndexName(),
454+
type: "search",
455+
},
436456
});
437457
expect(mockElicitInput.mock).toHaveBeenCalledTimes(1);
438458
expect(mockElicitInput.mock).toHaveBeenCalledWith({
@@ -446,6 +466,7 @@ describe.each([{ vectorSearchEnabled: false }, { vectorSearchEnabled: true }])(
446466
});
447467
},
448468
{
469+
getUserConfig: () => ({ ...defaultTestConfig, voyageApiKey: "test-api-key" }),
449470
downloadOptions: { search: true },
450471
getMockElicitationInput: () => mockElicitInput,
451472
}

0 commit comments

Comments
 (0)