mcp-mongo-server 0.2.0 → 1.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +35 -14
- package/build/index.js +705 -99
- package/build/types.js +41 -15
- package/package.json +51 -51
package/README.md
CHANGED
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|

|
|
6
6
|
[](https://smithery.ai/server/mcp-mongo-server)
|
|
7
7
|
|
|
8
|
-
A Model Context Protocol server that provides access to MongoDB databases. This server enables LLMs to inspect collection schemas and execute
|
|
8
|
+
A Model Context Protocol server that provides access to MongoDB databases. This server enables LLMs to inspect collection schemas and execute MongoDB operations.
|
|
9
9
|
|
|
10
10
|
## Demo
|
|
11
11
|
|
|
@@ -20,23 +20,44 @@ A Model Context Protocol server that provides access to MongoDB databases. This
|
|
|
20
20
|
|
|
21
21
|
### Tools
|
|
22
22
|
- **query**
|
|
23
|
-
- Execute
|
|
24
|
-
- Input:
|
|
25
|
-
-
|
|
23
|
+
- Execute MongoDB queries with optional execution plan analysis
|
|
24
|
+
- Input: Collection name, filter, projection, limit, explain options
|
|
25
|
+
- Returns query results or execution plan
|
|
26
26
|
|
|
27
27
|
- **aggregate**
|
|
28
|
-
- Execute
|
|
29
|
-
- Input:
|
|
30
|
-
-
|
|
28
|
+
- Execute MongoDB aggregation pipelines with optional execution plan analysis
|
|
29
|
+
- Input: Collection name, pipeline stages, explain options
|
|
30
|
+
- Returns aggregation results or execution plan
|
|
31
|
+
|
|
32
|
+
- **update**
|
|
33
|
+
- Update documents in a collection
|
|
34
|
+
- Input: Collection name, filter, update operations, upsert/multi options
|
|
35
|
+
- Returns update operation results
|
|
36
|
+
|
|
37
|
+
- **serverInfo**
|
|
38
|
+
- Get MongoDB server information and status
|
|
39
|
+
- Input: Optional debug info flag
|
|
40
|
+
- Returns version, storage engine, and server details
|
|
41
|
+
|
|
42
|
+
- **insert**
|
|
43
|
+
- Insert documents into a collection
|
|
44
|
+
- Input: Collection name, documents array, write options
|
|
45
|
+
- Returns insert operation results
|
|
46
|
+
|
|
47
|
+
- **createIndex**
|
|
48
|
+
- Create indexes on a collection
|
|
49
|
+
- Input: Collection name, index specifications, write options
|
|
50
|
+
- Returns index creation results
|
|
51
|
+
|
|
52
|
+
- **count**
|
|
53
|
+
- Count documents matching a query
|
|
54
|
+
- Input: Collection name, query filter, count options
|
|
55
|
+
- Returns document count
|
|
31
56
|
|
|
32
57
|
### Prompts
|
|
33
|
-
- `
|
|
34
|
-
- Input:
|
|
35
|
-
- Output:
|
|
36
|
-
- `aggregate` - Provide insights about the collection's structure, data types, and basic statistics
|
|
37
|
-
- Input: `collection` (string): The name of the collection to analyze
|
|
38
|
-
- Output: `text` (string): A summary of the collection's structure, data types, and basic statistics
|
|
39
|
-
|
|
58
|
+
- `analyze_collection` - Analyze collection structure and contents
|
|
59
|
+
- Input: Collection name
|
|
60
|
+
- Output: Insights about schema, data types, and statistics
|
|
40
61
|
|
|
41
62
|
|
|
42
63
|
## Development
|
package/build/index.js
CHANGED
|
@@ -9,8 +9,8 @@
|
|
|
9
9
|
*/
|
|
10
10
|
import { Server } from "@modelcontextprotocol/sdk/server/index.js";
|
|
11
11
|
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
|
|
12
|
-
import { CallToolRequestSchema, ListResourcesRequestSchema, ListToolsRequestSchema, ReadResourceRequestSchema, ListPromptsRequestSchema, GetPromptRequestSchema, ListResourceTemplatesRequestSchema, } from "@modelcontextprotocol/sdk/types.js";
|
|
13
|
-
import { MongoClient
|
|
12
|
+
import { CallToolRequestSchema, ListResourcesRequestSchema, ListToolsRequestSchema, ReadResourceRequestSchema, ListPromptsRequestSchema, GetPromptRequestSchema, ListResourceTemplatesRequestSchema, PingRequestSchema, } from "@modelcontextprotocol/sdk/types.js";
|
|
13
|
+
import { MongoClient } from "mongodb";
|
|
14
14
|
/**
|
|
15
15
|
* MongoDB connection client and database reference
|
|
16
16
|
*/
|
|
@@ -22,7 +22,7 @@ let db = null;
|
|
|
22
22
|
*/
|
|
23
23
|
const server = new Server({
|
|
24
24
|
name: "mongodb",
|
|
25
|
-
version: "
|
|
25
|
+
version: "1.1.0",
|
|
26
26
|
}, {
|
|
27
27
|
capabilities: {
|
|
28
28
|
resources: {},
|
|
@@ -35,13 +35,7 @@ const server = new Server({
|
|
|
35
35
|
*/
|
|
36
36
|
async function connectToMongoDB(url) {
|
|
37
37
|
try {
|
|
38
|
-
client = new MongoClient(url
|
|
39
|
-
serverApi: {
|
|
40
|
-
version: ServerApiVersion.v1,
|
|
41
|
-
strict: true,
|
|
42
|
-
deprecationErrors: true,
|
|
43
|
-
},
|
|
44
|
-
});
|
|
38
|
+
client = new MongoClient(url);
|
|
45
39
|
await client.connect();
|
|
46
40
|
db = client.db();
|
|
47
41
|
return true;
|
|
@@ -51,6 +45,26 @@ async function connectToMongoDB(url) {
|
|
|
51
45
|
return false;
|
|
52
46
|
}
|
|
53
47
|
}
|
|
48
|
+
/**
|
|
49
|
+
* Handler for ping requests to check server health
|
|
50
|
+
*/
|
|
51
|
+
server.setRequestHandler(PingRequestSchema, async () => {
|
|
52
|
+
try {
|
|
53
|
+
// Check MongoDB connection
|
|
54
|
+
if (!client) {
|
|
55
|
+
throw new Error("MongoDB connection is not available");
|
|
56
|
+
}
|
|
57
|
+
// Ping MongoDB to verify connection
|
|
58
|
+
await db.command({ ping: 1 });
|
|
59
|
+
return {};
|
|
60
|
+
}
|
|
61
|
+
catch (error) {
|
|
62
|
+
if (error instanceof Error) {
|
|
63
|
+
throw new Error(`MongoDB ping failed: ${error.message}`);
|
|
64
|
+
}
|
|
65
|
+
throw new Error("MongoDB ping failed: Unknown error");
|
|
66
|
+
}
|
|
67
|
+
});
|
|
54
68
|
/**
|
|
55
69
|
* Handler for listing available collections as resources.
|
|
56
70
|
* Each collection is exposed as a resource with:
|
|
@@ -74,7 +88,7 @@ server.setRequestHandler(ListResourcesRequestSchema, async () => {
|
|
|
74
88
|
if (error instanceof Error) {
|
|
75
89
|
throw new Error(`Failed to list collections: ${error.message}`);
|
|
76
90
|
}
|
|
77
|
-
throw new Error(
|
|
91
|
+
throw new Error("Failed to list collections: Unknown error");
|
|
78
92
|
}
|
|
79
93
|
});
|
|
80
94
|
/**
|
|
@@ -89,29 +103,33 @@ server.setRequestHandler(ReadResourceRequestSchema, async (request) => {
|
|
|
89
103
|
const sample = await collection.findOne({});
|
|
90
104
|
const indexes = await collection.indexes();
|
|
91
105
|
// Infer schema from sample document
|
|
92
|
-
const schema = sample
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
106
|
+
const schema = sample
|
|
107
|
+
? {
|
|
108
|
+
type: "collection",
|
|
109
|
+
name: collectionName,
|
|
110
|
+
fields: Object.entries(sample).map(([key, value]) => ({
|
|
111
|
+
name: key,
|
|
112
|
+
type: typeof value,
|
|
113
|
+
})),
|
|
114
|
+
indexes: indexes.map((idx) => ({
|
|
115
|
+
name: idx.name,
|
|
116
|
+
keys: idx.key,
|
|
117
|
+
})),
|
|
118
|
+
}
|
|
119
|
+
: {
|
|
120
|
+
type: "collection",
|
|
121
|
+
name: collectionName,
|
|
122
|
+
fields: [],
|
|
123
|
+
indexes: [],
|
|
124
|
+
};
|
|
109
125
|
return {
|
|
110
|
-
contents: [
|
|
126
|
+
contents: [
|
|
127
|
+
{
|
|
111
128
|
uri: request.params.uri,
|
|
112
129
|
mimeType: "application/json",
|
|
113
|
-
text: JSON.stringify(schema, null, 2)
|
|
114
|
-
}
|
|
130
|
+
text: JSON.stringify(schema, null, 2),
|
|
131
|
+
},
|
|
132
|
+
],
|
|
115
133
|
};
|
|
116
134
|
}
|
|
117
135
|
catch (error) {
|
|
@@ -130,49 +148,254 @@ server.setRequestHandler(ListToolsRequestSchema, async () => {
|
|
|
130
148
|
tools: [
|
|
131
149
|
{
|
|
132
150
|
name: "query",
|
|
133
|
-
description: "Execute a MongoDB query",
|
|
151
|
+
description: "Execute a MongoDB query with optional execution plan analysis",
|
|
134
152
|
inputSchema: {
|
|
135
153
|
type: "object",
|
|
136
154
|
properties: {
|
|
137
155
|
collection: {
|
|
138
156
|
type: "string",
|
|
139
|
-
description: "Name of the collection to query"
|
|
157
|
+
description: "Name of the collection to query",
|
|
140
158
|
},
|
|
141
159
|
filter: {
|
|
142
160
|
type: "object",
|
|
143
|
-
description: "MongoDB query filter"
|
|
161
|
+
description: "MongoDB query filter",
|
|
144
162
|
},
|
|
145
163
|
projection: {
|
|
146
164
|
type: "object",
|
|
147
|
-
description: "Fields to include/exclude"
|
|
165
|
+
description: "Fields to include/exclude",
|
|
148
166
|
},
|
|
149
167
|
limit: {
|
|
150
168
|
type: "number",
|
|
151
|
-
description: "Maximum number of documents to return"
|
|
152
|
-
}
|
|
169
|
+
description: "Maximum number of documents to return",
|
|
170
|
+
},
|
|
171
|
+
explain: {
|
|
172
|
+
type: "string",
|
|
173
|
+
description: "Optional: Get query execution information (queryPlanner, executionStats, or allPlansExecution)",
|
|
174
|
+
enum: ["queryPlanner", "executionStats", "allPlansExecution"],
|
|
175
|
+
},
|
|
153
176
|
},
|
|
154
|
-
required: ["collection"]
|
|
155
|
-
}
|
|
177
|
+
required: ["collection"],
|
|
178
|
+
},
|
|
156
179
|
},
|
|
157
180
|
{
|
|
158
181
|
name: "aggregate",
|
|
159
|
-
description: "Execute a MongoDB aggregation pipeline",
|
|
182
|
+
description: "Execute a MongoDB aggregation pipeline with optional execution plan analysis",
|
|
160
183
|
inputSchema: {
|
|
161
184
|
type: "object",
|
|
162
185
|
properties: {
|
|
163
186
|
collection: {
|
|
164
187
|
type: "string",
|
|
165
|
-
description: "Name of the collection to aggregate"
|
|
188
|
+
description: "Name of the collection to aggregate",
|
|
166
189
|
},
|
|
167
190
|
pipeline: {
|
|
168
191
|
type: "array",
|
|
169
|
-
description: "Aggregation pipeline stages"
|
|
170
|
-
}
|
|
192
|
+
description: "Aggregation pipeline stages",
|
|
193
|
+
},
|
|
194
|
+
explain: {
|
|
195
|
+
type: "string",
|
|
196
|
+
description: "Optional: Get aggregation execution information (queryPlanner, executionStats, or allPlansExecution)",
|
|
197
|
+
enum: ["queryPlanner", "executionStats", "allPlansExecution"],
|
|
198
|
+
},
|
|
199
|
+
},
|
|
200
|
+
required: ["collection", "pipeline"],
|
|
201
|
+
},
|
|
202
|
+
},
|
|
203
|
+
{
|
|
204
|
+
name: "update",
|
|
205
|
+
description: "Update documents in a MongoDB collection",
|
|
206
|
+
inputSchema: {
|
|
207
|
+
type: "object",
|
|
208
|
+
properties: {
|
|
209
|
+
collection: {
|
|
210
|
+
type: "string",
|
|
211
|
+
description: "Name of the collection to update",
|
|
212
|
+
},
|
|
213
|
+
filter: {
|
|
214
|
+
type: "object",
|
|
215
|
+
description: "Filter to select documents to update",
|
|
216
|
+
},
|
|
217
|
+
update: {
|
|
218
|
+
type: "object",
|
|
219
|
+
description: "Update operations to apply ($set, $unset, $inc, etc.)",
|
|
220
|
+
},
|
|
221
|
+
upsert: {
|
|
222
|
+
type: "boolean",
|
|
223
|
+
description: "Create a new document if no documents match the filter",
|
|
224
|
+
},
|
|
225
|
+
multi: {
|
|
226
|
+
type: "boolean",
|
|
227
|
+
description: "Update multiple documents that match the filter",
|
|
228
|
+
},
|
|
171
229
|
},
|
|
172
|
-
required: ["collection", "
|
|
230
|
+
required: ["collection", "filter", "update"],
|
|
231
|
+
},
|
|
232
|
+
},
|
|
233
|
+
{
|
|
234
|
+
name: "serverInfo",
|
|
235
|
+
description: "Get MongoDB server information including version, storage engine, and other details",
|
|
236
|
+
inputSchema: {
|
|
237
|
+
type: "object",
|
|
238
|
+
properties: {
|
|
239
|
+
includeDebugInfo: {
|
|
240
|
+
type: "boolean",
|
|
241
|
+
description: "Include additional debug information about the server",
|
|
242
|
+
},
|
|
243
|
+
},
|
|
244
|
+
},
|
|
245
|
+
},
|
|
246
|
+
{
|
|
247
|
+
name: "insert",
|
|
248
|
+
description: "Insert one or more documents into a MongoDB collection",
|
|
249
|
+
inputSchema: {
|
|
250
|
+
type: "object",
|
|
251
|
+
properties: {
|
|
252
|
+
collection: {
|
|
253
|
+
type: "string",
|
|
254
|
+
description: "Name of the collection to insert into",
|
|
255
|
+
},
|
|
256
|
+
documents: {
|
|
257
|
+
type: "array",
|
|
258
|
+
description: "Array of documents to insert",
|
|
259
|
+
items: {
|
|
260
|
+
type: "object",
|
|
261
|
+
},
|
|
262
|
+
},
|
|
263
|
+
ordered: {
|
|
264
|
+
type: "boolean",
|
|
265
|
+
description: "Optional: If true, perform an ordered insert of the documents. If false, perform an unordered insert",
|
|
266
|
+
},
|
|
267
|
+
writeConcern: {
|
|
268
|
+
type: "object",
|
|
269
|
+
description: "Optional: Write concern for the insert operation",
|
|
270
|
+
},
|
|
271
|
+
bypassDocumentValidation: {
|
|
272
|
+
type: "boolean",
|
|
273
|
+
description: "Optional: Allow insert to bypass schema validation",
|
|
274
|
+
},
|
|
275
|
+
},
|
|
276
|
+
required: ["collection", "documents"],
|
|
277
|
+
},
|
|
278
|
+
},
|
|
279
|
+
{
|
|
280
|
+
name: "createIndex",
|
|
281
|
+
description: "Create one or more indexes on a MongoDB collection",
|
|
282
|
+
inputSchema: {
|
|
283
|
+
type: "object",
|
|
284
|
+
properties: {
|
|
285
|
+
collection: {
|
|
286
|
+
type: "string",
|
|
287
|
+
description: "Name of the collection to create indexes on",
|
|
288
|
+
},
|
|
289
|
+
indexes: {
|
|
290
|
+
type: "array",
|
|
291
|
+
description: "Array of index specifications",
|
|
292
|
+
items: {
|
|
293
|
+
type: "object",
|
|
294
|
+
properties: {
|
|
295
|
+
key: {
|
|
296
|
+
type: "object",
|
|
297
|
+
description: "Index key pattern, e.g. { field: 1 } for ascending, { field: -1 } for descending",
|
|
298
|
+
},
|
|
299
|
+
name: {
|
|
300
|
+
type: "string",
|
|
301
|
+
description: "Optional: Name of the index",
|
|
302
|
+
},
|
|
303
|
+
unique: {
|
|
304
|
+
type: "boolean",
|
|
305
|
+
description: "Optional: If true, creates a unique index",
|
|
306
|
+
},
|
|
307
|
+
sparse: {
|
|
308
|
+
type: "boolean",
|
|
309
|
+
description: "Optional: If true, creates a sparse index",
|
|
310
|
+
},
|
|
311
|
+
background: {
|
|
312
|
+
type: "boolean",
|
|
313
|
+
description: "Optional: If true, creates the index in the background",
|
|
314
|
+
},
|
|
315
|
+
expireAfterSeconds: {
|
|
316
|
+
type: "number",
|
|
317
|
+
description: "Optional: Specifies the TTL for documents (time to live)",
|
|
318
|
+
},
|
|
319
|
+
partialFilterExpression: {
|
|
320
|
+
type: "object",
|
|
321
|
+
description: "Optional: Filter expression for partial indexes",
|
|
322
|
+
},
|
|
323
|
+
},
|
|
324
|
+
required: ["key"],
|
|
325
|
+
},
|
|
326
|
+
},
|
|
327
|
+
writeConcern: {
|
|
328
|
+
type: "object",
|
|
329
|
+
description: "Optional: Write concern for the index creation",
|
|
330
|
+
},
|
|
331
|
+
commitQuorum: {
|
|
332
|
+
type: ["string", "number"],
|
|
333
|
+
description: "Optional: Number of voting members required to create index",
|
|
334
|
+
},
|
|
335
|
+
},
|
|
336
|
+
required: ["collection", "indexes"],
|
|
337
|
+
},
|
|
338
|
+
},
|
|
339
|
+
{
|
|
340
|
+
name: "count",
|
|
341
|
+
description: "Count the number of documents in a collection that match a query",
|
|
342
|
+
inputSchema: {
|
|
343
|
+
type: "object",
|
|
344
|
+
properties: {
|
|
345
|
+
collection: {
|
|
346
|
+
type: "string",
|
|
347
|
+
description: "Name of the collection to count documents in",
|
|
348
|
+
},
|
|
349
|
+
query: {
|
|
350
|
+
type: "object",
|
|
351
|
+
description: "Optional: Query filter to select documents to count",
|
|
352
|
+
},
|
|
353
|
+
limit: {
|
|
354
|
+
type: "integer",
|
|
355
|
+
description: "Optional: Maximum number of documents to count",
|
|
356
|
+
},
|
|
357
|
+
skip: {
|
|
358
|
+
type: "integer",
|
|
359
|
+
description: "Optional: Number of documents to skip before counting",
|
|
360
|
+
},
|
|
361
|
+
hint: {
|
|
362
|
+
type: "object",
|
|
363
|
+
description: "Optional: Index hint to force query plan",
|
|
364
|
+
},
|
|
365
|
+
readConcern: {
|
|
366
|
+
type: "object",
|
|
367
|
+
description: "Optional: Read concern for the count operation",
|
|
368
|
+
},
|
|
369
|
+
maxTimeMS: {
|
|
370
|
+
type: "integer",
|
|
371
|
+
description: "Optional: Maximum time to allow the count to run",
|
|
372
|
+
},
|
|
373
|
+
collation: {
|
|
374
|
+
type: "object",
|
|
375
|
+
description: "Optional: Collation rules for string comparison",
|
|
376
|
+
},
|
|
377
|
+
},
|
|
378
|
+
required: ["collection"],
|
|
379
|
+
},
|
|
380
|
+
},
|
|
381
|
+
{
|
|
382
|
+
name: "listCollections",
|
|
383
|
+
description: "List all collections in the MongoDB database",
|
|
384
|
+
inputSchema: {
|
|
385
|
+
type: "object",
|
|
386
|
+
properties: {
|
|
387
|
+
nameOnly: {
|
|
388
|
+
type: "boolean",
|
|
389
|
+
description: "Optional: If true, returns only the collection names instead of full collection info"
|
|
390
|
+
},
|
|
391
|
+
filter: {
|
|
392
|
+
type: "object",
|
|
393
|
+
description: "Optional: Filter to apply to the collections"
|
|
394
|
+
}
|
|
395
|
+
}
|
|
173
396
|
}
|
|
174
|
-
}
|
|
175
|
-
]
|
|
397
|
+
},
|
|
398
|
+
],
|
|
176
399
|
};
|
|
177
400
|
});
|
|
178
401
|
/**
|
|
@@ -183,42 +406,66 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
|
|
|
183
406
|
const collection = db.collection(request.params.arguments?.collection);
|
|
184
407
|
switch (request.params.name) {
|
|
185
408
|
case "query": {
|
|
186
|
-
const { filter, projection, limit } = request.params.arguments || {};
|
|
409
|
+
const { filter, projection, limit, explain } = request.params.arguments || {};
|
|
187
410
|
// Validate collection name to prevent access to system collections
|
|
188
|
-
if (collection.collectionName.startsWith(
|
|
189
|
-
throw new Error(
|
|
411
|
+
if (collection.collectionName.startsWith("system.")) {
|
|
412
|
+
throw new Error("Access to system collections is not allowed");
|
|
190
413
|
}
|
|
191
414
|
// Validate and parse filter
|
|
192
415
|
let queryFilter = {};
|
|
193
416
|
if (filter) {
|
|
194
|
-
if (typeof filter ===
|
|
417
|
+
if (typeof filter === "string") {
|
|
195
418
|
try {
|
|
196
419
|
queryFilter = JSON.parse(filter);
|
|
197
420
|
}
|
|
198
421
|
catch (e) {
|
|
199
|
-
throw new Error(
|
|
422
|
+
throw new Error("Invalid filter format: must be a valid JSON object");
|
|
200
423
|
}
|
|
201
424
|
}
|
|
202
|
-
else if (typeof filter ===
|
|
425
|
+
else if (typeof filter === "object" &&
|
|
426
|
+
filter !== null &&
|
|
427
|
+
!Array.isArray(filter)) {
|
|
203
428
|
queryFilter = filter;
|
|
204
429
|
}
|
|
205
430
|
else {
|
|
206
|
-
throw new Error(
|
|
431
|
+
throw new Error("Query filter must be a plain object or ObjectId");
|
|
207
432
|
}
|
|
208
433
|
}
|
|
209
434
|
// Execute the find operation with error handling
|
|
210
435
|
try {
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
436
|
+
if (explain) {
|
|
437
|
+
// Use explain for query analysis
|
|
438
|
+
const explainResult = await collection
|
|
439
|
+
.find(queryFilter, {
|
|
440
|
+
projection,
|
|
441
|
+
limit: limit || 100,
|
|
442
|
+
})
|
|
443
|
+
.explain(explain);
|
|
444
|
+
return {
|
|
445
|
+
content: [
|
|
446
|
+
{
|
|
447
|
+
type: "text",
|
|
448
|
+
text: JSON.stringify(explainResult, null, 2),
|
|
449
|
+
},
|
|
450
|
+
],
|
|
451
|
+
};
|
|
452
|
+
}
|
|
453
|
+
else {
|
|
454
|
+
// Regular query execution
|
|
455
|
+
const cursor = collection.find(queryFilter, {
|
|
456
|
+
projection,
|
|
457
|
+
limit: limit || 100,
|
|
458
|
+
});
|
|
459
|
+
const results = await cursor.toArray();
|
|
460
|
+
return {
|
|
461
|
+
content: [
|
|
462
|
+
{
|
|
463
|
+
type: "text",
|
|
464
|
+
text: JSON.stringify(results, null, 2),
|
|
465
|
+
},
|
|
466
|
+
],
|
|
467
|
+
};
|
|
468
|
+
}
|
|
222
469
|
}
|
|
223
470
|
catch (error) {
|
|
224
471
|
if (error instanceof Error) {
|
|
@@ -228,33 +475,392 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
|
|
|
228
475
|
}
|
|
229
476
|
}
|
|
230
477
|
case "aggregate": {
|
|
231
|
-
const { pipeline } = request.params.arguments || {};
|
|
478
|
+
const { pipeline, explain } = request.params.arguments || {};
|
|
232
479
|
if (!Array.isArray(pipeline)) {
|
|
233
480
|
throw new Error("Pipeline must be an array");
|
|
234
481
|
}
|
|
235
482
|
// Validate collection name to prevent access to system collections
|
|
236
|
-
if (collection.collectionName.startsWith(
|
|
237
|
-
throw new Error(
|
|
483
|
+
if (collection.collectionName.startsWith("system.")) {
|
|
484
|
+
throw new Error("Access to system collections is not allowed");
|
|
238
485
|
}
|
|
239
486
|
// Execute the aggregation operation with error handling
|
|
240
487
|
try {
|
|
241
|
-
|
|
488
|
+
if (explain) {
|
|
489
|
+
// Use explain for aggregation analysis
|
|
490
|
+
const explainResult = await collection
|
|
491
|
+
.aggregate(pipeline, {
|
|
492
|
+
explain: {
|
|
493
|
+
verbosity: explain,
|
|
494
|
+
},
|
|
495
|
+
})
|
|
496
|
+
.toArray();
|
|
497
|
+
return {
|
|
498
|
+
content: [
|
|
499
|
+
{
|
|
500
|
+
type: "text",
|
|
501
|
+
text: JSON.stringify(explainResult, null, 2),
|
|
502
|
+
},
|
|
503
|
+
],
|
|
504
|
+
};
|
|
505
|
+
}
|
|
506
|
+
else {
|
|
507
|
+
// Regular aggregation execution
|
|
508
|
+
const results = await collection.aggregate(pipeline).toArray();
|
|
509
|
+
return {
|
|
510
|
+
content: [
|
|
511
|
+
{
|
|
512
|
+
type: "text",
|
|
513
|
+
text: JSON.stringify(results, null, 2),
|
|
514
|
+
},
|
|
515
|
+
],
|
|
516
|
+
};
|
|
517
|
+
}
|
|
518
|
+
}
|
|
519
|
+
catch (error) {
|
|
520
|
+
if (error instanceof Error) {
|
|
521
|
+
throw new Error(`Failed to aggregate collection ${collection.collectionName}: ${error.message}`);
|
|
522
|
+
}
|
|
523
|
+
throw new Error(`Failed to aggregate collection ${collection.collectionName}: Unknown error`);
|
|
524
|
+
}
|
|
525
|
+
}
|
|
526
|
+
case "update": {
|
|
527
|
+
const { filter, update, upsert, multi } = request.params.arguments || {};
|
|
528
|
+
// Validate collection name to prevent access to system collections
|
|
529
|
+
if (collection.collectionName.startsWith("system.")) {
|
|
530
|
+
throw new Error("Access to system collections is not allowed");
|
|
531
|
+
}
|
|
532
|
+
// Validate and parse filter
|
|
533
|
+
let queryFilter = {};
|
|
534
|
+
if (filter) {
|
|
535
|
+
if (typeof filter === "string") {
|
|
536
|
+
try {
|
|
537
|
+
queryFilter = JSON.parse(filter);
|
|
538
|
+
}
|
|
539
|
+
catch (e) {
|
|
540
|
+
throw new Error("Invalid filter format: must be a valid JSON object");
|
|
541
|
+
}
|
|
542
|
+
}
|
|
543
|
+
else if (typeof filter === "object" &&
|
|
544
|
+
filter !== null &&
|
|
545
|
+
!Array.isArray(filter)) {
|
|
546
|
+
queryFilter = filter;
|
|
547
|
+
}
|
|
548
|
+
else {
|
|
549
|
+
throw new Error("Query filter must be a plain object or ObjectId");
|
|
550
|
+
}
|
|
551
|
+
}
|
|
552
|
+
// Validate update operations
|
|
553
|
+
if (!update || typeof update !== "object" || Array.isArray(update)) {
|
|
554
|
+
throw new Error("Update must be a valid MongoDB update document");
|
|
555
|
+
}
|
|
556
|
+
// Check if update operations use valid operators
|
|
557
|
+
const validUpdateOperators = [
|
|
558
|
+
"$set",
|
|
559
|
+
"$unset",
|
|
560
|
+
"$inc",
|
|
561
|
+
"$push",
|
|
562
|
+
"$pull",
|
|
563
|
+
"$addToSet",
|
|
564
|
+
"$pop",
|
|
565
|
+
"$rename",
|
|
566
|
+
"$mul",
|
|
567
|
+
];
|
|
568
|
+
const hasValidOperator = Object.keys(update).some((key) => validUpdateOperators.includes(key));
|
|
569
|
+
if (!hasValidOperator) {
|
|
570
|
+
throw new Error("Update must include at least one valid update operator ($set, $unset, etc.)");
|
|
571
|
+
}
|
|
572
|
+
try {
|
|
573
|
+
const options = {
|
|
574
|
+
upsert: !!upsert,
|
|
575
|
+
multi: !!multi,
|
|
576
|
+
};
|
|
577
|
+
// Use updateOne or updateMany based on multi option
|
|
578
|
+
const updateMethod = options.multi ? "updateMany" : "updateOne";
|
|
579
|
+
const result = await collection[updateMethod](queryFilter, update, options);
|
|
242
580
|
return {
|
|
243
|
-
content: [
|
|
581
|
+
content: [
|
|
582
|
+
{
|
|
244
583
|
type: "text",
|
|
245
|
-
text: JSON.stringify(
|
|
246
|
-
|
|
584
|
+
text: JSON.stringify({
|
|
585
|
+
matchedCount: result.matchedCount,
|
|
586
|
+
modifiedCount: result.modifiedCount,
|
|
587
|
+
upsertedCount: result.upsertedCount,
|
|
588
|
+
upsertedId: result.upsertedId,
|
|
589
|
+
}, null, 2),
|
|
590
|
+
},
|
|
591
|
+
],
|
|
247
592
|
};
|
|
248
593
|
}
|
|
249
594
|
catch (error) {
|
|
250
595
|
if (error instanceof Error) {
|
|
251
|
-
throw new Error(`Failed to
|
|
596
|
+
throw new Error(`Failed to update collection ${collection.collectionName}: ${error.message}`);
|
|
252
597
|
}
|
|
253
|
-
throw new Error(`Failed to
|
|
598
|
+
throw new Error(`Failed to update collection ${collection.collectionName}: Unknown error`);
|
|
599
|
+
}
|
|
600
|
+
}
|
|
601
|
+
case "serverInfo": {
|
|
602
|
+
const { includeDebugInfo } = request.params.arguments || {};
|
|
603
|
+
try {
|
|
604
|
+
// Get basic server information using buildInfo command
|
|
605
|
+
const buildInfo = await db.command({ buildInfo: 1 });
|
|
606
|
+
// Get additional server status if debug info is requested
|
|
607
|
+
let serverStatus = null;
|
|
608
|
+
if (includeDebugInfo) {
|
|
609
|
+
serverStatus = await db.command({ serverStatus: 1 });
|
|
610
|
+
}
|
|
611
|
+
// Construct the response
|
|
612
|
+
const serverInfo = {
|
|
613
|
+
version: buildInfo.version,
|
|
614
|
+
gitVersion: buildInfo.gitVersion,
|
|
615
|
+
modules: buildInfo.modules,
|
|
616
|
+
allocator: buildInfo.allocator,
|
|
617
|
+
javascriptEngine: buildInfo.javascriptEngine,
|
|
618
|
+
sysInfo: buildInfo.sysInfo,
|
|
619
|
+
storageEngines: buildInfo.storageEngines,
|
|
620
|
+
debug: buildInfo.debug,
|
|
621
|
+
maxBsonObjectSize: buildInfo.maxBsonObjectSize,
|
|
622
|
+
openssl: buildInfo.openssl,
|
|
623
|
+
buildEnvironment: buildInfo.buildEnvironment,
|
|
624
|
+
bits: buildInfo.bits,
|
|
625
|
+
ok: buildInfo.ok,
|
|
626
|
+
status: {},
|
|
627
|
+
};
|
|
628
|
+
// Add server status information if requested
|
|
629
|
+
if (serverStatus) {
|
|
630
|
+
serverInfo.status = {
|
|
631
|
+
host: serverStatus.host,
|
|
632
|
+
version: serverStatus.version,
|
|
633
|
+
process: serverStatus.process,
|
|
634
|
+
pid: serverStatus.pid,
|
|
635
|
+
uptime: serverStatus.uptime,
|
|
636
|
+
uptimeMillis: serverStatus.uptimeMillis,
|
|
637
|
+
uptimeEstimate: serverStatus.uptimeEstimate,
|
|
638
|
+
localTime: serverStatus.localTime,
|
|
639
|
+
connections: serverStatus.connections,
|
|
640
|
+
network: serverStatus.network,
|
|
641
|
+
memory: serverStatus.mem,
|
|
642
|
+
storageEngine: serverStatus.storageEngine,
|
|
643
|
+
security: serverStatus.security,
|
|
644
|
+
};
|
|
645
|
+
}
|
|
646
|
+
return {
|
|
647
|
+
content: [
|
|
648
|
+
{
|
|
649
|
+
type: "text",
|
|
650
|
+
text: JSON.stringify(serverInfo, null, 2),
|
|
651
|
+
},
|
|
652
|
+
],
|
|
653
|
+
};
|
|
654
|
+
}
|
|
655
|
+
catch (error) {
|
|
656
|
+
if (error instanceof Error) {
|
|
657
|
+
throw new Error(`Failed to get server information: ${error.message}`);
|
|
658
|
+
}
|
|
659
|
+
throw new Error("Failed to get server information: Unknown error");
|
|
660
|
+
}
|
|
661
|
+
}
|
|
662
|
+
case "insert": {
|
|
663
|
+
const { documents, ordered, writeConcern, bypassDocumentValidation } = request.params.arguments || {};
|
|
664
|
+
// Validate collection name to prevent access to system collections
|
|
665
|
+
if (collection.collectionName.startsWith("system.")) {
|
|
666
|
+
throw new Error("Access to system collections is not allowed");
|
|
667
|
+
}
|
|
668
|
+
// Validate documents array
|
|
669
|
+
if (!Array.isArray(documents)) {
|
|
670
|
+
throw new Error("Documents must be an array");
|
|
671
|
+
}
|
|
672
|
+
if (documents.length === 0) {
|
|
673
|
+
throw new Error("Documents array cannot be empty");
|
|
674
|
+
}
|
|
675
|
+
if (!documents.every((doc) => doc && typeof doc === "object" && !Array.isArray(doc))) {
|
|
676
|
+
throw new Error("Each document must be a valid MongoDB document object");
|
|
677
|
+
}
|
|
678
|
+
try {
|
|
679
|
+
// Prepare insert options
|
|
680
|
+
const options = {
|
|
681
|
+
ordered: ordered !== false, // default to true if not specified
|
|
682
|
+
writeConcern,
|
|
683
|
+
bypassDocumentValidation,
|
|
684
|
+
};
|
|
685
|
+
// Use insertMany for consistency, it works for single documents too
|
|
686
|
+
const result = await collection.insertMany(documents, options);
|
|
687
|
+
return {
|
|
688
|
+
content: [
|
|
689
|
+
{
|
|
690
|
+
type: "text",
|
|
691
|
+
text: JSON.stringify({
|
|
692
|
+
acknowledged: result.acknowledged,
|
|
693
|
+
insertedCount: result.insertedCount,
|
|
694
|
+
insertedIds: result.insertedIds,
|
|
695
|
+
}, null, 2),
|
|
696
|
+
},
|
|
697
|
+
],
|
|
698
|
+
};
|
|
699
|
+
}
|
|
700
|
+
catch (error) {
|
|
701
|
+
if (error instanceof Error) {
|
|
702
|
+
// Handle bulk write errors specially to provide more detail
|
|
703
|
+
if (error.name === "BulkWriteError") {
|
|
704
|
+
const bulkError = error;
|
|
705
|
+
return {
|
|
706
|
+
content: [
|
|
707
|
+
{
|
|
708
|
+
type: "text",
|
|
709
|
+
text: JSON.stringify({
|
|
710
|
+
error: "Bulk write error occurred",
|
|
711
|
+
writeErrors: bulkError.writeErrors,
|
|
712
|
+
insertedCount: bulkError.result?.nInserted || 0,
|
|
713
|
+
failedCount: bulkError.result?.nFailedInserts || 0,
|
|
714
|
+
}, null, 2),
|
|
715
|
+
},
|
|
716
|
+
],
|
|
717
|
+
};
|
|
718
|
+
}
|
|
719
|
+
throw new Error(`Failed to insert documents into collection ${collection.collectionName}: ${error.message}`);
|
|
720
|
+
}
|
|
721
|
+
throw new Error(`Failed to insert documents into collection ${collection.collectionName}: Unknown error`);
|
|
722
|
+
}
|
|
723
|
+
}
|
|
724
|
+
case "createIndex": {
|
|
725
|
+
const { indexes, writeConcern, commitQuorum } = request.params.arguments || {};
|
|
726
|
+
// Validate collection name to prevent access to system collections
|
|
727
|
+
if (collection.collectionName.startsWith("system.")) {
|
|
728
|
+
throw new Error("Access to system collections is not allowed");
|
|
729
|
+
}
|
|
730
|
+
// Validate indexes array
|
|
731
|
+
if (!Array.isArray(indexes) || indexes.length === 0) {
|
|
732
|
+
throw new Error("Indexes must be a non-empty array");
|
|
733
|
+
}
|
|
734
|
+
// Validate writeConcern
|
|
735
|
+
if (writeConcern &&
|
|
736
|
+
(typeof writeConcern !== "object" || Array.isArray(writeConcern))) {
|
|
737
|
+
throw new Error("Write concern must be a valid MongoDB write concern object");
|
|
738
|
+
}
|
|
739
|
+
// Validate commitQuorum
|
|
740
|
+
if (commitQuorum &&
|
|
741
|
+
typeof commitQuorum !== "string" &&
|
|
742
|
+
typeof commitQuorum !== "number") {
|
|
743
|
+
throw new Error("Commit quorum must be a string or number");
|
|
744
|
+
}
|
|
745
|
+
try {
|
|
746
|
+
const result = await collection.createIndexes(indexes, {
|
|
747
|
+
writeConcern,
|
|
748
|
+
commitQuorum: typeof commitQuorum === "number" ? commitQuorum : undefined,
|
|
749
|
+
});
|
|
750
|
+
return {
|
|
751
|
+
content: [
|
|
752
|
+
{
|
|
753
|
+
type: "text",
|
|
754
|
+
text: JSON.stringify({
|
|
755
|
+
acknowledged: result.acknowledged,
|
|
756
|
+
createdIndexes: result.createdIndexes,
|
|
757
|
+
numIndexesBefore: result.numIndexesBefore,
|
|
758
|
+
numIndexesAfter: result.numIndexesAfter,
|
|
759
|
+
}, null, 2),
|
|
760
|
+
},
|
|
761
|
+
],
|
|
762
|
+
};
|
|
763
|
+
}
|
|
764
|
+
catch (error) {
|
|
765
|
+
if (error instanceof Error) {
|
|
766
|
+
throw new Error(`Failed to create indexes on collection ${collection.collectionName}: ${error.message}`);
|
|
767
|
+
}
|
|
768
|
+
throw new Error(`Failed to create indexes on collection ${collection.collectionName}: Unknown error`);
|
|
769
|
+
}
|
|
770
|
+
}
|
|
771
|
+
case "count": {
|
|
772
|
+
const args = request.params.arguments || {};
|
|
773
|
+
const { query } = args;
|
|
774
|
+
// Validate collection name to prevent access to system collections
|
|
775
|
+
if (collection.collectionName.startsWith("system.")) {
|
|
776
|
+
throw new Error("Access to system collections is not allowed");
|
|
777
|
+
}
|
|
778
|
+
// Validate and parse query
|
|
779
|
+
let countQuery = {};
|
|
780
|
+
if (query) {
|
|
781
|
+
if (typeof query === "string") {
|
|
782
|
+
try {
|
|
783
|
+
countQuery = JSON.parse(query);
|
|
784
|
+
}
|
|
785
|
+
catch (e) {
|
|
786
|
+
throw new Error("Invalid query format: must be a valid JSON object");
|
|
787
|
+
}
|
|
788
|
+
}
|
|
789
|
+
else if (typeof query === "object" &&
|
|
790
|
+
query !== null &&
|
|
791
|
+
!Array.isArray(query)) {
|
|
792
|
+
countQuery = query;
|
|
793
|
+
}
|
|
794
|
+
else {
|
|
795
|
+
throw new Error("Query must be a plain object");
|
|
796
|
+
}
|
|
797
|
+
}
|
|
798
|
+
try {
|
|
799
|
+
const options = {
|
|
800
|
+
limit: typeof args.limit === "number" ? args.limit : undefined,
|
|
801
|
+
skip: typeof args.skip === "number" ? args.skip : undefined,
|
|
802
|
+
hint: typeof args.hint === "object" && args.hint !== null
|
|
803
|
+
? args.hint
|
|
804
|
+
: undefined,
|
|
805
|
+
readConcern: typeof args.readConcern === "object" && args.readConcern !== null
|
|
806
|
+
? args.readConcern
|
|
807
|
+
: undefined,
|
|
808
|
+
maxTimeMS: typeof args.maxTimeMS === "number" ? args.maxTimeMS : undefined,
|
|
809
|
+
collation: typeof args.collation === "object" && args.collation !== null
|
|
810
|
+
? args.collation
|
|
811
|
+
: undefined,
|
|
812
|
+
};
|
|
813
|
+
// Remove undefined options
|
|
814
|
+
Object.keys(options).forEach((key) => options[key] === undefined && delete options[key]);
|
|
815
|
+
// Execute count operation
|
|
816
|
+
const count = await collection.countDocuments(countQuery, options);
|
|
817
|
+
return {
|
|
818
|
+
content: [
|
|
819
|
+
{
|
|
820
|
+
type: "text",
|
|
821
|
+
text: JSON.stringify({
|
|
822
|
+
count: count,
|
|
823
|
+
ok: 1,
|
|
824
|
+
}, null, 2),
|
|
825
|
+
},
|
|
826
|
+
],
|
|
827
|
+
};
|
|
828
|
+
}
|
|
829
|
+
catch (error) {
|
|
830
|
+
if (error instanceof Error) {
|
|
831
|
+
throw new Error(`Failed to count documents in collection ${collection.collectionName}: ${error.message}`);
|
|
832
|
+
}
|
|
833
|
+
throw new Error(`Failed to count documents in collection ${collection.collectionName}: Unknown error`);
|
|
834
|
+
}
|
|
835
|
+
}
|
|
836
|
+
case "listCollections": {
|
|
837
|
+
const { nameOnly, filter } = request.params.arguments || {};
|
|
838
|
+
try {
|
|
839
|
+
// Get the list of collections
|
|
840
|
+
const options = filter ? { filter } : {};
|
|
841
|
+
const collections = await db.listCollections(options).toArray();
|
|
842
|
+
// If nameOnly is true, return only the collection names
|
|
843
|
+
const result = nameOnly
|
|
844
|
+
? collections.map((collection) => collection.name)
|
|
845
|
+
: collections;
|
|
846
|
+
return {
|
|
847
|
+
content: [
|
|
848
|
+
{
|
|
849
|
+
type: "text",
|
|
850
|
+
text: JSON.stringify(result, null, 2),
|
|
851
|
+
},
|
|
852
|
+
],
|
|
853
|
+
};
|
|
854
|
+
}
|
|
855
|
+
catch (error) {
|
|
856
|
+
if (error instanceof Error) {
|
|
857
|
+
throw new Error(`Failed to list collections: ${error.message}`);
|
|
858
|
+
}
|
|
859
|
+
throw new Error("Failed to list collections: Unknown error");
|
|
254
860
|
}
|
|
255
861
|
}
|
|
256
862
|
default:
|
|
257
|
-
throw new Error(
|
|
863
|
+
throw new Error(`Unknown tool: ${request.params.name}`);
|
|
258
864
|
}
|
|
259
865
|
});
|
|
260
866
|
/**
|
|
@@ -271,11 +877,11 @@ server.setRequestHandler(ListPromptsRequestSchema, async () => {
|
|
|
271
877
|
{
|
|
272
878
|
name: "collection",
|
|
273
879
|
description: "Name of the collection to analyze",
|
|
274
|
-
required: true
|
|
275
|
-
}
|
|
276
|
-
]
|
|
277
|
-
}
|
|
278
|
-
]
|
|
880
|
+
required: true,
|
|
881
|
+
},
|
|
882
|
+
],
|
|
883
|
+
},
|
|
884
|
+
],
|
|
279
885
|
};
|
|
280
886
|
});
|
|
281
887
|
/**
|
|
@@ -293,22 +899,22 @@ server.setRequestHandler(GetPromptRequestSchema, async (request) => {
|
|
|
293
899
|
try {
|
|
294
900
|
const collection = db.collection(collectionName);
|
|
295
901
|
// Validate collection name to prevent access to system collections
|
|
296
|
-
if (collection.collectionName.startsWith(
|
|
297
|
-
throw new Error(
|
|
902
|
+
if (collection.collectionName.startsWith("system.")) {
|
|
903
|
+
throw new Error("Access to system collections is not allowed");
|
|
298
904
|
}
|
|
299
905
|
const schema = await collection.findOne({});
|
|
300
906
|
// Get basic collection stats - just count in API v1
|
|
301
|
-
const stats = await collection
|
|
907
|
+
const stats = await collection
|
|
908
|
+
.aggregate([
|
|
302
909
|
{
|
|
303
910
|
$collStats: {
|
|
304
|
-
count: {}
|
|
305
|
-
}
|
|
306
|
-
}
|
|
307
|
-
])
|
|
308
|
-
// Also get a sample of documents to show data distribution
|
|
309
|
-
const sampleDocs = await collection.find({})
|
|
310
|
-
.limit(5)
|
|
911
|
+
count: {},
|
|
912
|
+
},
|
|
913
|
+
},
|
|
914
|
+
])
|
|
311
915
|
.toArray();
|
|
916
|
+
// Also get a sample of documents to show data distribution
|
|
917
|
+
const sampleDocs = await collection.find({}).limit(5).toArray();
|
|
312
918
|
return {
|
|
313
919
|
messages: [
|
|
314
920
|
{
|
|
@@ -322,20 +928,20 @@ Schema:
|
|
|
322
928
|
${JSON.stringify(schema, null, 2)}
|
|
323
929
|
|
|
324
930
|
Stats:
|
|
325
|
-
Document count: ${stats[0]?.count ||
|
|
931
|
+
Document count: ${stats[0]?.count || "unknown"}
|
|
326
932
|
|
|
327
933
|
Sample documents:
|
|
328
|
-
${JSON.stringify(sampleDocs, null, 2)}
|
|
329
|
-
}
|
|
934
|
+
${JSON.stringify(sampleDocs, null, 2)}`,
|
|
935
|
+
},
|
|
330
936
|
},
|
|
331
937
|
{
|
|
332
938
|
role: "user",
|
|
333
939
|
content: {
|
|
334
940
|
type: "text",
|
|
335
|
-
text: "Provide insights about the collection's structure, data types, and basic statistics."
|
|
336
|
-
}
|
|
337
|
-
}
|
|
338
|
-
]
|
|
941
|
+
text: "Provide insights about the collection's structure, data types, and basic statistics.",
|
|
942
|
+
},
|
|
943
|
+
},
|
|
944
|
+
],
|
|
339
945
|
};
|
|
340
946
|
}
|
|
341
947
|
catch (error) {
|
|
@@ -379,9 +985,9 @@ Example queries:
|
|
|
379
985
|
3. Find documents with existing email:
|
|
380
986
|
{ "email": { "$exists": true } }
|
|
381
987
|
|
|
382
|
-
Use these patterns to construct MongoDB queries
|
|
383
|
-
}
|
|
384
|
-
]
|
|
988
|
+
Use these patterns to construct MongoDB queries.`,
|
|
989
|
+
},
|
|
990
|
+
],
|
|
385
991
|
};
|
|
386
992
|
});
|
|
387
993
|
/**
|
package/build/types.js
CHANGED
|
@@ -4,11 +4,13 @@ import { CallToolRequestSchema, ToolSchema, ResourceSchema, TextResourceContents
|
|
|
4
4
|
export const MongoCollectionSchema = ResourceSchema.extend({
|
|
5
5
|
collectionName: z.string(),
|
|
6
6
|
databaseName: z.string(),
|
|
7
|
-
indexes: z
|
|
7
|
+
indexes: z
|
|
8
|
+
.array(z.object({
|
|
8
9
|
name: z.string(),
|
|
9
10
|
keys: z.record(z.union([z.number(), z.string()])),
|
|
10
11
|
unique: z.optional(z.boolean()),
|
|
11
|
-
}))
|
|
12
|
+
}))
|
|
13
|
+
.optional(),
|
|
12
14
|
});
|
|
13
15
|
// MongoDB Document Schema
|
|
14
16
|
export const MongoDocumentSchema = TextResourceContentsSchema.extend({
|
|
@@ -40,14 +42,16 @@ export const MongoSortSchema = z.record(z.union([z.literal(1), z.literal(-1)]));
|
|
|
40
42
|
export const MongoQueryToolSchema = ToolSchema.extend({
|
|
41
43
|
inputSchema: z.object({
|
|
42
44
|
type: z.literal("object"),
|
|
43
|
-
properties: z
|
|
45
|
+
properties: z
|
|
46
|
+
.object({
|
|
44
47
|
collection: z.string(),
|
|
45
48
|
filter: z.record(z.union([z.unknown(), MongoQueryOperatorSchema])),
|
|
46
49
|
projection: z.record(z.union([z.literal(0), z.literal(1)])).optional(),
|
|
47
50
|
sort: MongoSortSchema.optional(),
|
|
48
51
|
limit: z.number().int().positive().optional(),
|
|
49
52
|
skip: z.number().int().nonnegative().optional(),
|
|
50
|
-
})
|
|
53
|
+
})
|
|
54
|
+
.passthrough(),
|
|
51
55
|
required: z.array(z.literal("collection")),
|
|
52
56
|
}),
|
|
53
57
|
});
|
|
@@ -55,10 +59,12 @@ export const MongoQueryToolSchema = ToolSchema.extend({
|
|
|
55
59
|
export const MongoAggregateToolSchema = ToolSchema.extend({
|
|
56
60
|
inputSchema: z.object({
|
|
57
61
|
type: z.literal("object"),
|
|
58
|
-
properties: z
|
|
62
|
+
properties: z
|
|
63
|
+
.object({
|
|
59
64
|
collection: z.string(),
|
|
60
65
|
pipeline: z.array(z.record(z.unknown())),
|
|
61
|
-
})
|
|
66
|
+
})
|
|
67
|
+
.passthrough(),
|
|
62
68
|
required: z.array(z.literal("collection")),
|
|
63
69
|
}),
|
|
64
70
|
});
|
|
@@ -66,10 +72,14 @@ export const MongoAggregateToolSchema = ToolSchema.extend({
|
|
|
66
72
|
export const MongoCountToolSchema = ToolSchema.extend({
|
|
67
73
|
inputSchema: z.object({
|
|
68
74
|
type: z.literal("object"),
|
|
69
|
-
properties: z
|
|
75
|
+
properties: z
|
|
76
|
+
.object({
|
|
70
77
|
collection: z.string(),
|
|
71
|
-
filter: z
|
|
72
|
-
|
|
78
|
+
filter: z
|
|
79
|
+
.record(z.union([z.unknown(), MongoQueryOperatorSchema]))
|
|
80
|
+
.optional(),
|
|
81
|
+
})
|
|
82
|
+
.passthrough(),
|
|
73
83
|
required: z.array(z.literal("collection")),
|
|
74
84
|
}),
|
|
75
85
|
});
|
|
@@ -77,18 +87,32 @@ export const MongoCountToolSchema = ToolSchema.extend({
|
|
|
77
87
|
export const MongoDistinctToolSchema = ToolSchema.extend({
|
|
78
88
|
inputSchema: z.object({
|
|
79
89
|
type: z.literal("object"),
|
|
80
|
-
properties: z
|
|
90
|
+
properties: z
|
|
91
|
+
.object({
|
|
81
92
|
collection: z.string(),
|
|
82
93
|
field: z.string(),
|
|
83
|
-
filter: z
|
|
84
|
-
|
|
94
|
+
filter: z
|
|
95
|
+
.record(z.union([z.unknown(), MongoQueryOperatorSchema]))
|
|
96
|
+
.optional(),
|
|
97
|
+
})
|
|
98
|
+
.passthrough(),
|
|
85
99
|
required: z.array(z.literal("collection")),
|
|
86
100
|
}),
|
|
87
101
|
});
|
|
88
102
|
// MongoDB Call Tool Request Schema
|
|
89
103
|
export const MongoCallToolRequestSchema = CallToolRequestSchema.extend({
|
|
90
104
|
params: z.object({
|
|
91
|
-
name: z.enum([
|
|
105
|
+
name: z.enum([
|
|
106
|
+
"query",
|
|
107
|
+
"aggregate",
|
|
108
|
+
"update",
|
|
109
|
+
"serverInfo",
|
|
110
|
+
"insert",
|
|
111
|
+
"createIndex",
|
|
112
|
+
"count",
|
|
113
|
+
"distinct",
|
|
114
|
+
"listCollections",
|
|
115
|
+
]),
|
|
92
116
|
arguments: z.union([
|
|
93
117
|
MongoQueryToolSchema.shape.inputSchema,
|
|
94
118
|
MongoAggregateToolSchema.shape.inputSchema,
|
|
@@ -136,8 +160,10 @@ export const MongoFieldSchemaSchema = z.object({
|
|
|
136
160
|
export const MongoCollectionSchemaSchema = z.object({
|
|
137
161
|
name: z.string(),
|
|
138
162
|
fields: z.record(MongoFieldSchemaSchema),
|
|
139
|
-
options: z
|
|
163
|
+
options: z
|
|
164
|
+
.object({
|
|
140
165
|
timestamps: z.boolean().optional(),
|
|
141
166
|
strict: z.boolean().optional(),
|
|
142
|
-
})
|
|
167
|
+
})
|
|
168
|
+
.optional(),
|
|
143
169
|
});
|
package/package.json
CHANGED
|
@@ -1,52 +1,52 @@
|
|
|
1
1
|
{
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
}
|
|
2
|
+
"name": "mcp-mongo-server",
|
|
3
|
+
"version": "1.1.0",
|
|
4
|
+
"description": "A Model Context Protocol server for MongoDB connections",
|
|
5
|
+
"private": false,
|
|
6
|
+
"type": "module",
|
|
7
|
+
"bin": {
|
|
8
|
+
"mongodb": "./build/index.js"
|
|
9
|
+
},
|
|
10
|
+
"files": [
|
|
11
|
+
"build"
|
|
12
|
+
],
|
|
13
|
+
"scripts": {
|
|
14
|
+
"build": "tsc && node -e \"require('fs').chmodSync('build/index.js', '755')\"",
|
|
15
|
+
"prepare": "npm run build",
|
|
16
|
+
"watch": "tsc --watch",
|
|
17
|
+
"inspector": "npx @modelcontextprotocol/inspector build/index.js"
|
|
18
|
+
},
|
|
19
|
+
"dependencies": {
|
|
20
|
+
"@modelcontextprotocol/sdk": "0.6.0",
|
|
21
|
+
"@types/mongodb": "^4.0.6",
|
|
22
|
+
"mongodb": "^6.11.0",
|
|
23
|
+
"zod": "^3.23.8"
|
|
24
|
+
},
|
|
25
|
+
"devDependencies": {
|
|
26
|
+
"@types/node": "^20.11.24",
|
|
27
|
+
"typescript": "^5.3.3"
|
|
28
|
+
},
|
|
29
|
+
"publishConfig": {
|
|
30
|
+
"access": "public"
|
|
31
|
+
},
|
|
32
|
+
"keywords": [
|
|
33
|
+
"claude",
|
|
34
|
+
"claude-desktop",
|
|
35
|
+
"mcp",
|
|
36
|
+
"mongo",
|
|
37
|
+
"mongodb",
|
|
38
|
+
"model-context-protocol",
|
|
39
|
+
"llm",
|
|
40
|
+
"mcp-server",
|
|
41
|
+
"mcp-protocol",
|
|
42
|
+
"mcp-connector",
|
|
43
|
+
"mcp-connector-mongodb"
|
|
44
|
+
],
|
|
45
|
+
"author": "Muhammed Kilic <kiliczsh>",
|
|
46
|
+
"license": "MIT",
|
|
47
|
+
"repository": {
|
|
48
|
+
"type": "git",
|
|
49
|
+
"url": "https://github.com/kiliczsh/mcp-mongo-server"
|
|
50
|
+
},
|
|
51
|
+
"homepage": "https://github.com/kiliczsh/mcp-mongo-server"
|
|
52
|
+
}
|