mcp-mongo-server 1.2.0 → 2.0.2

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.
@@ -1,112 +0,0 @@
1
- /**
2
- * Handles completion requests from the Model Context Protocol
3
- * @param options The request options
4
- * @returns Completion result with matching values
5
- */
6
- export async function handleCompletionRequest({ request, client, db, isReadOnlyMode, }) {
7
- const { ref, argument } = request.params;
8
- // Handle completions for prompts
9
- if (ref.type === "ref/prompt") {
10
- return handlePromptCompletion(client, db, isReadOnlyMode, ref.name, argument);
11
- }
12
- // Handle completions for resources
13
- if (ref.type === "ref/resource") {
14
- return handleResourceCompletion(client, db, isReadOnlyMode, ref.uri, argument);
15
- }
16
- // Default empty response if reference type is not supported
17
- return emptyCompletionResult();
18
- }
19
- /**
20
- * Handle completion requests for prompts
21
- * @param client MongoDB client
22
- * @param db MongoDB database
23
- * @param isReadOnlyMode Whether the database is in read-only mode
24
- * @param promptName Name of the prompt
25
- * @param argument Argument being completed
26
- * @returns Completion result
27
- */
28
- async function handlePromptCompletion(client, db, isReadOnlyMode, promptName, argument) {
29
- if (!promptName) {
30
- return emptyCompletionResult();
31
- }
32
- // Handle collection name completions
33
- if (argument.name === "collection") {
34
- return await completeCollectionNames(argument.value, db, isReadOnlyMode);
35
- }
36
- // Add other prompt completions here as needed
37
- return emptyCompletionResult();
38
- }
39
- /**
40
- * Handle completion requests for resources
41
- * @param client MongoDB client
42
- * @param db MongoDB database
43
- * @param isReadOnlyMode Whether the database is in read-only mode
44
- * @param uri Resource URI
45
- * @param argument Argument being completed
46
- * @returns Completion result
47
- */
48
- async function handleResourceCompletion(client, db, isReadOnlyMode, promptName, argument) {
49
- if (!promptName) {
50
- return emptyCompletionResult();
51
- }
52
- // Handle collection name completions
53
- if (argument.name === "collection") {
54
- return await completeCollectionNames(argument.value, db, isReadOnlyMode);
55
- }
56
- // Add other prompt completions here as needed
57
- return emptyCompletionResult();
58
- }
59
- /**
60
- * Get collection name completions for a partial value
61
- * @param partialValue Partial collection name
62
- * @param db MongoDB database
63
- * @param isReadOnlyMode Whether the database is in read-only mode
64
- * @returns Completion result with matching collection names
65
- */
66
- async function completeCollectionNames(partialValue, db, isReadOnlyMode) {
67
- try {
68
- console.warn(`Completing collection names with partial value: ${partialValue}`);
69
- // Get list of collections
70
- const collections = await db.listCollections().toArray();
71
- // Filter collections by partial value (case insensitive)
72
- const matchingCollections = collections
73
- .map((c) => c.name)
74
- .filter((name) =>
75
- // Filter out system collections
76
- !name.startsWith("system.") &&
77
- // Match partial value
78
- name.toLowerCase().includes(partialValue.toLowerCase()))
79
- // Sort alphabetically
80
- .sort();
81
- console.warn(`Found ${matchingCollections.length} matching collections`);
82
- // Limit to 100 items as per spec
83
- const MAX_ITEMS = 100;
84
- const limitedResults = matchingCollections.slice(0, MAX_ITEMS);
85
- const hasMore = matchingCollections.length > MAX_ITEMS;
86
- return {
87
- completion: {
88
- values: limitedResults,
89
- total: matchingCollections.length,
90
- hasMore,
91
- },
92
- };
93
- }
94
- catch (error) {
95
- console.error("Error completing collection names:", error);
96
- // Return empty result on error
97
- return emptyCompletionResult();
98
- }
99
- }
100
- /**
101
- * Create an empty completion result
102
- * @returns Empty completion result object
103
- */
104
- function emptyCompletionResult() {
105
- return {
106
- completion: {
107
- values: [],
108
- total: 0,
109
- hasMore: false,
110
- },
111
- };
112
- }
@@ -1,20 +0,0 @@
1
- export async function handlePingRequest({ request, client, db, isReadOnlyMode, }) {
2
- try {
3
- // Check MongoDB connection
4
- if (!client) {
5
- throw new Error("MongoDB connection is not available");
6
- }
7
- // Ping MongoDB to verify connection
8
- const pong = await db.command({ ping: 1 });
9
- if (pong.ok !== 1) {
10
- throw new Error(`MongoDB ping failed: ${pong.errmsg}`);
11
- }
12
- return {};
13
- }
14
- catch (error) {
15
- if (error instanceof Error) {
16
- throw new Error(`MongoDB ping failed: ${error.message}`);
17
- }
18
- throw new Error("MongoDB ping failed: Unknown error");
19
- }
20
- }
@@ -1,71 +0,0 @@
1
- export async function handleListPromptsRequest({ request, client, db, isReadOnlyMode, }) {
2
- return {
3
- prompts: [
4
- {
5
- name: "analyze_collection",
6
- description: "Analyze a MongoDB collection structure and contents",
7
- arguments: [
8
- {
9
- name: "collection",
10
- description: "Name of the collection to analyze",
11
- required: true,
12
- },
13
- ],
14
- },
15
- ],
16
- };
17
- }
18
- export async function handleGetPromptRequest({ request, client, db, isReadOnlyMode, }) {
19
- const { name, arguments: args = {} } = request.params;
20
- if (name !== "analyze_collection") {
21
- throw new Error("Unknown prompt");
22
- }
23
- const collectionName = args.collection;
24
- if (!collectionName) {
25
- throw new Error("Collection name is required");
26
- }
27
- try {
28
- const collection = db.collection(collectionName);
29
- if (collection.collectionName.startsWith("system.")) {
30
- throw new Error("Access to system collections is not allowed");
31
- }
32
- const schemaSample = await collection.findOne({});
33
- const stats = await collection
34
- .aggregate([{ $collStats: { count: {} } }])
35
- .toArray();
36
- const sampleDocs = await collection.find({}).limit(5).toArray();
37
- const documentCount = stats[0]?.count ?? "unknown";
38
- return {
39
- messages: [
40
- {
41
- role: "user",
42
- content: {
43
- type: "text",
44
- text: `Please analyze the following MongoDB collection:
45
- Collection: ${collectionName}
46
-
47
- Schema:
48
- ${JSON.stringify(schemaSample, null, 2)}
49
-
50
- Stats:
51
- Document count: ${documentCount}
52
-
53
- Sample documents:
54
- ${JSON.stringify(sampleDocs, null, 2)}`,
55
- },
56
- },
57
- {
58
- role: "user",
59
- content: {
60
- type: "text",
61
- text: "Provide insights about the collection's structure, data types, and basic statistics.",
62
- },
63
- },
64
- ],
65
- };
66
- }
67
- catch (error) {
68
- const msg = error instanceof Error ? error.message : "Unknown error";
69
- throw new Error(`Failed to analyze collection ${collectionName}: ${msg}`);
70
- }
71
- }
@@ -1,62 +0,0 @@
1
- export async function handleReadResourceRequest({ request, client, db, isReadOnlyMode, }) {
2
- const url = new URL(request.params.uri);
3
- const collectionName = url.pathname.replace(/^\//, "");
4
- try {
5
- const collection = db.collection(collectionName);
6
- const sample = await collection.findOne({});
7
- const indexes = await collection.indexes();
8
- const schema = sample
9
- ? {
10
- type: "collection",
11
- name: collectionName,
12
- fields: Object.entries(sample).map(([key, value]) => ({
13
- name: key,
14
- type: typeof value,
15
- })),
16
- indexes: indexes.map((idx) => ({
17
- name: idx.name,
18
- keys: idx.key,
19
- })),
20
- }
21
- : {
22
- type: "collection",
23
- name: collectionName,
24
- fields: [],
25
- indexes: [],
26
- };
27
- return {
28
- contents: [
29
- {
30
- uri: request.params.uri,
31
- mimeType: "application/json",
32
- text: JSON.stringify(schema, null, 2),
33
- },
34
- ],
35
- };
36
- }
37
- catch (error) {
38
- if (error instanceof Error) {
39
- throw new Error(`Failed to read collection ${collectionName}: ${error.message}`);
40
- }
41
- throw new Error(`Failed to read collection ${collectionName}: Unknown error`);
42
- }
43
- }
44
- export async function handleListResourcesRequest({ request, client, db, isReadOnlyMode, }) {
45
- try {
46
- const collections = await db.listCollections().toArray();
47
- return {
48
- resources: collections.map((collection) => ({
49
- uri: `mongodb:///${collection.name}`,
50
- mimeType: "application/json",
51
- name: collection.name,
52
- description: `MongoDB collection: ${collection.name}`,
53
- })),
54
- };
55
- }
56
- catch (error) {
57
- if (error instanceof Error) {
58
- throw new Error(`Failed to list collections: ${error.message}`);
59
- }
60
- throw new Error("Failed to list collections: Unknown error");
61
- }
62
- }
@@ -1,33 +0,0 @@
1
- export async function handleListResourceTemplatesRequest({ request, client, db, isReadOnlyMode, }) {
2
- return {
3
- resourceTemplates: [
4
- {
5
- name: "mongodb_query",
6
- description: "Template for constructing MongoDB queries",
7
- uriTemplate: "mongodb:///{collection}",
8
- text: `To query MongoDB collections, you can use these operators:
9
-
10
- Filter operators:
11
- - $eq: Matches values equal to a specified value
12
- - $gt/$gte: Matches values greater than (or equal to) a specified value
13
- - $lt/$lte: Matches values less than (or equal to) a specified value
14
- - $in: Matches any of the values in an array
15
- - $nin: Matches none of the values in an array
16
- - $ne: Matches values not equal to a specified value
17
- - $exists: Matches documents that have the specified field
18
-
19
- Example queries:
20
- 1. Find documents where age > 21:
21
- { "age": { "$gt": 21 } }
22
-
23
- 2. Find documents with specific status:
24
- { "status": { "$in": ["active", "pending"] } }
25
-
26
- 3. Find documents with existing email:
27
- { "email": { "$exists": true } }
28
-
29
- Use these patterns to construct MongoDB queries.`,
30
- },
31
- ],
32
- };
33
- }
@@ -1,278 +0,0 @@
1
- export async function handleListToolsRequest({ request, client, db, isReadOnlyMode, }) {
2
- return {
3
- tools: [
4
- {
5
- name: "query",
6
- description: "Execute a MongoDB query with optional execution plan analysis",
7
- inputSchema: {
8
- type: "object",
9
- properties: {
10
- collection: {
11
- type: "string",
12
- description: "Name of the collection to query",
13
- },
14
- filter: { type: "object", description: "MongoDB query filter" },
15
- projection: {
16
- type: "object",
17
- description: "Fields to include/exclude",
18
- },
19
- limit: {
20
- type: "number",
21
- description: "Maximum number of documents to return",
22
- default: 10,
23
- },
24
- explain: {
25
- type: "string",
26
- description: "Optional: Get query execution information",
27
- enum: ["queryPlanner", "executionStats", "allPlansExecution"],
28
- },
29
- objectIdMode: {
30
- type: "string",
31
- description: "Control how 24-character hex strings are handled",
32
- enum: ["auto", "none", "force"],
33
- default: "auto",
34
- },
35
- },
36
- required: ["collection"],
37
- },
38
- },
39
- {
40
- name: "aggregate",
41
- description: "Execute a MongoDB aggregation pipeline with optional execution plan analysis",
42
- inputSchema: {
43
- type: "object",
44
- properties: {
45
- collection: {
46
- type: "string",
47
- description: "Name of the collection to aggregate",
48
- },
49
- pipeline: {
50
- type: "array",
51
- description: "Aggregation pipeline stages",
52
- items: {
53
- type: "object",
54
- },
55
- },
56
- explain: {
57
- type: "string",
58
- description: "Optional: Get aggregation execution information (queryPlanner, executionStats, or allPlansExecution)",
59
- enum: ["queryPlanner", "executionStats", "allPlansExecution"],
60
- },
61
- objectIdMode: {
62
- type: "string",
63
- description: "Control how 24-character hex strings are handled",
64
- enum: ["auto", "none", "force"],
65
- default: "auto",
66
- },
67
- },
68
- required: ["collection", "pipeline"],
69
- },
70
- },
71
- {
72
- name: "update",
73
- description: "Update documents in a MongoDB collection",
74
- inputSchema: {
75
- type: "object",
76
- properties: {
77
- collection: {
78
- type: "string",
79
- description: "Name of the collection to update",
80
- },
81
- filter: {
82
- type: "object",
83
- description: "Filter to select documents to update",
84
- },
85
- update: {
86
- type: "object",
87
- description: "Update operations to apply ($set, $unset, $inc, etc.)",
88
- },
89
- upsert: {
90
- type: "boolean",
91
- description: "Create a new document if no documents match the filter",
92
- },
93
- multi: {
94
- type: "boolean",
95
- description: "Update multiple documents that match the filter",
96
- },
97
- objectIdMode: {
98
- type: "string",
99
- description: "Control how 24-character hex strings are handled",
100
- enum: ["auto", "none", "force"],
101
- default: "auto",
102
- },
103
- },
104
- required: ["collection", "filter", "update"],
105
- },
106
- },
107
- {
108
- name: "serverInfo",
109
- description: "Get MongoDB server information including version, storage engine, and other details",
110
- inputSchema: {
111
- type: "object",
112
- properties: {
113
- includeDebugInfo: {
114
- type: "boolean",
115
- description: "Include additional debug information about the server",
116
- },
117
- },
118
- },
119
- },
120
- {
121
- name: "insert",
122
- description: "Insert one or more documents into a MongoDB collection",
123
- inputSchema: {
124
- type: "object",
125
- properties: {
126
- collection: {
127
- type: "string",
128
- description: "Name of the collection to insert into",
129
- },
130
- documents: {
131
- type: "array",
132
- description: "Array of documents to insert",
133
- items: { type: "object" },
134
- },
135
- ordered: {
136
- type: "boolean",
137
- description: "If true, perform ordered insert. If false, insert unordered",
138
- },
139
- writeConcern: {
140
- type: "object",
141
- description: "Write concern for the insert operation",
142
- },
143
- bypassDocumentValidation: {
144
- type: "boolean",
145
- description: "Allow insert to bypass schema validation",
146
- },
147
- objectIdMode: {
148
- type: "string",
149
- description: "Control how 24-character hex strings are handled",
150
- enum: ["auto", "none", "force"],
151
- default: "auto",
152
- },
153
- },
154
- required: ["collection", "documents"],
155
- },
156
- },
157
- {
158
- name: "createIndex",
159
- description: "Create one or more indexes on a MongoDB collection",
160
- inputSchema: {
161
- type: "object",
162
- properties: {
163
- collection: {
164
- type: "string",
165
- description: "Name of the collection to create indexes on",
166
- },
167
- indexes: {
168
- type: "array",
169
- description: "Array of index specifications",
170
- items: {
171
- type: "object",
172
- properties: {
173
- key: {
174
- type: "object",
175
- description: "Index key pattern, e.g. { field: 1 }",
176
- },
177
- name: {
178
- type: "string",
179
- description: "Optional: Name of the index",
180
- },
181
- unique: {
182
- type: "boolean",
183
- description: "Optional: Creates a unique index",
184
- },
185
- sparse: {
186
- type: "boolean",
187
- description: "Optional: Creates a sparse index",
188
- },
189
- background: {
190
- type: "boolean",
191
- description: "Optional: Builds index in background",
192
- },
193
- expireAfterSeconds: {
194
- type: "number",
195
- description: "TTL in seconds for documents",
196
- },
197
- partialFilterExpression: {
198
- type: "object",
199
- description: "Filter expression for partial indexes",
200
- },
201
- },
202
- required: ["key"],
203
- },
204
- },
205
- writeConcern: {
206
- type: "object",
207
- description: "Write concern for index creation",
208
- },
209
- commitQuorum: {
210
- type: "string",
211
- description: "Number of members required to create the index",
212
- },
213
- objectIdMode: {
214
- type: "string",
215
- description: "Control how 24-character hex strings are handled",
216
- enum: ["auto", "none", "force"],
217
- default: "auto",
218
- },
219
- },
220
- required: ["collection", "indexes"],
221
- },
222
- },
223
- {
224
- name: "count",
225
- description: "Count documents in a collection matching a query",
226
- inputSchema: {
227
- type: "object",
228
- properties: {
229
- collection: { type: "string", description: "Collection name" },
230
- query: { type: "object", description: "Query filter to count" },
231
- limit: { type: "integer", description: "Max documents to count" },
232
- skip: {
233
- type: "integer",
234
- description: "Docs to skip before counting",
235
- },
236
- hint: { type: "object", description: "Index hint" },
237
- readConcern: { type: "object", description: "Read concern option" },
238
- maxTimeMS: { type: "integer", description: "Max execution time" },
239
- collation: {
240
- type: "object",
241
- description: "Collation rules for comparison",
242
- },
243
- objectIdMode: {
244
- type: "string",
245
- description: "Control how 24-character hex strings are handled",
246
- enum: ["auto", "none", "force"],
247
- default: "auto",
248
- },
249
- },
250
- required: ["collection"],
251
- },
252
- },
253
- {
254
- name: "listCollections",
255
- description: "List all collections in the MongoDB database",
256
- inputSchema: {
257
- type: "object",
258
- properties: {
259
- nameOnly: {
260
- type: "boolean",
261
- description: "If true, return only collection names",
262
- },
263
- filter: {
264
- type: "object",
265
- description: "Filter for collections",
266
- },
267
- objectIdMode: {
268
- type: "string",
269
- description: "Control how 24-character hex strings are handled",
270
- enum: ["auto", "none", "force"],
271
- default: "auto",
272
- },
273
- },
274
- },
275
- },
276
- ],
277
- };
278
- }
package/build/server.js DELETED
@@ -1,65 +0,0 @@
1
- import { Server } from "@modelcontextprotocol/sdk/server/index.js";
2
- import { CallToolRequestSchema, ListResourcesRequestSchema, ListToolsRequestSchema, ReadResourceRequestSchema, ListPromptsRequestSchema, GetPromptRequestSchema, ListResourceTemplatesRequestSchema, PingRequestSchema, CompleteRequestSchema, } from "@modelcontextprotocol/sdk/types.js";
3
- import { handleReadResourceRequest, handleListResourcesRequest, } from "./schemas/resource.js";
4
- import { handlePingRequest } from "./schemas/ping.js";
5
- import { handleListToolsRequest } from "./schemas/tools.js";
6
- import { handleListPromptsRequest } from "./schemas/prompts.js";
7
- import { handleCallToolRequest } from "./schemas/call.js";
8
- import { handleListResourceTemplatesRequest } from "./schemas/templates.js";
9
- import { handleGetPromptRequest } from "./schemas/prompts.js";
10
- import { handleCompletionRequest } from "./schemas/completion.js";
11
- /**
12
- * Create an MCP server with capabilities for resources (to list/read collections),
13
- * tools (to query data), and prompts (to analyze collections).
14
- */
15
- export function createServer(client, db, isReadOnlyMode = false, options = {}) {
16
- const server = new Server({
17
- name: "mongodb",
18
- version: "1.2.0",
19
- ...options,
20
- }, {
21
- capabilities: {
22
- resources: {},
23
- tools: {},
24
- prompts: {},
25
- },
26
- ...options,
27
- });
28
- /**
29
- * Handler for ping requests to check server health
30
- */
31
- server.setRequestHandler(PingRequestSchema, (request) => handlePingRequest({ request, client, db, isReadOnlyMode }));
32
- /**
33
- * Handler for listing available collections as resources.
34
- */
35
- server.setRequestHandler(ListResourcesRequestSchema, (request) => handleListResourcesRequest({ request, client, db, isReadOnlyMode }));
36
- /**
37
- * Handler for reading a collection's schema or contents.
38
- */
39
- server.setRequestHandler(ReadResourceRequestSchema, (request) => handleReadResourceRequest({ request, client, db, isReadOnlyMode }));
40
- /**
41
- * Handler that lists available tools.
42
- */
43
- server.setRequestHandler(ListToolsRequestSchema, (request) => handleListToolsRequest({ request, client, db, isReadOnlyMode }));
44
- /**
45
- * Handler for MongoDB tools.
46
- */
47
- server.setRequestHandler(CallToolRequestSchema, (request) => handleCallToolRequest({ request, client, db, isReadOnlyMode }));
48
- /**
49
- * Handler that lists available prompts.
50
- */
51
- server.setRequestHandler(ListPromptsRequestSchema, (request) => handleListPromptsRequest({ request, client, db, isReadOnlyMode }));
52
- /**
53
- * Handler for collection analysis prompt.
54
- */
55
- server.setRequestHandler(GetPromptRequestSchema, (request) => handleGetPromptRequest({ request, client, db, isReadOnlyMode }));
56
- /**
57
- * Handler for listing templates.
58
- */
59
- server.setRequestHandler(ListResourceTemplatesRequestSchema, (request) => handleListResourceTemplatesRequest({ request, client, db, isReadOnlyMode }));
60
- /**
61
- * Handler for completion requests.
62
- */
63
- server.setRequestHandler(CompleteRequestSchema, (request) => handleCompletionRequest({ request, client, db, isReadOnlyMode }));
64
- return server;
65
- }