diff --git a/src/tools/mongodb/create/createSearchIndex.ts b/src/tools/mongodb/create/createSearchIndex.ts new file mode 100644 index 00000000..8f01c9d1 --- /dev/null +++ b/src/tools/mongodb/create/createSearchIndex.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"; + +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, + analyzer: SearchIndexArgs.analyzer, + mappings: SearchIndexArgs.mappings, + }; + + protected operationType: OperationType = "create"; + + protected async execute({ + database, + collection, + name, + analyzer, + mappings, + }: ToolArgs): Promise { + const provider = await this.ensureConnected(); + const indexes = await provider.createSearchIndexes(database, collection, [ + { + name, + type: SEARCH_INDEX_TYPE, + definition: { + analyzer, + mappings, + }, + }, + ]); + + return { + content: [ + { + 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 db4e0e2a..7ce15daa 100644 --- a/src/tools/mongodb/mongodbTool.ts +++ b/src/tools/mongodb/mongodbTool.ts @@ -10,6 +10,57 @@ export const DbOperationArgs = { collection: z.string().describe("Collection name"), }; +export const SearchIndexArgs = { + name: z.string().describe("The name 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", + "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."), +}; + export enum VectorFieldType { VECTOR = "vector", FILTER = "filter", diff --git a/src/tools/mongodb/tools.ts b/src/tools/mongodb/tools.ts index ac79121c..ea8b5d3b 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"; import { CreateVectorIndexTool } from "./create/createVectorIndex.js"; import { UpdateVectorIndexTool } from "./update/updateVectorIndex.js"; import { CollectionSearchIndexesTool } from "./read/collectionSearchIndexes.js"; @@ -45,6 +47,8 @@ export const MongoDbTools = [ ExplainTool, CreateCollectionTool, LogsTool, + CreateSearchIndexTool, + UpdateSearchIndexTool, CreateVectorIndexTool, UpdateVectorIndexTool, DropSearchIndexTool, diff --git a/src/tools/mongodb/update/updateSearchIndex.ts b/src/tools/mongodb/update/updateSearchIndex.ts new file mode 100644 index 00000000..f34cce25 --- /dev/null +++ b/src/tools/mongodb/update/updateSearchIndex.ts @@ -0,0 +1,41 @@ +import { CallToolResult } from "@modelcontextprotocol/sdk/types.js"; +import { DbOperationArgs, MongoDBToolBase, SearchIndexArgs } from "../mongodbTool.js"; +import { OperationType, ToolArgs } from "../../tool.js"; + +export class UpdateSearchIndexTool extends MongoDBToolBase { + protected name = "update-search-index"; + protected description = "Updates an Atlas 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(); + // @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 search index "${name}" on collection "${collection}" in database "${database}"`, + type: "text", + }, + ], + }; + } +}