mongodb-mcp-server 0.0.4 → 0.0.6

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.
Files changed (185) hide show
  1. package/.github/CODEOWNERS +3 -0
  2. package/.github/dependabot.yml +10 -0
  3. package/.github/workflows/code_health.yaml +53 -22
  4. package/.github/workflows/code_health_fork.yaml +106 -0
  5. package/.github/workflows/codeql.yml +34 -0
  6. package/.github/workflows/lint.yml +37 -0
  7. package/.github/workflows/prepare_release.yaml +6 -4
  8. package/.github/workflows/publish.yaml +6 -3
  9. package/.prettierrc.json +1 -1
  10. package/README.md +18 -0
  11. package/dist/common/atlas/apiClient.js +28 -4
  12. package/dist/common/atlas/apiClient.js.map +1 -1
  13. package/dist/config.js +4 -7
  14. package/dist/config.js.map +1 -1
  15. package/dist/errors.js +1 -1
  16. package/dist/errors.js.map +1 -1
  17. package/dist/index.js +12 -7
  18. package/dist/index.js.map +1 -1
  19. package/dist/logger.js +72 -28
  20. package/dist/logger.js.map +1 -1
  21. package/dist/packageInfo.js +6 -0
  22. package/dist/packageInfo.js.map +1 -0
  23. package/dist/server.js +114 -10
  24. package/dist/server.js.map +1 -1
  25. package/dist/session.js +66 -16
  26. package/dist/session.js.map +1 -1
  27. package/dist/telemetry/constants.js +15 -0
  28. package/dist/telemetry/constants.js.map +1 -0
  29. package/dist/telemetry/eventCache.js +53 -0
  30. package/dist/telemetry/eventCache.js.map +1 -0
  31. package/dist/telemetry/telemetry.js +97 -0
  32. package/dist/telemetry/telemetry.js.map +1 -0
  33. package/dist/telemetry/types.js +2 -0
  34. package/dist/telemetry/types.js.map +1 -0
  35. package/dist/tools/atlas/atlasTool.js +8 -3
  36. package/dist/tools/atlas/atlasTool.js.map +1 -1
  37. package/dist/tools/atlas/{createAccessList.js → create/createAccessList.js} +1 -2
  38. package/dist/tools/atlas/create/createAccessList.js.map +1 -0
  39. package/dist/tools/atlas/{createDBUser.js → create/createDBUser.js} +1 -2
  40. package/dist/tools/atlas/create/createDBUser.js.map +1 -0
  41. package/dist/tools/atlas/{createFreeCluster.js → create/createFreeCluster.js} +5 -3
  42. package/dist/tools/atlas/create/createFreeCluster.js.map +1 -0
  43. package/dist/tools/atlas/{createProject.js → create/createProject.js} +1 -2
  44. package/dist/tools/atlas/create/createProject.js.map +1 -0
  45. package/dist/tools/atlas/metadata/connectCluster.js +97 -0
  46. package/dist/tools/atlas/metadata/connectCluster.js.map +1 -0
  47. package/dist/tools/atlas/{inspectAccessList.js → read/inspectAccessList.js} +1 -2
  48. package/dist/tools/atlas/read/inspectAccessList.js.map +1 -0
  49. package/dist/tools/atlas/{inspectCluster.js → read/inspectCluster.js} +1 -2
  50. package/dist/tools/atlas/read/inspectCluster.js.map +1 -0
  51. package/dist/tools/atlas/{listClusters.js → read/listClusters.js} +1 -2
  52. package/dist/tools/atlas/read/listClusters.js.map +1 -0
  53. package/dist/tools/atlas/{listDBUsers.js → read/listDBUsers.js} +1 -2
  54. package/dist/tools/atlas/read/listDBUsers.js.map +1 -0
  55. package/dist/tools/atlas/{listOrgs.js → read/listOrgs.js} +1 -2
  56. package/dist/tools/atlas/read/listOrgs.js.map +1 -0
  57. package/dist/tools/atlas/{listProjects.js → read/listProjects.js} +11 -5
  58. package/dist/tools/atlas/read/listProjects.js.map +1 -0
  59. package/dist/tools/atlas/tools.js +12 -10
  60. package/dist/tools/atlas/tools.js.map +1 -1
  61. package/dist/tools/mongodb/create/insertMany.js +1 -1
  62. package/dist/tools/mongodb/create/insertMany.js.map +1 -1
  63. package/dist/tools/mongodb/delete/deleteMany.js +1 -2
  64. package/dist/tools/mongodb/delete/deleteMany.js.map +1 -1
  65. package/dist/tools/mongodb/metadata/collectionSchema.js +15 -13
  66. package/dist/tools/mongodb/metadata/collectionSchema.js.map +1 -1
  67. package/dist/tools/mongodb/metadata/collectionStorageSize.js +32 -3
  68. package/dist/tools/mongodb/metadata/collectionStorageSize.js.map +1 -1
  69. package/dist/tools/mongodb/metadata/connect.js +59 -72
  70. package/dist/tools/mongodb/metadata/connect.js.map +1 -1
  71. package/dist/tools/mongodb/metadata/dbStats.js +6 -1
  72. package/dist/tools/mongodb/metadata/dbStats.js.map +1 -1
  73. package/dist/tools/mongodb/metadata/explain.js +14 -7
  74. package/dist/tools/mongodb/metadata/explain.js.map +1 -1
  75. package/dist/tools/mongodb/metadata/logs.js +45 -0
  76. package/dist/tools/mongodb/metadata/logs.js.map +1 -0
  77. package/dist/tools/mongodb/mongodbTool.js +42 -36
  78. package/dist/tools/mongodb/mongodbTool.js.map +1 -1
  79. package/dist/tools/mongodb/read/aggregate.js +4 -4
  80. package/dist/tools/mongodb/read/aggregate.js.map +1 -1
  81. package/dist/tools/mongodb/read/collectionIndexes.js +24 -5
  82. package/dist/tools/mongodb/read/collectionIndexes.js.map +1 -1
  83. package/dist/tools/mongodb/read/count.js +1 -2
  84. package/dist/tools/mongodb/read/count.js.map +1 -1
  85. package/dist/tools/mongodb/read/find.js +5 -6
  86. package/dist/tools/mongodb/read/find.js.map +1 -1
  87. package/dist/tools/mongodb/tools.js +6 -2
  88. package/dist/tools/mongodb/tools.js.map +1 -1
  89. package/dist/tools/mongodb/update/renameCollection.js +28 -4
  90. package/dist/tools/mongodb/update/renameCollection.js.map +1 -1
  91. package/dist/tools/mongodb/update/updateMany.js +7 -11
  92. package/dist/tools/mongodb/update/updateMany.js.map +1 -1
  93. package/dist/tools/tool.js +68 -15
  94. package/dist/tools/tool.js.map +1 -1
  95. package/eslint.config.js +29 -10
  96. package/global.d.ts +1 -0
  97. package/package.json +7 -3
  98. package/scripts/apply.ts +9 -7
  99. package/scripts/filter.ts +3 -2
  100. package/src/common/atlas/apiClient.ts +44 -11
  101. package/src/config.ts +16 -17
  102. package/src/errors.ts +1 -1
  103. package/src/index.ts +12 -8
  104. package/src/logger.ts +92 -29
  105. package/src/packageInfo.ts +6 -0
  106. package/src/server.ts +160 -11
  107. package/src/session.ts +102 -22
  108. package/src/telemetry/constants.ts +15 -0
  109. package/src/telemetry/eventCache.ts +62 -0
  110. package/src/telemetry/telemetry.ts +125 -0
  111. package/src/telemetry/types.ts +77 -0
  112. package/src/tools/atlas/atlasTool.ts +7 -5
  113. package/src/tools/atlas/{createAccessList.ts → create/createAccessList.ts} +2 -4
  114. package/src/tools/atlas/{createDBUser.ts → create/createDBUser.ts} +3 -5
  115. package/src/tools/atlas/{createFreeCluster.ts → create/createFreeCluster.ts} +7 -6
  116. package/src/tools/atlas/{createProject.ts → create/createProject.ts} +3 -4
  117. package/src/tools/atlas/metadata/connectCluster.ts +114 -0
  118. package/src/tools/atlas/{inspectAccessList.ts → read/inspectAccessList.ts} +2 -4
  119. package/src/tools/atlas/{inspectCluster.ts → read/inspectCluster.ts} +3 -5
  120. package/src/tools/atlas/{listClusters.ts → read/listClusters.ts} +3 -5
  121. package/src/tools/atlas/{listDBUsers.ts → read/listDBUsers.ts} +3 -5
  122. package/src/tools/atlas/{listOrgs.ts → read/listOrgs.ts} +2 -4
  123. package/src/tools/atlas/{listProjects.ts → read/listProjects.ts} +15 -7
  124. package/src/tools/atlas/tools.ts +12 -10
  125. package/src/tools/mongodb/create/insertMany.ts +1 -1
  126. package/src/tools/mongodb/delete/deleteMany.ts +1 -2
  127. package/src/tools/mongodb/metadata/collectionSchema.ts +16 -14
  128. package/src/tools/mongodb/metadata/collectionStorageSize.ts +41 -3
  129. package/src/tools/mongodb/metadata/connect.ts +78 -76
  130. package/src/tools/mongodb/metadata/dbStats.ts +6 -1
  131. package/src/tools/mongodb/metadata/explain.ts +20 -7
  132. package/src/tools/mongodb/metadata/logs.ts +55 -0
  133. package/src/tools/mongodb/mongodbTool.ts +47 -40
  134. package/src/tools/mongodb/read/aggregate.ts +4 -4
  135. package/src/tools/mongodb/read/collectionIndexes.ts +29 -5
  136. package/src/tools/mongodb/read/count.ts +1 -2
  137. package/src/tools/mongodb/read/find.ts +5 -6
  138. package/src/tools/mongodb/tools.ts +6 -2
  139. package/src/tools/mongodb/update/renameCollection.ts +33 -4
  140. package/src/tools/mongodb/update/updateMany.ts +7 -11
  141. package/src/tools/tool.ts +89 -26
  142. package/tests/integration/helpers.ts +94 -107
  143. package/tests/integration/inMemoryTransport.ts +3 -2
  144. package/tests/integration/server.test.ts +75 -23
  145. package/tests/integration/tools/atlas/accessLists.test.ts +13 -15
  146. package/tests/integration/tools/atlas/atlasHelpers.ts +10 -13
  147. package/tests/integration/tools/atlas/clusters.test.ts +79 -16
  148. package/tests/integration/tools/atlas/dbUsers.test.ts +9 -9
  149. package/tests/integration/tools/atlas/orgs.test.ts +4 -4
  150. package/tests/integration/tools/atlas/projects.test.ts +10 -12
  151. package/tests/integration/tools/mongodb/create/createCollection.test.ts +19 -62
  152. package/tests/integration/tools/mongodb/create/createIndex.test.ts +41 -87
  153. package/tests/integration/tools/mongodb/create/insertMany.test.ts +35 -78
  154. package/tests/integration/tools/mongodb/delete/deleteMany.test.ts +25 -62
  155. package/tests/integration/tools/mongodb/delete/dropCollection.test.ts +22 -71
  156. package/tests/integration/tools/mongodb/delete/dropDatabase.test.ts +29 -63
  157. package/tests/integration/tools/mongodb/metadata/collectionSchema.test.ts +154 -0
  158. package/tests/integration/tools/mongodb/metadata/collectionStorageSize.test.ts +86 -0
  159. package/tests/integration/tools/mongodb/metadata/connect.test.ts +88 -93
  160. package/tests/integration/tools/mongodb/metadata/dbStats.test.ts +104 -0
  161. package/tests/integration/tools/mongodb/metadata/explain.test.ts +171 -0
  162. package/tests/integration/tools/mongodb/metadata/listCollections.test.ts +28 -56
  163. package/tests/integration/tools/mongodb/metadata/listDatabases.test.ts +32 -26
  164. package/tests/integration/tools/mongodb/metadata/logs.test.ts +83 -0
  165. package/tests/integration/tools/mongodb/mongodbHelpers.ts +176 -0
  166. package/tests/integration/tools/mongodb/read/aggregate.test.ts +99 -0
  167. package/tests/integration/tools/mongodb/read/collectionIndexes.test.ts +99 -0
  168. package/tests/integration/tools/mongodb/read/count.test.ts +31 -79
  169. package/tests/integration/tools/mongodb/read/find.test.ts +182 -0
  170. package/tests/integration/tools/mongodb/update/renameCollection.test.ts +194 -0
  171. package/tests/integration/tools/mongodb/update/updateMany.test.ts +238 -0
  172. package/tests/unit/telemetry.test.ts +200 -0
  173. package/tsconfig.jest.json +2 -1
  174. package/tsconfig.json +1 -1
  175. package/tsconfig.lint.json +8 -0
  176. package/dist/tools/atlas/createAccessList.js.map +0 -1
  177. package/dist/tools/atlas/createDBUser.js.map +0 -1
  178. package/dist/tools/atlas/createFreeCluster.js.map +0 -1
  179. package/dist/tools/atlas/createProject.js.map +0 -1
  180. package/dist/tools/atlas/inspectAccessList.js.map +0 -1
  181. package/dist/tools/atlas/inspectCluster.js.map +0 -1
  182. package/dist/tools/atlas/listClusters.js.map +0 -1
  183. package/dist/tools/atlas/listDBUsers.js.map +0 -1
  184. package/dist/tools/atlas/listOrgs.js.map +0 -1
  185. package/dist/tools/atlas/listProjects.js.map +0 -1
@@ -1,63 +1,38 @@
1
+ import { describeWithMongoDB, validateAutoConnectBehavior } from "../mongodbHelpers.js";
2
+
1
3
  import {
2
4
  getResponseContent,
3
- validateParameters,
4
- dbOperationParameters,
5
- setupIntegrationTest,
5
+ databaseCollectionParameters,
6
+ validateToolMetadata,
7
+ validateThrowsForInvalidArguments,
8
+ expectDefined,
6
9
  } from "../../../helpers.js";
7
- import { McpError } from "@modelcontextprotocol/sdk/types.js";
8
10
  import { IndexDirection } from "mongodb";
9
- import config from "../../../../../src/config.js";
10
-
11
- describe("createIndex tool", () => {
12
- const integration = setupIntegrationTest();
13
-
14
- it("should have correct metadata", async () => {
15
- const { tools } = await integration.mcpClient().listTools();
16
- const createIndex = tools.find((tool) => tool.name === "create-index")!;
17
- expect(createIndex).toBeDefined();
18
- expect(createIndex.description).toBe("Create an index for a collection");
19
-
20
- validateParameters(createIndex, [
21
- ...dbOperationParameters,
22
- {
23
- name: "keys",
24
- type: "object",
25
- description: "The index definition",
26
- required: true,
27
- },
28
- {
29
- name: "name",
30
- type: "string",
31
- description: "The name of the index",
32
- required: false,
33
- },
34
- ]);
35
- });
36
11
 
37
- describe("with invalid arguments", () => {
38
- const args = [
39
- {},
40
- { collection: "bar", database: 123, keys: { foo: 1 } },
41
- { collection: "bar", database: "test", keys: { foo: 5 } },
42
- { collection: [], database: "test", keys: { foo: 1 } },
43
- { collection: "bar", database: "test", keys: { foo: 1 }, name: 123 },
44
- { collection: "bar", database: "test", keys: "foo", name: "my-index" },
45
- ];
46
- for (const arg of args) {
47
- it(`throws a schema error for: ${JSON.stringify(arg)}`, async () => {
48
- await integration.connectMcpClient();
49
- try {
50
- await integration.mcpClient().callTool({ name: "create-index", arguments: arg });
51
- expect.fail("Expected an error to be thrown");
52
- } catch (error) {
53
- expect(error).toBeInstanceOf(McpError);
54
- const mcpError = error as McpError;
55
- expect(mcpError.code).toEqual(-32602);
56
- expect(mcpError.message).toContain("Invalid arguments for tool create-index");
57
- }
58
- });
59
- }
60
- });
12
+ describeWithMongoDB("createIndex tool", (integration) => {
13
+ validateToolMetadata(integration, "create-index", "Create an index for a collection", [
14
+ ...databaseCollectionParameters,
15
+ {
16
+ name: "keys",
17
+ type: "object",
18
+ description: "The index definition",
19
+ required: true,
20
+ },
21
+ {
22
+ name: "name",
23
+ type: "string",
24
+ description: "The name of the index",
25
+ required: false,
26
+ },
27
+ ]);
28
+
29
+ validateThrowsForInvalidArguments(integration, "create-index", [
30
+ {},
31
+ { collection: "bar", database: 123, keys: { foo: 1 } },
32
+ { collection: [], database: "test", keys: { foo: 1 } },
33
+ { collection: "bar", database: "test", keys: { foo: 1 }, name: 123 },
34
+ { collection: "bar", database: "test", keys: "foo", name: "my-index" },
35
+ ]);
61
36
 
62
37
  const validateIndex = async (collection: string, expected: { name: string; key: object }[]) => {
63
38
  const mongoClient = integration.mongoClient();
@@ -69,8 +44,8 @@ describe("createIndex tool", () => {
69
44
  expect(indexes[0].name).toEqual("_id_");
70
45
  for (const index of expected) {
71
46
  const foundIndex = indexes.find((i) => i.name === index.name);
72
- expect(foundIndex).toBeDefined();
73
- expect(foundIndex!.key).toEqual(index.key);
47
+ expectDefined(foundIndex);
48
+ expect(foundIndex.key).toEqual(index.key);
74
49
  }
75
50
  };
76
51
 
@@ -215,35 +190,14 @@ describe("createIndex tool", () => {
215
190
  });
216
191
  }
217
192
 
218
- describe("when not connected", () => {
219
- it("connects automatically if connection string is configured", async () => {
220
- config.connectionString = integration.connectionString();
221
-
222
- const response = await integration.mcpClient().callTool({
223
- name: "create-index",
224
- arguments: {
225
- database: integration.randomDbName(),
226
- collection: "coll1",
227
- keys: { prop1: 1 },
228
- },
229
- });
230
- const content = getResponseContent(response.content);
231
- expect(content).toEqual(
232
- `Created the index "prop1_1" on collection "coll1" in database "${integration.randomDbName()}"`
233
- );
234
- });
235
-
236
- it("throws an error if connection string is not configured", async () => {
237
- const response = await integration.mcpClient().callTool({
238
- name: "create-index",
239
- arguments: {
240
- database: integration.randomDbName(),
241
- collection: "coll1",
242
- keys: { prop1: 1 },
243
- },
244
- });
245
- const content = getResponseContent(response.content);
246
- expect(content).toContain("You need to connect to a MongoDB instance before you can access its data.");
247
- });
193
+ validateAutoConnectBehavior(integration, "create-index", () => {
194
+ return {
195
+ args: {
196
+ database: integration.randomDbName(),
197
+ collection: "coll1",
198
+ keys: { prop1: 1 },
199
+ },
200
+ expectedResponse: `Created the index "prop1_1" on collection "coll1" in database "${integration.randomDbName()}"`,
201
+ };
248
202
  });
249
203
  });
@@ -1,60 +1,36 @@
1
+ import { describeWithMongoDB, validateAutoConnectBehavior } from "../mongodbHelpers.js";
2
+
1
3
  import {
2
4
  getResponseContent,
3
- validateParameters,
4
- dbOperationParameters,
5
- setupIntegrationTest,
5
+ databaseCollectionParameters,
6
+ validateToolMetadata,
7
+ validateThrowsForInvalidArguments,
8
+ expectDefined,
6
9
  } from "../../../helpers.js";
7
- import { McpError } from "@modelcontextprotocol/sdk/types.js";
8
- import config from "../../../../../src/config.js";
9
-
10
- describe("insertMany tool", () => {
11
- const integration = setupIntegrationTest();
12
-
13
- it("should have correct metadata", async () => {
14
- const { tools } = await integration.mcpClient().listTools();
15
- const insertMany = tools.find((tool) => tool.name === "insert-many")!;
16
- expect(insertMany).toBeDefined();
17
- expect(insertMany.description).toBe("Insert an array of documents into a MongoDB collection");
18
-
19
- validateParameters(insertMany, [
20
- ...dbOperationParameters,
21
- {
22
- name: "documents",
23
- type: "array",
24
- description:
25
- "The array of documents to insert, matching the syntax of the document argument of db.collection.insertMany()",
26
- required: true,
27
- },
28
- ]);
29
- });
30
10
 
31
- describe("with invalid arguments", () => {
32
- const args = [
33
- {},
34
- { collection: "bar", database: 123, documents: [] },
35
- { collection: [], database: "test", documents: [] },
36
- { collection: "bar", database: "test", documents: "my-document" },
37
- { collection: "bar", database: "test", documents: { name: "Peter" } },
38
- ];
39
- for (const arg of args) {
40
- it(`throws a schema error for: ${JSON.stringify(arg)}`, async () => {
41
- await integration.connectMcpClient();
42
- try {
43
- await integration.mcpClient().callTool({ name: "insert-many", arguments: arg });
44
- expect.fail("Expected an error to be thrown");
45
- } catch (error) {
46
- expect(error).toBeInstanceOf(McpError);
47
- const mcpError = error as McpError;
48
- expect(mcpError.code).toEqual(-32602);
49
- expect(mcpError.message).toContain("Invalid arguments for tool insert-many");
50
- }
51
- });
52
- }
53
- });
11
+ describeWithMongoDB("insertMany tool", (integration) => {
12
+ validateToolMetadata(integration, "insert-many", "Insert an array of documents into a MongoDB collection", [
13
+ ...databaseCollectionParameters,
14
+ {
15
+ name: "documents",
16
+ type: "array",
17
+ description:
18
+ "The array of documents to insert, matching the syntax of the document argument of db.collection.insertMany()",
19
+ required: true,
20
+ },
21
+ ]);
22
+
23
+ validateThrowsForInvalidArguments(integration, "insert-many", [
24
+ {},
25
+ { collection: "bar", database: 123, documents: [] },
26
+ { collection: [], database: "test", documents: [] },
27
+ { collection: "bar", database: "test", documents: "my-document" },
28
+ { collection: "bar", database: "test", documents: { name: "Peter" } },
29
+ ]);
54
30
 
55
31
  const validateDocuments = async (collection: string, expectedDocuments: object[]) => {
56
32
  const collections = await integration.mongoClient().db(integration.randomDbName()).listCollections().toArray();
57
- expect(collections.find((c) => c.name === collection)).toBeDefined();
33
+ expectDefined(collections.find((c) => c.name === collection));
58
34
 
59
35
  const docs = await integration
60
36
  .mongoClient()
@@ -109,33 +85,14 @@ describe("insertMany tool", () => {
109
85
  expect(content).toContain(insertedIds[0].toString());
110
86
  });
111
87
 
112
- describe("when not connected", () => {
113
- it("connects automatically if connection string is configured", async () => {
114
- config.connectionString = integration.connectionString();
115
-
116
- const response = await integration.mcpClient().callTool({
117
- name: "insert-many",
118
- arguments: {
119
- database: integration.randomDbName(),
120
- collection: "coll1",
121
- documents: [{ prop1: "value1" }],
122
- },
123
- });
124
- const content = getResponseContent(response.content);
125
- expect(content).toContain('Inserted `1` document(s) into collection "coll1"');
126
- });
127
-
128
- it("throws an error if connection string is not configured", async () => {
129
- const response = await integration.mcpClient().callTool({
130
- name: "insert-many",
131
- arguments: {
132
- database: integration.randomDbName(),
133
- collection: "coll1",
134
- documents: [{ prop1: "value1" }],
135
- },
136
- });
137
- const content = getResponseContent(response.content);
138
- expect(content).toContain("You need to connect to a MongoDB instance before you can access its data.");
139
- });
88
+ validateAutoConnectBehavior(integration, "insert-many", () => {
89
+ return {
90
+ args: {
91
+ database: integration.randomDbName(),
92
+ collection: "coll1",
93
+ documents: [{ prop1: "value1" }],
94
+ },
95
+ expectedResponse: 'Inserted `1` document(s) into collection "coll1"',
96
+ };
140
97
  });
141
98
  });
@@ -1,23 +1,19 @@
1
+ import { describeWithMongoDB, validateAutoConnectBehavior } from "../mongodbHelpers.js";
2
+
1
3
  import {
2
4
  getResponseContent,
3
- validateParameters,
4
- dbOperationParameters,
5
- setupIntegrationTest,
5
+ databaseCollectionParameters,
6
+ validateToolMetadata,
7
+ validateThrowsForInvalidArguments,
6
8
  } from "../../../helpers.js";
7
- import { McpError } from "@modelcontextprotocol/sdk/types.js";
8
- import config from "../../../../../src/config.js";
9
-
10
- describe("deleteMany tool", () => {
11
- const integration = setupIntegrationTest();
12
9
 
13
- it("should have correct metadata", async () => {
14
- const { tools } = await integration.mcpClient().listTools();
15
- const deleteMany = tools.find((tool) => tool.name === "delete-many")!;
16
- expect(deleteMany).toBeDefined();
17
- expect(deleteMany.description).toBe("Removes all documents that match the filter from a MongoDB collection");
18
-
19
- validateParameters(deleteMany, [
20
- ...dbOperationParameters,
10
+ describeWithMongoDB("deleteMany tool", (integration) => {
11
+ validateToolMetadata(
12
+ integration,
13
+ "delete-many",
14
+ "Removes all documents that match the filter from a MongoDB collection",
15
+ [
16
+ ...databaseCollectionParameters,
21
17
  {
22
18
  name: "filter",
23
19
  type: "object",
@@ -25,31 +21,17 @@ describe("deleteMany tool", () => {
25
21
  "The query filter, specifying the deletion criteria. Matches the syntax of the filter argument of db.collection.deleteMany()",
26
22
  required: false,
27
23
  },
28
- ]);
29
- });
24
+ ]
25
+ );
30
26
 
31
27
  describe("with invalid arguments", () => {
32
- const args = [
28
+ validateThrowsForInvalidArguments(integration, "delete-many", [
33
29
  {},
34
30
  { collection: "bar", database: 123, filter: {} },
35
31
  { collection: [], database: "test", filter: {} },
36
32
  { collection: "bar", database: "test", filter: "my-document" },
37
33
  { collection: "bar", database: "test", filter: [{ name: "Peter" }] },
38
- ];
39
- for (const arg of args) {
40
- it(`throws a schema error for: ${JSON.stringify(arg)}`, async () => {
41
- await integration.connectMcpClient();
42
- try {
43
- await integration.mcpClient().callTool({ name: "delete-many", arguments: arg });
44
- expect.fail("Expected an error to be thrown");
45
- } catch (error) {
46
- expect(error).toBeInstanceOf(McpError);
47
- const mcpError = error as McpError;
48
- expect(mcpError.code).toEqual(-32602);
49
- expect(mcpError.message).toContain("Invalid arguments for tool delete-many");
50
- }
51
- });
52
- }
34
+ ]);
53
35
  });
54
36
 
55
37
  it("doesn't create the collection if it doesn't exist", async () => {
@@ -159,33 +141,14 @@ describe("deleteMany tool", () => {
159
141
  await validateDocuments([]);
160
142
  });
161
143
 
162
- describe("when not connected", () => {
163
- it("connects automatically if connection string is configured", async () => {
164
- config.connectionString = integration.connectionString();
165
-
166
- const response = await integration.mcpClient().callTool({
167
- name: "delete-many",
168
- arguments: {
169
- database: integration.randomDbName(),
170
- collection: "coll1",
171
- filter: {},
172
- },
173
- });
174
- const content = getResponseContent(response.content);
175
- expect(content).toContain('Deleted `0` document(s) from collection "coll1"');
176
- });
177
-
178
- it("throws an error if connection string is not configured", async () => {
179
- const response = await integration.mcpClient().callTool({
180
- name: "delete-many",
181
- arguments: {
182
- database: integration.randomDbName(),
183
- collection: "coll1",
184
- filter: {},
185
- },
186
- });
187
- const content = getResponseContent(response.content);
188
- expect(content).toContain("You need to connect to a MongoDB instance before you can access its data.");
189
- });
144
+ validateAutoConnectBehavior(integration, "delete-many", () => {
145
+ return {
146
+ args: {
147
+ database: integration.randomDbName(),
148
+ collection: "coll1",
149
+ filter: {},
150
+ },
151
+ expectedResponse: 'Deleted `0` document(s) from collection "coll1"',
152
+ };
190
153
  });
191
154
  });
@@ -1,48 +1,22 @@
1
+ import { describeWithMongoDB, validateAutoConnectBehavior } from "../mongodbHelpers.js";
2
+
1
3
  import {
2
4
  getResponseContent,
3
- validateParameters,
4
- dbOperationParameters,
5
- setupIntegrationTest,
5
+ databaseCollectionParameters,
6
+ validateToolMetadata,
7
+ validateThrowsForInvalidArguments,
8
+ databaseCollectionInvalidArgs,
6
9
  } from "../../../helpers.js";
7
- import { McpError } from "@modelcontextprotocol/sdk/types.js";
8
- import config from "../../../../../src/config.js";
9
-
10
- describe("dropCollection tool", () => {
11
- const integration = setupIntegrationTest();
12
10
 
13
- it("should have correct metadata", async () => {
14
- const { tools } = await integration.mcpClient().listTools();
15
- const dropCollection = tools.find((tool) => tool.name === "drop-collection")!;
16
- expect(dropCollection).toBeDefined();
17
- expect(dropCollection.description).toBe(
18
- "Removes a collection or view from the database. The method also removes any indexes associated with the dropped collection."
19
- );
11
+ describeWithMongoDB("dropCollection tool", (integration) => {
12
+ validateToolMetadata(
13
+ integration,
14
+ "drop-collection",
15
+ "Removes a collection or view from the database. The method also removes any indexes associated with the dropped collection.",
16
+ databaseCollectionParameters
17
+ );
20
18
 
21
- validateParameters(dropCollection, [...dbOperationParameters]);
22
- });
23
-
24
- describe("with invalid arguments", () => {
25
- const args = [
26
- {},
27
- { database: 123, collection: "bar" },
28
- { foo: "bar", database: "test", collection: "bar" },
29
- { collection: [], database: "test" },
30
- ];
31
- for (const arg of args) {
32
- it(`throws a schema error for: ${JSON.stringify(arg)}`, async () => {
33
- await integration.connectMcpClient();
34
- try {
35
- await integration.mcpClient().callTool({ name: "drop-collection", arguments: arg });
36
- expect.fail("Expected an error to be thrown");
37
- } catch (error) {
38
- expect(error).toBeInstanceOf(McpError);
39
- const mcpError = error as McpError;
40
- expect(mcpError.code).toEqual(-32602);
41
- expect(mcpError.message).toContain("Invalid arguments for tool drop-collection");
42
- }
43
- });
44
- }
45
- });
19
+ validateThrowsForInvalidArguments(integration, "drop-collection", databaseCollectionInvalidArgs);
46
20
 
47
21
  it("can drop non-existing collection", async () => {
48
22
  await integration.connectMcpClient();
@@ -83,36 +57,13 @@ describe("dropCollection tool", () => {
83
57
  expect(collections[0].name).toBe("coll2");
84
58
  });
85
59
 
86
- describe("when not connected", () => {
87
- it("connects automatically if connection string is configured", async () => {
88
- await integration.connectMcpClient();
89
- await integration.mongoClient().db(integration.randomDbName()).createCollection("coll1");
90
-
91
- config.connectionString = integration.connectionString();
92
-
93
- const response = await integration.mcpClient().callTool({
94
- name: "drop-collection",
95
- arguments: {
96
- database: integration.randomDbName(),
97
- collection: "coll1",
98
- },
99
- });
100
- const content = getResponseContent(response.content);
101
- expect(content).toContain(
102
- `Successfully dropped collection "coll1" from database "${integration.randomDbName()}"`
103
- );
104
- });
105
-
106
- it("throws an error if connection string is not configured", async () => {
107
- const response = await integration.mcpClient().callTool({
108
- name: "drop-collection",
109
- arguments: {
110
- database: integration.randomDbName(),
111
- collection: "coll1",
112
- },
113
- });
114
- const content = getResponseContent(response.content);
115
- expect(content).toContain("You need to connect to a MongoDB instance before you can access its data.");
116
- });
60
+ validateAutoConnectBehavior(integration, "drop-collection", () => {
61
+ return {
62
+ args: {
63
+ database: integration.randomDbName(),
64
+ collection: "coll1",
65
+ },
66
+ expectedResponse: `Successfully dropped collection "coll1" from database "${integration.randomDbName()}"`,
67
+ };
117
68
  });
118
69
  });
@@ -1,46 +1,28 @@
1
+ import { describeWithMongoDB, validateAutoConnectBehavior } from "../mongodbHelpers.js";
2
+
1
3
  import {
2
4
  getResponseContent,
3
- validateParameters,
4
- dbOperationParameters,
5
- setupIntegrationTest,
5
+ validateToolMetadata,
6
+ validateThrowsForInvalidArguments,
7
+ databaseParameters,
8
+ databaseInvalidArgs,
9
+ expectDefined,
6
10
  } from "../../../helpers.js";
7
- import { McpError } from "@modelcontextprotocol/sdk/types.js";
8
- import config from "../../../../../src/config.js";
9
-
10
- describe("dropDatabase tool", () => {
11
- const integration = setupIntegrationTest();
12
11
 
13
- it("should have correct metadata", async () => {
14
- const { tools } = await integration.mcpClient().listTools();
15
- const dropDatabase = tools.find((tool) => tool.name === "drop-database")!;
16
- expect(dropDatabase).toBeDefined();
17
- expect(dropDatabase.description).toBe("Removes the specified database, deleting the associated data files");
12
+ describeWithMongoDB("dropDatabase tool", (integration) => {
13
+ validateToolMetadata(
14
+ integration,
15
+ "drop-database",
16
+ "Removes the specified database, deleting the associated data files",
17
+ databaseParameters
18
+ );
18
19
 
19
- validateParameters(dropDatabase, [dbOperationParameters.find((d) => d.name === "database")!]);
20
- });
21
-
22
- describe("with invalid arguments", () => {
23
- const args = [{}, { database: 123 }, { foo: "bar", database: "test" }];
24
- for (const arg of args) {
25
- it(`throws a schema error for: ${JSON.stringify(arg)}`, async () => {
26
- await integration.connectMcpClient();
27
- try {
28
- await integration.mcpClient().callTool({ name: "drop-database", arguments: arg });
29
- expect.fail("Expected an error to be thrown");
30
- } catch (error) {
31
- expect(error).toBeInstanceOf(McpError);
32
- const mcpError = error as McpError;
33
- expect(mcpError.code).toEqual(-32602);
34
- expect(mcpError.message).toContain("Invalid arguments for tool drop-database");
35
- }
36
- });
37
- }
38
- });
20
+ validateThrowsForInvalidArguments(integration, "drop-database", databaseInvalidArgs);
39
21
 
40
22
  it("can drop non-existing database", async () => {
41
23
  let { databases } = await integration.mongoClient().db("").admin().listDatabases();
42
24
 
43
- const preDropLength = databases.length;
25
+ expect(databases.find((db) => db.name === integration.randomDbName())).toBeUndefined();
44
26
 
45
27
  await integration.connectMcpClient();
46
28
  const response = await integration.mcpClient().callTool({
@@ -55,7 +37,6 @@ describe("dropDatabase tool", () => {
55
37
 
56
38
  ({ databases } = await integration.mongoClient().db("").admin().listDatabases());
57
39
 
58
- expect(databases).toHaveLength(preDropLength);
59
40
  expect(databases.find((db) => db.name === integration.randomDbName())).toBeUndefined();
60
41
  });
61
42
 
@@ -65,7 +46,7 @@ describe("dropDatabase tool", () => {
65
46
  await integration.mongoClient().db(integration.randomDbName()).createCollection("coll2");
66
47
 
67
48
  let { databases } = await integration.mongoClient().db("").admin().listDatabases();
68
- expect(databases.find((db) => db.name === integration.randomDbName())).toBeDefined();
49
+ expectDefined(databases.find((db) => db.name === integration.randomDbName()));
69
50
 
70
51
  const response = await integration.mcpClient().callTool({
71
52
  name: "drop-database",
@@ -83,32 +64,17 @@ describe("dropDatabase tool", () => {
83
64
  expect(collections).toHaveLength(0);
84
65
  });
85
66
 
86
- describe("when not connected", () => {
87
- it("connects automatically if connection string is configured", async () => {
88
- await integration.connectMcpClient();
67
+ validateAutoConnectBehavior(
68
+ integration,
69
+ "drop-database",
70
+ () => {
71
+ return {
72
+ args: { database: integration.randomDbName() },
73
+ expectedResponse: `Successfully dropped database "${integration.randomDbName()}"`,
74
+ };
75
+ },
76
+ async () => {
89
77
  await integration.mongoClient().db(integration.randomDbName()).createCollection("coll1");
90
-
91
- config.connectionString = integration.connectionString();
92
-
93
- const response = await integration.mcpClient().callTool({
94
- name: "drop-database",
95
- arguments: {
96
- database: integration.randomDbName(),
97
- },
98
- });
99
- const content = getResponseContent(response.content);
100
- expect(content).toContain(`Successfully dropped database "${integration.randomDbName()}"`);
101
- });
102
-
103
- it("throws an error if connection string is not configured", async () => {
104
- const response = await integration.mcpClient().callTool({
105
- name: "drop-database",
106
- arguments: {
107
- database: integration.randomDbName(),
108
- },
109
- });
110
- const content = getResponseContent(response.content);
111
- expect(content).toContain("You need to connect to a MongoDB instance before you can access its data.");
112
- });
113
- });
78
+ }
79
+ );
114
80
  });