From 4e68c4a2714df52e4efbb61458b9b10eb9c59d46 Mon Sep 17 00:00:00 2001 From: Edgar Wang Date: Tue, 13 May 2025 16:43:10 -0400 Subject: [PATCH 1/6] two functions --- src/tools/mongodb/create/createSearchIndex.ts | 38 +++++++++++++++++++ src/tools/mongodb/update/updateSearchIndex.ts | 0 2 files changed, 38 insertions(+) create mode 100644 src/tools/mongodb/create/createSearchIndex.ts create mode 100644 src/tools/mongodb/update/updateSearchIndex.ts diff --git a/src/tools/mongodb/create/createSearchIndex.ts b/src/tools/mongodb/create/createSearchIndex.ts new file mode 100644 index 00000000..a2fa3664 --- /dev/null +++ b/src/tools/mongodb/create/createSearchIndex.ts @@ -0,0 +1,38 @@ +import { DbOperationArgs, MongoDBToolBase, SearchIndexArgs } from "../mongodbTool.js"; + +export class CreateSearchIndexTool extends MongoDBToolBase { + constructor() { + super(...arguments); + this.name = "create-search-index"; + this.description = "Create an Atlas Search index for a collection"; + this.argsShape = { + ...DbOperationArgs, + name: SearchIndexArgs.name, + type: SearchIndexArgs.type, + analyzer: SearchIndexArgs.analyzer, + mappings: SearchIndexArgs.mappings, + }; + this.operationType = "create"; + } + async execute({ database, collection, name, type, analyzer, mappings, }) { + const provider = await this.ensureConnected(); + const indexes = await provider.createSearchIndexes(database, collection, [ + { + name, + type, + definition: { + analyzer, + mappings, + }, + }, + ]); + return { + content: [ + { + text: `Created the index "${indexes[0]}" on collection "${collection}" in database "${database}"`, + type: "text", + }, + ], + }; + } +} \ No newline at end of file diff --git a/src/tools/mongodb/update/updateSearchIndex.ts b/src/tools/mongodb/update/updateSearchIndex.ts new file mode 100644 index 00000000..e69de29b From 49a75e81386e34db8b6f2c572eba6e51cec9297b Mon Sep 17 00:00:00 2001 From: Edgar Wang Date: Tue, 13 May 2025 16:43:56 -0400 Subject: [PATCH 2/6] save --- src/tools/mongodb/create/createSearchIndex.ts | 39 +++++++++------ src/tools/mongodb/update/updateSearchIndex.ts | 47 +++++++++++++++++++ 2 files changed, 71 insertions(+), 15 deletions(-) diff --git a/src/tools/mongodb/create/createSearchIndex.ts b/src/tools/mongodb/create/createSearchIndex.ts index a2fa3664..0e6a1918 100644 --- a/src/tools/mongodb/create/createSearchIndex.ts +++ b/src/tools/mongodb/create/createSearchIndex.ts @@ -1,20 +1,28 @@ +import { CallToolResult } from "@modelcontextprotocol/sdk/types.js"; import { DbOperationArgs, MongoDBToolBase, SearchIndexArgs } from "../mongodbTool.js"; +import { OperationType, ToolArgs } from "../../tool.js"; export class CreateSearchIndexTool extends MongoDBToolBase { - constructor() { - super(...arguments); - this.name = "create-search-index"; - this.description = "Create an Atlas Search index for a collection"; - this.argsShape = { - ...DbOperationArgs, - name: SearchIndexArgs.name, - type: SearchIndexArgs.type, - analyzer: SearchIndexArgs.analyzer, - mappings: SearchIndexArgs.mappings, - }; - this.operationType = "create"; - } - async execute({ database, collection, name, type, analyzer, mappings, }) { + protected name = "create-search-index"; + protected description = "Create an Atlas Search index for a collection"; + protected argsShape = { + ...DbOperationArgs, + name: SearchIndexArgs.name, + type: SearchIndexArgs.type, + analyzer: SearchIndexArgs.analyzer, + mappings: SearchIndexArgs.mappings, + }; + + protected operationType: OperationType = "create"; + + protected async execute({ + database, + collection, + name, + type, + analyzer, + mappings, + }: ToolArgs): Promise { const provider = await this.ensureConnected(); const indexes = await provider.createSearchIndexes(database, collection, [ { @@ -26,6 +34,7 @@ export class CreateSearchIndexTool extends MongoDBToolBase { }, }, ]); + return { content: [ { @@ -35,4 +44,4 @@ export class CreateSearchIndexTool extends MongoDBToolBase { ], }; } -} \ No newline at end of file +} diff --git a/src/tools/mongodb/update/updateSearchIndex.ts b/src/tools/mongodb/update/updateSearchIndex.ts index e69de29b..9b2780c8 100644 --- a/src/tools/mongodb/update/updateSearchIndex.ts +++ b/src/tools/mongodb/update/updateSearchIndex.ts @@ -0,0 +1,47 @@ +import { CallToolResult } from "@modelcontextprotocol/sdk/types.js"; +import { DbOperationArgs, MongoDBToolBase, SearchIndexArgs } from "../mongodbTool.js"; +import { OperationType, ToolArgs } from "../../tool.js"; +import logger, { LogId } from "../../../logger.js"; + +export class UpdateSearchIndexTool extends MongoDBToolBase { + protected name = "update-search-index"; + protected description = "Updates a search index for a collection"; + protected argsShape = { + ...DbOperationArgs, + name: SearchIndexArgs.name, + analyzer: SearchIndexArgs.analyzer, + mappings: SearchIndexArgs.mappings, + }; + + protected operationType: OperationType = "update"; + + protected async execute({ + database, + collection, + name, + analyzer, + mappings, + }: ToolArgs): Promise { + const provider = await this.ensureConnected(); + logger.info( + LogId.toolExecute, + "server", + `Server started with transport ${JSON.stringify({ definition: { analyzer, mappings } })}` + ); + // @ts-expect-error: Interface expects a SearchIndexDefinition. However, + // passing analyzer/mappings at the root for the definition is necessary for the call to succeed. + await provider.updateSearchIndex(database, collection, name, { + analyzer, + mappings, + }); + + return { + content: [ + { + text: `Successfully updated index "${name}" on collection "${collection}" in database "${database}"`, + type: "text", + }, + ], + }; + } +} From 49ac3d84a790c26a386849b61c2917ba058f34ca Mon Sep 17 00:00:00 2001 From: Edgar Wang Date: Tue, 13 May 2025 16:52:12 -0400 Subject: [PATCH 3/6] add definitions --- src/tools/mongodb/mongodbTool.ts | 57 +++++++++++++++++++++++++++++++- src/tools/mongodb/tools.ts | 4 +++ 2 files changed, 60 insertions(+), 1 deletion(-) diff --git a/src/tools/mongodb/mongodbTool.ts b/src/tools/mongodb/mongodbTool.ts index 2ef1aee0..a09fb7b6 100644 --- a/src/tools/mongodb/mongodbTool.ts +++ b/src/tools/mongodb/mongodbTool.ts @@ -1,5 +1,5 @@ import { z } from "zod"; -import { ToolArgs, ToolBase, ToolCategory, TelemetryToolMetadata } from "../tool.js"; +import { TelemetryToolMetadata, ToolArgs, ToolBase, ToolCategory } from "../tool.js"; import { NodeDriverServiceProvider } from "@mongosh/service-provider-node-driver"; import { CallToolResult } from "@modelcontextprotocol/sdk/types.js"; import { ErrorCodes, MongoDBError } from "../../errors.js"; @@ -10,6 +10,61 @@ export const DbOperationArgs = { collection: z.string().describe("Collection name"), }; +export const SearchIndexArgs = { + name: z.string().describe("The name of the index"), + type: z.enum(["search", "vectorSearch"]).optional().default("search").describe("The type of the index"), + analyzer: z + .string() + .optional() + .default("lucene.standard") + .describe( + "The analyzer to use for the index. Can be one of the built-in lucene analyzers (`lucene.standard`, `lucene.simple`, `lucene.whitespace`, `lucene.keyword`), a language-specific analyzer, such as `lucene.cjk` or `lucene.czech`, or a custom analyzer defined in the Atlas UI." + ), + mappings: z + .object({ + dynamic: z + .boolean() + .optional() + .default(false) + .describe( + "Enables or disables dynamic mapping of fields for this index. If set to true, Atlas Search recursively indexes all dynamically indexable fields. If set to false, you must specify individual fields to index using mappings.fields." + ), + fields: z + .record( + z.string().describe("The field name"), + z + .object({ + type: z + .enum([ + "autocomplete", + "boolean", + "date", + "document", + "embeddedDocuments", + "geo", + "knnVector", + "number", + "objectId", + "string", + "token", + "uuid", + ]) + .describe("The field type"), + }) + .passthrough() + + .describe( + "The field index definition. It must contain the field type, as well as any additional options for that field type." + ) + ) + .optional() + .describe("The field mapping definitions. If `dynamic` is set to false, this is required."), + }) + .describe( + "Document describing the index to create. The definition syntax depends on whether you create a standard search index or a Vector Search index." + ), +}; + export abstract class MongoDBToolBase extends ToolBase { protected category: ToolCategory = "mongodb"; diff --git a/src/tools/mongodb/tools.ts b/src/tools/mongodb/tools.ts index d64d53ea..a98c9a2f 100644 --- a/src/tools/mongodb/tools.ts +++ b/src/tools/mongodb/tools.ts @@ -18,6 +18,8 @@ import { DropCollectionTool } from "./delete/dropCollection.js"; import { ExplainTool } from "./metadata/explain.js"; import { CreateCollectionTool } from "./create/createCollection.js"; import { LogsTool } from "./metadata/logs.js"; +import { CreateSearchIndexTool } from "./create/createSearchIndex.js"; +import { UpdateSearchIndexTool } from "./update/updateSearchIndex.js"; export const MongoDbTools = [ ConnectTool, @@ -40,4 +42,6 @@ export const MongoDbTools = [ ExplainTool, CreateCollectionTool, LogsTool, + CreateSearchIndexTool, + UpdateSearchIndexTool, ]; From 66102db7654b86cfdb93a89414b7518c58f00d2e Mon Sep 17 00:00:00 2001 From: Edgar Wang Date: Tue, 13 May 2025 16:58:15 -0400 Subject: [PATCH 4/6] remove log --- src/tools/mongodb/update/updateSearchIndex.ts | 6 ------ 1 file changed, 6 deletions(-) diff --git a/src/tools/mongodb/update/updateSearchIndex.ts b/src/tools/mongodb/update/updateSearchIndex.ts index 9b2780c8..d30e104a 100644 --- a/src/tools/mongodb/update/updateSearchIndex.ts +++ b/src/tools/mongodb/update/updateSearchIndex.ts @@ -1,7 +1,6 @@ import { CallToolResult } from "@modelcontextprotocol/sdk/types.js"; import { DbOperationArgs, MongoDBToolBase, SearchIndexArgs } from "../mongodbTool.js"; import { OperationType, ToolArgs } from "../../tool.js"; -import logger, { LogId } from "../../../logger.js"; export class UpdateSearchIndexTool extends MongoDBToolBase { protected name = "update-search-index"; @@ -23,11 +22,6 @@ export class UpdateSearchIndexTool extends MongoDBToolBase { mappings, }: ToolArgs): Promise { const provider = await this.ensureConnected(); - logger.info( - LogId.toolExecute, - "server", - `Server started with transport ${JSON.stringify({ definition: { analyzer, mappings } })}` - ); // @ts-expect-error: Interface expects a SearchIndexDefinition. However, // passing analyzer/mappings at the root for the definition is necessary for the call to succeed. await provider.updateSearchIndex(database, collection, name, { From 6b4371dc8d11569d78267df9f48d49f4e906dd10 Mon Sep 17 00:00:00 2001 From: Edgar Wang Date: Tue, 13 May 2025 17:30:36 -0400 Subject: [PATCH 5/6] be precise for search index --- src/tools/mongodb/create/createSearchIndex.ts | 8 ++++---- src/tools/mongodb/mongodbTool.ts | 4 +--- src/tools/mongodb/update/updateSearchIndex.ts | 4 ++-- 3 files changed, 7 insertions(+), 9 deletions(-) diff --git a/src/tools/mongodb/create/createSearchIndex.ts b/src/tools/mongodb/create/createSearchIndex.ts index 0e6a1918..8f01c9d1 100644 --- a/src/tools/mongodb/create/createSearchIndex.ts +++ b/src/tools/mongodb/create/createSearchIndex.ts @@ -2,13 +2,14 @@ import { CallToolResult } from "@modelcontextprotocol/sdk/types.js"; import { DbOperationArgs, MongoDBToolBase, SearchIndexArgs } from "../mongodbTool.js"; import { OperationType, ToolArgs } from "../../tool.js"; +const SEARCH_INDEX_TYPE = "search"; + export class CreateSearchIndexTool extends MongoDBToolBase { protected name = "create-search-index"; protected description = "Create an Atlas Search index for a collection"; protected argsShape = { ...DbOperationArgs, name: SearchIndexArgs.name, - type: SearchIndexArgs.type, analyzer: SearchIndexArgs.analyzer, mappings: SearchIndexArgs.mappings, }; @@ -19,7 +20,6 @@ export class CreateSearchIndexTool extends MongoDBToolBase { database, collection, name, - type, analyzer, mappings, }: ToolArgs): Promise { @@ -27,7 +27,7 @@ export class CreateSearchIndexTool extends MongoDBToolBase { const indexes = await provider.createSearchIndexes(database, collection, [ { name, - type, + type: SEARCH_INDEX_TYPE, definition: { analyzer, mappings, @@ -38,7 +38,7 @@ export class CreateSearchIndexTool extends MongoDBToolBase { return { content: [ { - text: `Created the index "${indexes[0]}" on collection "${collection}" in database "${database}"`, + text: `Created the search index "${indexes[0]}" on collection "${collection}" in database "${database}"`, type: "text", }, ], diff --git a/src/tools/mongodb/mongodbTool.ts b/src/tools/mongodb/mongodbTool.ts index a09fb7b6..14ccf2a6 100644 --- a/src/tools/mongodb/mongodbTool.ts +++ b/src/tools/mongodb/mongodbTool.ts @@ -12,7 +12,6 @@ export const DbOperationArgs = { export const SearchIndexArgs = { name: z.string().describe("The name of the index"), - type: z.enum(["search", "vectorSearch"]).optional().default("search").describe("The type of the index"), analyzer: z .string() .optional() @@ -42,7 +41,6 @@ export const SearchIndexArgs = { "document", "embeddedDocuments", "geo", - "knnVector", "number", "objectId", "string", @@ -61,7 +59,7 @@ export const SearchIndexArgs = { .describe("The field mapping definitions. If `dynamic` is set to false, this is required."), }) .describe( - "Document describing the index to create. The definition syntax depends on whether you create a standard search index or a Vector Search index." + "Document describing the index to create." ), }; diff --git a/src/tools/mongodb/update/updateSearchIndex.ts b/src/tools/mongodb/update/updateSearchIndex.ts index d30e104a..f34cce25 100644 --- a/src/tools/mongodb/update/updateSearchIndex.ts +++ b/src/tools/mongodb/update/updateSearchIndex.ts @@ -4,7 +4,7 @@ import { OperationType, ToolArgs } from "../../tool.js"; export class UpdateSearchIndexTool extends MongoDBToolBase { protected name = "update-search-index"; - protected description = "Updates a search index for a collection"; + protected description = "Updates an Atlas Search index for a collection"; protected argsShape = { ...DbOperationArgs, name: SearchIndexArgs.name, @@ -32,7 +32,7 @@ export class UpdateSearchIndexTool extends MongoDBToolBase { return { content: [ { - text: `Successfully updated index "${name}" on collection "${collection}" in database "${database}"`, + text: `Successfully updated search index "${name}" on collection "${collection}" in database "${database}"`, type: "text", }, ], From 9c2c185a52424ee5272e5397161d2abf2bd7a690 Mon Sep 17 00:00:00 2001 From: Edgar Wang Date: Tue, 13 May 2025 18:41:08 -0400 Subject: [PATCH 6/6] prettify --- src/tools/mongodb/mongodbTool.ts | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/src/tools/mongodb/mongodbTool.ts b/src/tools/mongodb/mongodbTool.ts index 14ccf2a6..b3aaaf15 100644 --- a/src/tools/mongodb/mongodbTool.ts +++ b/src/tools/mongodb/mongodbTool.ts @@ -58,9 +58,7 @@ export const SearchIndexArgs = { .optional() .describe("The field mapping definitions. If `dynamic` is set to false, this is required."), }) - .describe( - "Document describing the index to create." - ), + .describe("Document describing the index to create."), }; export abstract class MongoDBToolBase extends ToolBase {