Skip to content

Commit 7a0679a

Browse files
chore: quick consolidation for ddl tools
Consolidates list-databases, list-collections, collection-indexes and collection-schema. The tool calling accuracy went from 100 to 75. The LLM was always mistaking the command name to be listDatabases instead of list-databases and then course correcting when shown the error.
1 parent 666e20c commit 7a0679a

File tree

3 files changed

+214
-22
lines changed

3 files changed

+214
-22
lines changed

src/tools/mongodb/ddl/ddl.ts

Lines changed: 163 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,163 @@
1+
import { CallToolResult } from "@modelcontextprotocol/sdk/types.js";
2+
import { DbOperationArgs, MongoDBToolBase } from "../mongodbTool.js";
3+
import * as bson from "bson";
4+
import { OperationType, ToolArgs } from "../../tool.js";
5+
import z from "zod";
6+
import { getSimplifiedSchema } from "mongodb-schema";
7+
8+
export class MongoDBDDLTool extends MongoDBToolBase {
9+
protected name = "mongodb-ddl";
10+
protected description =
11+
"List databases, collections, indexes and describe the schema of a collection in a MongoDB database";
12+
protected argsShape = {
13+
command: z.discriminatedUnion("name", [
14+
z
15+
.object({
16+
name: z.literal("list-databases"),
17+
parameters: z.object({}),
18+
})
19+
.describe("List all databases for a MongoDB connection"),
20+
z
21+
.object({
22+
name: z.literal("list-collections"),
23+
parameters: z.object({
24+
database: DbOperationArgs.database,
25+
}),
26+
})
27+
.describe("List all collections for a given database"),
28+
z
29+
.object({
30+
name: z.literal("collection-indexes"),
31+
parameters: z.object(DbOperationArgs),
32+
})
33+
.describe("Describe the indexes for a collection"),
34+
z
35+
.object({
36+
name: z.literal("collection-schema"),
37+
parameters: z.object(DbOperationArgs),
38+
})
39+
.describe("Describe the schema for a collection"),
40+
]),
41+
};
42+
protected operationType: OperationType = "read";
43+
44+
protected async execute({ command }: ToolArgs<typeof this.argsShape>): Promise<CallToolResult> {
45+
const provider = await this.ensureConnected();
46+
if (command.name === "list-databases") {
47+
const dbs = (await provider.listDatabases("")).databases as { name: string; sizeOnDisk: bson.Long }[];
48+
49+
return {
50+
content: dbs.map((db) => {
51+
return {
52+
text: `Name: ${db.name}, Size: ${db.sizeOnDisk.toString()} bytes`,
53+
type: "text",
54+
};
55+
}),
56+
};
57+
}
58+
59+
if (command.name === "list-collections") {
60+
const { database } = command.parameters;
61+
const collections = await provider.listCollections(database);
62+
63+
if (collections.length === 0) {
64+
return {
65+
content: [
66+
{
67+
type: "text",
68+
text: `No collections found for database "${database}". To create a collection, use the "create-collection" tool.`,
69+
},
70+
],
71+
};
72+
}
73+
74+
return {
75+
content: collections.map((collection) => {
76+
return {
77+
text: `Name: "${collection.name}"`,
78+
type: "text",
79+
};
80+
}),
81+
};
82+
}
83+
84+
if (command.name === "collection-indexes") {
85+
const { database, collection } = command.parameters;
86+
const indexes = await provider.getIndexes(database, collection);
87+
88+
return {
89+
content: [
90+
{
91+
text: `Found ${indexes.length} indexes in the collection "${collection}":`,
92+
type: "text",
93+
},
94+
...(indexes.map((indexDefinition) => {
95+
return {
96+
text: `Name "${indexDefinition.name}", definition: ${JSON.stringify(indexDefinition.key)}`,
97+
type: "text",
98+
};
99+
}) as { text: string; type: "text" }[]),
100+
],
101+
};
102+
}
103+
104+
if (command.name === "collection-schema") {
105+
const { database, collection } = command.parameters;
106+
const documents = await provider.find(database, collection, {}, { limit: 5 }).toArray();
107+
const schema = await getSimplifiedSchema(documents);
108+
109+
const fieldsCount = Object.entries(schema).length;
110+
if (fieldsCount === 0) {
111+
return {
112+
content: [
113+
{
114+
text: `Could not deduce the schema for "${database}.${collection}". This may be because it doesn't exist or is empty.`,
115+
type: "text",
116+
},
117+
],
118+
};
119+
}
120+
121+
return {
122+
content: [
123+
{
124+
text: `Found ${fieldsCount} fields in the schema for "${database}.${collection}"`,
125+
type: "text",
126+
},
127+
{
128+
text: JSON.stringify(schema),
129+
type: "text",
130+
},
131+
],
132+
};
133+
}
134+
135+
return {
136+
content: [
137+
{
138+
text: `Unknown command provided to the tool.`,
139+
type: "text",
140+
},
141+
],
142+
};
143+
}
144+
145+
protected handleError(
146+
error: unknown,
147+
args: ToolArgs<typeof this.argsShape>
148+
): Promise<CallToolResult> | CallToolResult {
149+
if (args.command.name === "collection-indexes") {
150+
if (error instanceof Error && "codeName" in error && error.codeName === "NamespaceNotFound") {
151+
return {
152+
content: [
153+
{
154+
text: `The indexes for "${args.command.parameters.database}.${args.command.parameters.collection}" cannot be determined because the collection does not exist.`,
155+
type: "text",
156+
},
157+
],
158+
};
159+
}
160+
}
161+
return super.handleError(error, args);
162+
}
163+
}

src/tools/mongodb/tools.ts

Lines changed: 2 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,5 @@
11
import { ConnectTool } from "./connect/connect.js";
2-
import { ListCollectionsTool } from "./metadata/listCollections.js";
3-
import { CollectionIndexesTool } from "./read/collectionIndexes.js";
4-
import { ListDatabasesTool } from "./metadata/listDatabases.js";
52
import { CreateIndexTool } from "./create/createIndex.js";
6-
import { CollectionSchemaTool } from "./metadata/collectionSchema.js";
73
import { FindTool } from "./read/find.js";
84
import { InsertManyTool } from "./create/insertMany.js";
95
import { DeleteManyTool } from "./delete/deleteMany.js";
@@ -18,14 +14,12 @@ import { DropCollectionTool } from "./delete/dropCollection.js";
1814
import { ExplainTool } from "./metadata/explain.js";
1915
import { CreateCollectionTool } from "./create/createCollection.js";
2016
import { LogsTool } from "./metadata/logs.js";
17+
import { MongoDBDDLTool } from "./ddl/ddl.js";
2118

2219
export const MongoDbTools = [
2320
ConnectTool,
24-
ListCollectionsTool,
25-
ListDatabasesTool,
26-
CollectionIndexesTool,
21+
MongoDBDDLTool,
2722
CreateIndexTool,
28-
CollectionSchemaTool,
2923
FindTool,
3024
InsertManyTool,
3125
DeleteManyTool,

tests/accuracy/list-collections.test.ts

Lines changed: 49 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -7,8 +7,13 @@ function callsListCollections(prompt: string): AccuracyTestConfig {
77
prompt: prompt,
88
expectedToolCalls: [
99
{
10-
toolName: "list-collections",
11-
parameters: { database: "mflix" },
10+
toolName: "mongodb-ddl",
11+
parameters: {
12+
command: {
13+
name: "list-collections",
14+
parameters: { database: "mflix" },
15+
},
16+
},
1217
},
1318
],
1419
};
@@ -21,28 +26,58 @@ function callsListDatabasesAndListCollections(prompt: string): AccuracyTestConfi
2126
mockedTools: {},
2227
expectedToolCalls: [
2328
{
24-
toolName: "list-databases",
25-
parameters: {},
29+
toolName: "mongodb-ddl",
30+
parameters: {
31+
command: {
32+
name: "list-databases",
33+
parameters: {},
34+
},
35+
},
2636
},
2737
{
28-
toolName: "list-collections",
29-
parameters: { database: "admin" },
38+
toolName: "mongodb-ddl",
39+
parameters: {
40+
command: {
41+
name: "list-collections",
42+
parameters: { database: "admin" },
43+
},
44+
},
3045
},
3146
{
32-
toolName: "list-collections",
33-
parameters: { database: "comics" },
47+
toolName: "mongodb-ddl",
48+
parameters: {
49+
command: {
50+
name: "list-collections",
51+
parameters: { database: "comics" },
52+
},
53+
},
3454
},
3555
{
36-
toolName: "list-collections",
37-
parameters: { database: "config" },
56+
toolName: "mongodb-ddl",
57+
parameters: {
58+
command: {
59+
name: "list-collections",
60+
parameters: { database: "config" },
61+
},
62+
},
3863
},
3964
{
40-
toolName: "list-collections",
41-
parameters: { database: "local" },
65+
toolName: "mongodb-ddl",
66+
parameters: {
67+
command: {
68+
name: "list-collections",
69+
parameters: { database: "local" },
70+
},
71+
},
4272
},
4373
{
44-
toolName: "list-collections",
45-
parameters: { database: "mflix" },
74+
toolName: "mongodb-ddl",
75+
parameters: {
76+
command: {
77+
name: "list-collections",
78+
parameters: { database: "mflix" },
79+
},
80+
},
4681
},
4782
],
4883
};

0 commit comments

Comments
 (0)