Skip to content

Commit f7282c5

Browse files
committed
save
save save vector index args save save save saving vector search
1 parent 4116d42 commit f7282c5

File tree

4 files changed

+158
-1
lines changed

4 files changed

+158
-1
lines changed
Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
import { CallToolResult } from "@modelcontextprotocol/sdk/types.js";
2+
import {
3+
DbOperationArgs,
4+
MongoDBToolBase,
5+
VectorFieldType,
6+
VectorIndexArgs,
7+
} from "../mongodbTool.js";
8+
import { OperationType, ToolArgs } from "../../tool.js";
9+
10+
const VECTOR_INDEX_TYPE = "vectorSearch";
11+
export class CreateVectorIndexTool extends MongoDBToolBase {
12+
protected name = "create-vector-index";
13+
protected description = "Create an Atlas Search vector for a collection";
14+
protected argsShape = {
15+
...DbOperationArgs,
16+
name: VectorIndexArgs.name,
17+
vectorDefinition: VectorIndexArgs.vectorDefinition,
18+
filterFields: VectorIndexArgs.filterFields,
19+
};
20+
21+
protected operationType: OperationType = "create";
22+
23+
protected async execute({
24+
database,
25+
collection,
26+
name,
27+
vectorDefinition,
28+
filterFields,
29+
}: ToolArgs<typeof this.argsShape>): Promise<CallToolResult> {
30+
const provider = await this.ensureConnected();
31+
32+
const typedVectorField = { ...vectorDefinition, type: VectorFieldType.VECTOR };
33+
const typedFilterFields =
34+
filterFields?.map((v) => ({
35+
...v,
36+
type: VectorFieldType.FILTER,
37+
})) || [];
38+
const indexes = await provider.createSearchIndexes(database, collection, [
39+
{
40+
name,
41+
type: VECTOR_INDEX_TYPE,
42+
definition: { fields: [typedVectorField, ...typedFilterFields] },
43+
},
44+
]);
45+
46+
return {
47+
content: [
48+
{
49+
text: `Created the vector index ${indexes[0]} on collection "${collection}" in database "${database}"`,
50+
type: "text",
51+
},
52+
],
53+
};
54+
}
55+
}

src/tools/mongodb/mongodbTool.ts

Lines changed: 46 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import { z } from "zod";
2-
import { ToolArgs, ToolBase, ToolCategory, TelemetryToolMetadata } from "../tool.js";
2+
import { TelemetryToolMetadata, ToolArgs, ToolBase, ToolCategory } from "../tool.js";
33
import { NodeDriverServiceProvider } from "@mongosh/service-provider-node-driver";
44
import { CallToolResult } from "@modelcontextprotocol/sdk/types.js";
55
import { ErrorCodes, MongoDBError } from "../../errors.js";
@@ -10,6 +10,51 @@ export const DbOperationArgs = {
1010
collection: z.string().describe("Collection name"),
1111
};
1212

13+
export enum VectorFieldType {
14+
VECTOR = "vector",
15+
FILTER = "filter",
16+
}
17+
export const VectorIndexArgs = {
18+
name: z.string().describe("The name of the index"),
19+
vectorDefinition: z
20+
.object({
21+
path: z
22+
.string()
23+
.describe(
24+
"Name of the field to index. For nested fields, use dot notation to specify path to embedded fields."
25+
),
26+
numDimensions: z
27+
.number()
28+
.int()
29+
.min(1)
30+
.max(8192)
31+
.describe("Number of vector dimensions to enforce at index-time and query-time."),
32+
similarity: z
33+
.enum(["euclidean", "cosine", "dotProduct"])
34+
.describe("Vector similarity function to use to search for top K-nearest neighbors."),
35+
quantization: z
36+
.enum(["none", "scalar", "binary"])
37+
.default("none")
38+
.optional()
39+
.describe(
40+
"Automatic vector quantization. Use this setting only if your embeddings are float or double vectors."
41+
),
42+
})
43+
.describe("The vector index definition."),
44+
filterFields: z
45+
.array(
46+
z.object({
47+
path: z
48+
.string()
49+
.describe(
50+
"Name of the field to filter by. For nested fields, use dot notation to specify path to embedded fields."
51+
),
52+
})
53+
)
54+
.optional()
55+
.describe("Additional indexed fields that pre-filter data."),
56+
};
57+
1358
export abstract class MongoDBToolBase extends ToolBase {
1459
protected category: ToolCategory = "mongodb";
1560

src/tools/mongodb/tools.ts

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,8 @@ import { DropCollectionTool } from "./delete/dropCollection.js";
1818
import { ExplainTool } from "./metadata/explain.js";
1919
import { CreateCollectionTool } from "./create/createCollection.js";
2020
import { LogsTool } from "./metadata/logs.js";
21+
import { CreateVectorIndexTool } from "./create/createVectorIndex.js";
22+
import { UpdateVectorIndexTool } from "./update/updateVectorIndex.js";
2123

2224
export const MongoDbTools = [
2325
ConnectTool,
@@ -40,4 +42,7 @@ export const MongoDbTools = [
4042
ExplainTool,
4143
CreateCollectionTool,
4244
LogsTool,
45+
46+
CreateVectorIndexTool,
47+
UpdateVectorIndexTool,
4348
];
Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
import { CallToolResult } from "@modelcontextprotocol/sdk/types.js";
2+
import {
3+
DbOperationArgs,
4+
MongoDBToolBase,
5+
VectorFieldType,
6+
VectorIndexArgs,
7+
} from "../mongodbTool.js";
8+
import { OperationType, ToolArgs } from "../../tool.js";
9+
10+
export class UpdateVectorIndexTool extends MongoDBToolBase {
11+
protected name = "update-vector-index";
12+
protected description = "Updates an Atlas Search vector for a collection";
13+
protected argsShape = {
14+
...DbOperationArgs,
15+
name: VectorIndexArgs.name,
16+
vectorDefinition: VectorIndexArgs.vectorDefinition,
17+
filterFields: VectorIndexArgs.filterFields,
18+
};
19+
20+
protected operationType: OperationType = "create";
21+
22+
protected async execute({
23+
database,
24+
collection,
25+
name,
26+
vectorDefinition,
27+
filterFields,
28+
}: ToolArgs<typeof this.argsShape>): Promise<CallToolResult> {
29+
const provider = await this.ensureConnected();
30+
31+
const typedVectorField = { ...vectorDefinition, type: VectorFieldType.VECTOR };
32+
const typedFilterFields =
33+
filterFields?.map((v) => ({
34+
...v,
35+
type: VectorFieldType.FILTER,
36+
})) || [];
37+
// @ts-expect-error: Interface expects a SearchIndexDefinition {definition: {fields}}. However,
38+
// passing fields at the root level is necessary for the call to succeed.
39+
await provider.updateSearchIndex(database, collection, name, {
40+
fields: [typedVectorField, ...typedFilterFields],
41+
});
42+
43+
return {
44+
content: [
45+
{
46+
text: `Successfully updated vector index "${name}" on collection "${collection}" in database "${database}"`,
47+
type: "text",
48+
},
49+
],
50+
};
51+
}
52+
}

0 commit comments

Comments
 (0)