Skip to content

Commit b6285eb

Browse files
committed
feat(search): Add a new tool to list search and vector search indexes
It detects if the cluster supports search indexes the same way we do in all other tooling: by catching the exception. In some old clusters it might still return an empty list for unsupported custers, but we can't really know for sure.
1 parent b9aa684 commit b6285eb

File tree

2 files changed

+62
-0
lines changed

2 files changed

+62
-0
lines changed
Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,60 @@
1+
import type { CallToolResult } from "@modelcontextprotocol/sdk/types.js";
2+
import type { Document } from "mongodb";
3+
import type { ToolArgs, OperationType } from "../../tool.js";
4+
import { DbOperationArgs, MongoDBToolBase } from "../mongodbTool.js";
5+
import { formatUntrustedData } from "../../tool.js";
6+
7+
export class ListSearchIndexesTool extends MongoDBToolBase {
8+
public name = "list-search-indexes";
9+
protected description = "Describes the search and vector search indexes for a collection";
10+
protected argsShape = DbOperationArgs;
11+
public operationType: OperationType = "metadata";
12+
13+
protected async execute({ database, collection }: ToolArgs<typeof DbOperationArgs>): Promise<CallToolResult> {
14+
const provider = await this.ensureConnected();
15+
const indexes: Document[] = await provider.getSearchIndexes(database, collection);
16+
const trimmedIndexDefinitions = this.pickRelevantInformation(indexes);
17+
18+
if (trimmedIndexDefinitions.length > 0) {
19+
return {
20+
content: formatUntrustedData(
21+
`Found ${trimmedIndexDefinitions.length} search and vector search indexes in ${database}.${collection}`,
22+
indexes.map((index) => JSON.stringify(index)).join("\n")
23+
),
24+
};
25+
} else {
26+
return {
27+
content: formatUntrustedData(
28+
`There are no search or vector search indexes in ${database}.${collection}`
29+
),
30+
};
31+
}
32+
}
33+
34+
/**
35+
* Atlas Search index status contains a lot of information that is not relevant for the agent at this stage.
36+
* Like for example, the status on each of the dedicated nodes. We only care about the main status, if it's
37+
* queryable and the index name. We are also picking the index definition as it can be used by the agent to
38+
* understand which fields are available for searching.
39+
**/
40+
protected pickRelevantInformation(indexes: Document[]): Document[] {
41+
return indexes.map((index) => ({
42+
name: index["name"] ?? "default",
43+
type: index["type"] ?? "UNKNOWN",
44+
status: index["status"] ?? "UNKNOWN",
45+
queryable: index["queryable"] ?? false,
46+
latestDefinition: index["latestDefinition"],
47+
}));
48+
}
49+
50+
protected handleError(): Promise<CallToolResult> | CallToolResult {
51+
return {
52+
content: [
53+
{
54+
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.",
55+
type: "text",
56+
},
57+
],
58+
};
59+
}
60+
}

src/tools/mongodb/tools.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ import { ExplainTool } from "./metadata/explain.js";
1919
import { CreateCollectionTool } from "./create/createCollection.js";
2020
import { LogsTool } from "./metadata/logs.js";
2121
import { ExportTool } from "./read/export.js";
22+
import { ListSearchIndexesTool } from "./search/listSearchIndexes.js";
2223

2324
export const MongoDbTools = [
2425
ConnectTool,
@@ -42,4 +43,5 @@ export const MongoDbTools = [
4243
CreateCollectionTool,
4344
LogsTool,
4445
ExportTool,
46+
ListSearchIndexesTool,
4547
];

0 commit comments

Comments
 (0)