mongodb-mcp-server 0.1.2 → 0.2.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.
Files changed (270) hide show
  1. package/.github/pull_request_template.md +5 -0
  2. package/.github/workflows/accuracy-tests.yml +55 -0
  3. package/.github/workflows/check.yml +1 -1
  4. package/.github/workflows/code_health.yaml +4 -4
  5. package/.github/workflows/code_health_fork.yaml +0 -14
  6. package/.github/workflows/dependabot_pr.yaml +26 -0
  7. package/.github/workflows/docker.yaml +1 -1
  8. package/.github/workflows/jira-issue.yml +72 -0
  9. package/.smithery/smithery.yaml +10 -0
  10. package/.vscode/extensions.json +1 -1
  11. package/.vscode/launch.json +11 -1
  12. package/.vscode/settings.json +1 -11
  13. package/Dockerfile +1 -0
  14. package/README.md +132 -31
  15. package/dist/common/atlas/accessListUtils.js +36 -0
  16. package/dist/common/atlas/accessListUtils.js.map +1 -0
  17. package/dist/common/atlas/apiClient.js +25 -6
  18. package/dist/common/atlas/apiClient.js.map +1 -1
  19. package/dist/common/atlas/cluster.js +1 -1
  20. package/dist/common/atlas/cluster.js.map +1 -1
  21. package/dist/{config.js → common/config.js} +8 -1
  22. package/dist/common/config.js.map +1 -0
  23. package/dist/{errors.js → common/errors.js} +1 -0
  24. package/dist/common/errors.js.map +1 -0
  25. package/dist/{logger.js → common/logger.js} +20 -19
  26. package/dist/common/logger.js.map +1 -0
  27. package/dist/common/managedTimeout.js +20 -0
  28. package/dist/common/managedTimeout.js.map +1 -0
  29. package/dist/common/packageInfo.js.map +1 -0
  30. package/dist/{session.js → common/session.js} +20 -21
  31. package/dist/common/session.js.map +1 -0
  32. package/dist/common/sessionStore.js +73 -0
  33. package/dist/common/sessionStore.js.map +1 -0
  34. package/dist/helpers/container.js +28 -0
  35. package/dist/helpers/container.js.map +1 -0
  36. package/dist/helpers/generatePassword.js.map +1 -0
  37. package/dist/helpers/indexCheck.js +63 -0
  38. package/dist/helpers/indexCheck.js.map +1 -0
  39. package/dist/index.js +30 -37
  40. package/dist/index.js.map +1 -1
  41. package/dist/server.js +44 -7
  42. package/dist/server.js.map +1 -1
  43. package/dist/telemetry/constants.js +1 -1
  44. package/dist/telemetry/constants.js.map +1 -1
  45. package/dist/telemetry/telemetry.js +86 -116
  46. package/dist/telemetry/telemetry.js.map +1 -1
  47. package/dist/tools/atlas/atlasTool.js +3 -3
  48. package/dist/tools/atlas/atlasTool.js.map +1 -1
  49. package/dist/tools/atlas/connect/connectCluster.js +198 -0
  50. package/dist/tools/atlas/connect/connectCluster.js.map +1 -0
  51. package/dist/tools/atlas/create/createAccessList.js +9 -10
  52. package/dist/tools/atlas/create/createAccessList.js.map +1 -1
  53. package/dist/tools/atlas/create/createDBUser.js +3 -1
  54. package/dist/tools/atlas/create/createDBUser.js.map +1 -1
  55. package/dist/tools/atlas/create/createFreeCluster.js +2 -0
  56. package/dist/tools/atlas/create/createFreeCluster.js.map +1 -1
  57. package/dist/tools/atlas/create/createProject.js.map +1 -1
  58. package/dist/tools/atlas/read/inspectAccessList.js.map +1 -1
  59. package/dist/tools/atlas/read/inspectCluster.js.map +1 -1
  60. package/dist/tools/atlas/read/listAlerts.js.map +1 -1
  61. package/dist/tools/atlas/read/listClusters.js.map +1 -1
  62. package/dist/tools/atlas/read/listDBUsers.js.map +1 -1
  63. package/dist/tools/atlas/read/listOrgs.js.map +1 -1
  64. package/dist/tools/atlas/read/listProjects.js.map +1 -1
  65. package/dist/tools/atlas/tools.js +1 -1
  66. package/dist/tools/atlas/tools.js.map +1 -1
  67. package/dist/tools/mongodb/{metadata → connect}/connect.js +7 -4
  68. package/dist/tools/mongodb/connect/connect.js.map +1 -0
  69. package/dist/tools/mongodb/create/createCollection.js.map +1 -1
  70. package/dist/tools/mongodb/create/createIndex.js +1 -1
  71. package/dist/tools/mongodb/create/createIndex.js.map +1 -1
  72. package/dist/tools/mongodb/create/insertMany.js +1 -1
  73. package/dist/tools/mongodb/create/insertMany.js.map +1 -1
  74. package/dist/tools/mongodb/delete/deleteMany.js +20 -1
  75. package/dist/tools/mongodb/delete/deleteMany.js.map +1 -1
  76. package/dist/tools/mongodb/delete/dropCollection.js.map +1 -1
  77. package/dist/tools/mongodb/delete/dropDatabase.js.map +1 -1
  78. package/dist/tools/mongodb/metadata/collectionSchema.js.map +1 -1
  79. package/dist/tools/mongodb/metadata/collectionStorageSize.js.map +1 -1
  80. package/dist/tools/mongodb/metadata/dbStats.js.map +1 -1
  81. package/dist/tools/mongodb/metadata/explain.js +2 -2
  82. package/dist/tools/mongodb/metadata/explain.js.map +1 -1
  83. package/dist/tools/mongodb/metadata/listCollections.js.map +1 -1
  84. package/dist/tools/mongodb/metadata/listDatabases.js.map +1 -1
  85. package/dist/tools/mongodb/metadata/logs.js.map +1 -1
  86. package/dist/tools/mongodb/mongodbTool.js +47 -10
  87. package/dist/tools/mongodb/mongodbTool.js.map +1 -1
  88. package/dist/tools/mongodb/read/aggregate.js +10 -1
  89. package/dist/tools/mongodb/read/aggregate.js.map +1 -1
  90. package/dist/tools/mongodb/read/collectionIndexes.js.map +1 -1
  91. package/dist/tools/mongodb/read/count.js +15 -1
  92. package/dist/tools/mongodb/read/count.js.map +1 -1
  93. package/dist/tools/mongodb/read/find.js +14 -4
  94. package/dist/tools/mongodb/read/find.js.map +1 -1
  95. package/dist/tools/mongodb/tools.js +1 -1
  96. package/dist/tools/mongodb/tools.js.map +1 -1
  97. package/dist/tools/mongodb/update/renameCollection.js.map +1 -1
  98. package/dist/tools/mongodb/update/updateMany.js +24 -2
  99. package/dist/tools/mongodb/update/updateMany.js.map +1 -1
  100. package/dist/tools/tool.js +12 -9
  101. package/dist/tools/tool.js.map +1 -1
  102. package/dist/transports/base.js +26 -0
  103. package/dist/transports/base.js.map +1 -0
  104. package/dist/{helpers/EJsonTransport.js → transports/stdio.js} +24 -2
  105. package/dist/transports/stdio.js.map +1 -0
  106. package/dist/transports/streamableHttp.js +140 -0
  107. package/dist/transports/streamableHttp.js.map +1 -0
  108. package/eslint.config.js +13 -4
  109. package/package.json +43 -33
  110. package/resources/test-summary-template.html +415 -0
  111. package/scripts/accuracy/generateTestSummary.ts +335 -0
  112. package/scripts/accuracy/runAccuracyTests.sh +45 -0
  113. package/scripts/accuracy/updateAccuracyRunStatus.ts +21 -0
  114. package/src/common/atlas/accessListUtils.ts +54 -0
  115. package/src/common/atlas/apiClient.ts +25 -6
  116. package/src/common/atlas/cluster.ts +1 -1
  117. package/src/{config.ts → common/config.ts} +16 -2
  118. package/src/{errors.ts → common/errors.ts} +1 -0
  119. package/src/{logger.ts → common/logger.ts} +21 -24
  120. package/src/common/managedTimeout.ts +27 -0
  121. package/src/{session.ts → common/session.ts} +24 -26
  122. package/src/common/sessionStore.ts +111 -0
  123. package/src/helpers/container.ts +35 -0
  124. package/src/helpers/indexCheck.ts +83 -0
  125. package/src/index.ts +30 -40
  126. package/src/server.ts +55 -11
  127. package/src/telemetry/constants.ts +1 -1
  128. package/src/telemetry/telemetry.ts +109 -153
  129. package/src/telemetry/types.ts +2 -1
  130. package/src/tools/atlas/atlasTool.ts +4 -4
  131. package/src/tools/atlas/connect/connectCluster.ts +259 -0
  132. package/src/tools/atlas/create/createAccessList.ts +15 -13
  133. package/src/tools/atlas/create/createDBUser.ts +5 -3
  134. package/src/tools/atlas/create/createFreeCluster.ts +4 -2
  135. package/src/tools/atlas/create/createProject.ts +2 -2
  136. package/src/tools/atlas/read/inspectAccessList.ts +2 -2
  137. package/src/tools/atlas/read/inspectCluster.ts +2 -2
  138. package/src/tools/atlas/read/listAlerts.ts +2 -2
  139. package/src/tools/atlas/read/listClusters.ts +2 -2
  140. package/src/tools/atlas/read/listDBUsers.ts +2 -2
  141. package/src/tools/atlas/read/listOrgs.ts +2 -2
  142. package/src/tools/atlas/read/listProjects.ts +2 -2
  143. package/src/tools/atlas/tools.ts +1 -1
  144. package/src/tools/mongodb/{metadata → connect}/connect.ts +12 -9
  145. package/src/tools/mongodb/create/createCollection.ts +2 -2
  146. package/src/tools/mongodb/create/createIndex.ts +3 -3
  147. package/src/tools/mongodb/create/insertMany.ts +3 -3
  148. package/src/tools/mongodb/delete/deleteMany.ts +24 -3
  149. package/src/tools/mongodb/delete/dropCollection.ts +2 -2
  150. package/src/tools/mongodb/delete/dropDatabase.ts +2 -2
  151. package/src/tools/mongodb/metadata/collectionSchema.ts +2 -2
  152. package/src/tools/mongodb/metadata/collectionStorageSize.ts +2 -2
  153. package/src/tools/mongodb/metadata/dbStats.ts +2 -2
  154. package/src/tools/mongodb/metadata/explain.ts +4 -4
  155. package/src/tools/mongodb/metadata/listCollections.ts +2 -2
  156. package/src/tools/mongodb/metadata/listDatabases.ts +2 -2
  157. package/src/tools/mongodb/metadata/logs.ts +2 -2
  158. package/src/tools/mongodb/mongodbTool.ts +60 -14
  159. package/src/tools/mongodb/read/aggregate.ts +14 -3
  160. package/src/tools/mongodb/read/collectionIndexes.ts +2 -2
  161. package/src/tools/mongodb/read/count.ts +19 -3
  162. package/src/tools/mongodb/read/find.ts +20 -6
  163. package/src/tools/mongodb/tools.ts +1 -1
  164. package/src/tools/mongodb/update/renameCollection.ts +2 -2
  165. package/src/tools/mongodb/update/updateMany.ts +28 -4
  166. package/src/tools/tool.ts +23 -18
  167. package/src/transports/base.ts +34 -0
  168. package/src/{helpers/EJsonTransport.ts → transports/stdio.ts} +30 -1
  169. package/src/transports/streamableHttp.ts +178 -0
  170. package/tests/accuracy/aggregate.test.ts +27 -0
  171. package/tests/accuracy/collectionIndexes.test.ts +40 -0
  172. package/tests/accuracy/collectionSchema.test.ts +28 -0
  173. package/tests/accuracy/collectionStorageSize.test.ts +41 -0
  174. package/tests/accuracy/count.test.ts +44 -0
  175. package/tests/accuracy/createCollection.test.ts +46 -0
  176. package/tests/accuracy/createIndex.test.ts +37 -0
  177. package/tests/accuracy/dbStats.test.ts +15 -0
  178. package/tests/accuracy/deleteMany.test.ts +44 -0
  179. package/tests/accuracy/dropCollection.test.ts +74 -0
  180. package/tests/accuracy/dropDatabase.test.ts +41 -0
  181. package/tests/accuracy/explain.test.ts +73 -0
  182. package/tests/accuracy/find.test.ts +114 -0
  183. package/tests/accuracy/insertMany.test.ts +48 -0
  184. package/tests/accuracy/listCollections.test.ts +60 -0
  185. package/tests/accuracy/listDatabases.test.ts +31 -0
  186. package/tests/accuracy/logs.test.ts +28 -0
  187. package/tests/accuracy/renameCollection.test.ts +31 -0
  188. package/tests/accuracy/sdk/accuracyResultStorage/diskStorage.ts +189 -0
  189. package/tests/accuracy/sdk/accuracyResultStorage/getAccuracyResultStorage.ts +11 -0
  190. package/tests/accuracy/sdk/accuracyResultStorage/mongodbStorage.ts +151 -0
  191. package/tests/accuracy/sdk/accuracyResultStorage/resultStorage.ts +117 -0
  192. package/tests/accuracy/sdk/accuracyScorer.ts +93 -0
  193. package/tests/accuracy/sdk/accuracyTestingClient.ts +94 -0
  194. package/tests/accuracy/sdk/agent.ts +56 -0
  195. package/tests/accuracy/sdk/constants.ts +26 -0
  196. package/tests/accuracy/sdk/describeAccuracyTests.ts +126 -0
  197. package/tests/accuracy/sdk/gitInfo.ts +7 -0
  198. package/tests/accuracy/sdk/matcher.ts +193 -0
  199. package/tests/accuracy/sdk/models.ts +95 -0
  200. package/tests/accuracy/test-data-dumps/comics.books.json +417 -0
  201. package/tests/accuracy/test-data-dumps/comics.characters.json +402 -0
  202. package/tests/accuracy/test-data-dumps/mflix.movies.json +496 -0
  203. package/tests/accuracy/test-data-dumps/mflix.shows.json +572 -0
  204. package/tests/accuracy/updateMany.test.ts +42 -0
  205. package/tests/integration/helpers.ts +9 -9
  206. package/tests/integration/indexCheck.test.ts +464 -0
  207. package/tests/integration/server.test.ts +6 -4
  208. package/tests/integration/telemetry.test.ts +29 -0
  209. package/tests/integration/tools/atlas/accessLists.test.ts +22 -2
  210. package/tests/integration/tools/atlas/alerts.test.ts +3 -2
  211. package/tests/integration/tools/atlas/atlasHelpers.ts +3 -0
  212. package/tests/integration/tools/atlas/clusters.test.ts +68 -16
  213. package/tests/integration/tools/atlas/dbUsers.test.ts +14 -1
  214. package/tests/integration/tools/atlas/orgs.test.ts +2 -1
  215. package/tests/integration/tools/atlas/projects.test.ts +4 -3
  216. package/tests/integration/tools/mongodb/{metadata → connect}/connect.test.ts +34 -3
  217. package/tests/integration/tools/mongodb/create/createCollection.test.ts +1 -0
  218. package/tests/integration/tools/mongodb/create/createIndex.test.ts +1 -0
  219. package/tests/integration/tools/mongodb/create/insertMany.test.ts +1 -0
  220. package/tests/integration/tools/mongodb/delete/deleteMany.test.ts +1 -0
  221. package/tests/integration/tools/mongodb/delete/dropCollection.test.ts +1 -1
  222. package/tests/integration/tools/mongodb/delete/dropDatabase.test.ts +1 -0
  223. package/tests/integration/tools/mongodb/metadata/collectionSchema.test.ts +1 -0
  224. package/tests/integration/tools/mongodb/metadata/collectionStorageSize.test.ts +1 -0
  225. package/tests/integration/tools/mongodb/metadata/dbStats.test.ts +1 -0
  226. package/tests/integration/tools/mongodb/metadata/explain.test.ts +1 -0
  227. package/tests/integration/tools/mongodb/metadata/listCollections.test.ts +1 -0
  228. package/tests/integration/tools/mongodb/metadata/listDatabases.test.ts +3 -2
  229. package/tests/integration/tools/mongodb/metadata/logs.test.ts +1 -0
  230. package/tests/integration/tools/mongodb/mongodbHelpers.ts +67 -2
  231. package/tests/integration/tools/mongodb/read/aggregate.test.ts +2 -1
  232. package/tests/integration/tools/mongodb/read/collectionIndexes.test.ts +1 -0
  233. package/tests/integration/tools/mongodb/read/count.test.ts +1 -0
  234. package/tests/integration/tools/mongodb/read/find.test.ts +2 -1
  235. package/tests/integration/tools/mongodb/update/renameCollection.test.ts +1 -0
  236. package/tests/integration/tools/mongodb/update/updateMany.test.ts +1 -0
  237. package/tests/integration/transports/stdio.test.ts +40 -0
  238. package/tests/integration/transports/streamableHttp.test.ts +56 -0
  239. package/tests/matchers/toIncludeSameMembers.test.ts +59 -0
  240. package/tests/matchers/toIncludeSameMembers.ts +12 -0
  241. package/tests/setup.ts +7 -0
  242. package/tests/unit/accessListUtils.test.ts +39 -0
  243. package/tests/unit/accuracyScorer.test.ts +390 -0
  244. package/tests/unit/{apiClient.test.ts → common/apiClient.test.ts} +15 -15
  245. package/tests/unit/common/managedTimeout.test.ts +67 -0
  246. package/tests/unit/{session.test.ts → common/session.test.ts} +7 -12
  247. package/tests/unit/helpers/indexCheck.test.ts +150 -0
  248. package/tests/unit/telemetry.test.ts +99 -137
  249. package/tests/unit/{EJsonTransport.test.ts → transports/stdio.test.ts} +4 -4
  250. package/tests/vitest.d.ts +11 -0
  251. package/tsconfig.json +0 -1
  252. package/{tsconfig.jest.json → tsconfig.test.json} +1 -2
  253. package/vitest.config.ts +41 -0
  254. package/dist/common/atlas/generatePassword.js.map +0 -1
  255. package/dist/config.js.map +0 -1
  256. package/dist/errors.js.map +0 -1
  257. package/dist/helpers/EJsonTransport.js.map +0 -1
  258. package/dist/helpers/packageInfo.js.map +0 -1
  259. package/dist/logger.js.map +0 -1
  260. package/dist/session.js.map +0 -1
  261. package/dist/tools/atlas/metadata/connectCluster.js +0 -100
  262. package/dist/tools/atlas/metadata/connectCluster.js.map +0 -1
  263. package/dist/tools/mongodb/metadata/connect.js.map +0 -1
  264. package/global.d.ts +0 -1
  265. package/jest.config.cjs +0 -22
  266. package/src/tools/atlas/metadata/connectCluster.ts +0 -121
  267. /package/dist/{helpers → common}/packageInfo.js +0 -0
  268. /package/dist/{common/atlas → helpers}/generatePassword.js +0 -0
  269. /package/src/{helpers → common}/packageInfo.ts +0 -0
  270. /package/src/{common/atlas → helpers}/generatePassword.ts +0 -0
@@ -1,7 +1,9 @@
1
- import { Session } from "../../../../src/session.js";
2
- import { expectDefined } from "../../helpers.js";
1
+ import { Session } from "../../../../src/common/session.js";
2
+ import { expectDefined, getResponseElements } from "../../helpers.js";
3
3
  import { describeWithAtlas, withProject, randomId } from "./atlasHelpers.js";
4
+ import { ClusterDescription20240805 } from "../../../../src/common/atlas/openapi.js";
4
5
  import { CallToolResult } from "@modelcontextprotocol/sdk/types.js";
6
+ import { afterAll, beforeAll, describe, expect, it } from "vitest";
5
7
 
6
8
  function sleep(ms: number) {
7
9
  return new Promise((resolve) => setTimeout(resolve, ms));
@@ -33,7 +35,12 @@ async function deleteAndWaitCluster(session: Session, projectId: string, cluster
33
35
  }
34
36
  }
35
37
 
36
- async function waitClusterState(session: Session, projectId: string, clusterName: string, state: string) {
38
+ async function waitCluster(
39
+ session: Session,
40
+ projectId: string,
41
+ clusterName: string,
42
+ check: (cluster: ClusterDescription20240805) => boolean | Promise<boolean>
43
+ ) {
37
44
  while (true) {
38
45
  const cluster = await session.apiClient.getCluster({
39
46
  params: {
@@ -43,7 +50,7 @@ async function waitClusterState(session: Session, projectId: string, clusterName
43
50
  },
44
51
  },
45
52
  });
46
- if (cluster?.stateName === state) {
53
+ if (await check(cluster)) {
47
54
  return;
48
55
  }
49
56
  await sleep(1000);
@@ -75,8 +82,10 @@ describeWithAtlas("clusters", (integration) => {
75
82
  expect(createFreeCluster.inputSchema.properties).toHaveProperty("region");
76
83
  });
77
84
 
78
- it("should create a free cluster", async () => {
85
+ it("should create a free cluster and add current IP to access list", async () => {
79
86
  const projectId = getProjectId();
87
+ const session = integration.mcpServer().session;
88
+ const ipInfo = await session.apiClient.getIpInfo();
80
89
 
81
90
  const response = (await integration.mcpClient().callTool({
82
91
  name: "atlas-create-free-cluster",
@@ -86,9 +95,16 @@ describeWithAtlas("clusters", (integration) => {
86
95
  region: "US_EAST_1",
87
96
  },
88
97
  })) as CallToolResult;
89
- expect(response.content).toBeArray();
98
+ expect(response.content).toBeInstanceOf(Array);
90
99
  expect(response.content).toHaveLength(2);
91
100
  expect(response.content[0]?.text).toContain("has been created");
101
+
102
+ // Check that the current IP is present in the access list
103
+ const accessList = await session.apiClient.listProjectIpAccessLists({
104
+ params: { path: { groupId: projectId } },
105
+ });
106
+ const found = accessList.results?.some((entry) => entry.ipAddress === ipInfo.currentIpv4Address);
107
+ expect(found).toBe(true);
92
108
  });
93
109
  });
94
110
 
@@ -111,7 +127,7 @@ describeWithAtlas("clusters", (integration) => {
111
127
  name: "atlas-inspect-cluster",
112
128
  arguments: { projectId, clusterName: clusterName },
113
129
  })) as CallToolResult;
114
- expect(response.content).toBeArray();
130
+ expect(response.content).toBeInstanceOf(Array);
115
131
  expect(response.content).toHaveLength(1);
116
132
  expect(response.content[0]?.text).toContain(`${clusterName} | `);
117
133
  });
@@ -133,7 +149,7 @@ describeWithAtlas("clusters", (integration) => {
133
149
  const response = (await integration
134
150
  .mcpClient()
135
151
  .callTool({ name: "atlas-list-clusters", arguments: { projectId } })) as CallToolResult;
136
- expect(response.content).toBeArray();
152
+ expect(response.content).toBeInstanceOf(Array);
137
153
  expect(response.content).toHaveLength(2);
138
154
  expect(response.content[1]?.text).toContain(`${clusterName} | `);
139
155
  });
@@ -142,7 +158,12 @@ describeWithAtlas("clusters", (integration) => {
142
158
  describe("atlas-connect-cluster", () => {
143
159
  beforeAll(async () => {
144
160
  const projectId = getProjectId();
145
- await waitClusterState(integration.mcpServer().session, projectId, clusterName, "IDLE");
161
+ await waitCluster(integration.mcpServer().session, projectId, clusterName, (cluster) => {
162
+ return (
163
+ cluster.stateName === "IDLE" &&
164
+ (cluster.connectionStrings?.standardSrv || cluster.connectionStrings?.standard) !== undefined
165
+ );
166
+ });
146
167
  await integration.mcpServer().session.apiClient.createProjectIpAccessList({
147
168
  params: {
148
169
  path: {
@@ -172,13 +193,44 @@ describeWithAtlas("clusters", (integration) => {
172
193
  it("connects to cluster", async () => {
173
194
  const projectId = getProjectId();
174
195
 
175
- const response = (await integration.mcpClient().callTool({
176
- name: "atlas-connect-cluster",
177
- arguments: { projectId, clusterName },
178
- })) as CallToolResult;
179
- expect(response.content).toBeArray();
180
- expect(response.content).toHaveLength(1);
181
- expect(response.content[0]?.text).toContain(`Connected to cluster "${clusterName}"`);
196
+ for (let i = 0; i < 10; i++) {
197
+ const response = (await integration.mcpClient().callTool({
198
+ name: "atlas-connect-cluster",
199
+ arguments: { projectId, clusterName },
200
+ })) as CallToolResult;
201
+ expect(response.content).toBeInstanceOf(Array);
202
+ expect(response.content.length).toBeGreaterThanOrEqual(1);
203
+ expect(response.content[0]?.type).toEqual("text");
204
+ const c = response.content[0] as { text: string };
205
+ if (
206
+ c.text.includes("Cluster is already connected.") ||
207
+ c.text.includes(`Connected to cluster "${clusterName}"`)
208
+ ) {
209
+ break; // success
210
+ } else {
211
+ expect(response.content[0]?.text).toContain(
212
+ `Attempting to connect to cluster "${clusterName}"...`
213
+ );
214
+ }
215
+ await sleep(500);
216
+ }
217
+ });
218
+
219
+ describe("when not connected", () => {
220
+ it("prompts for atlas-connect-cluster when querying mongodb", async () => {
221
+ const response = await integration.mcpClient().callTool({
222
+ name: "find",
223
+ arguments: { database: "some-db", collection: "some-collection" },
224
+ });
225
+ const elements = getResponseElements(response.content);
226
+ expect(elements).toHaveLength(2);
227
+ expect(elements[0]?.text).toContain(
228
+ "You need to connect to a MongoDB instance before you can access its data."
229
+ );
230
+ expect(elements[1]?.text).toContain(
231
+ 'Please use one of the following tools: "atlas-connect-cluster", "connect" to connect to a MongoDB instance'
232
+ );
233
+ });
182
234
  });
183
235
  });
184
236
  });
@@ -2,6 +2,7 @@ import { CallToolResult } from "@modelcontextprotocol/sdk/types.js";
2
2
  import { describeWithAtlas, withProject, randomId } from "./atlasHelpers.js";
3
3
  import { expectDefined, getResponseElements } from "../../helpers.js";
4
4
  import { ApiClientError } from "../../../../src/common/atlas/apiClientError.js";
5
+ import { afterEach, beforeEach, describe, expect, it } from "vitest";
5
6
 
6
7
  describeWithAtlas("db users", (integration) => {
7
8
  withProject(integration, ({ getProjectId }) => {
@@ -78,6 +79,18 @@ describeWithAtlas("db users", (integration) => {
78
79
  expect(elements[0]?.text).toContain(userName);
79
80
  expect(elements[0]?.text).toContain("with password: `");
80
81
  });
82
+
83
+ it("should add current IP to access list when creating a database user", async () => {
84
+ const projectId = getProjectId();
85
+ const session = integration.mcpServer().session;
86
+ const ipInfo = await session.apiClient.getIpInfo();
87
+ await createUserWithMCP();
88
+ const accessList = await session.apiClient.listProjectIpAccessLists({
89
+ params: { path: { groupId: projectId } },
90
+ });
91
+ const found = accessList.results?.some((entry) => entry.ipAddress === ipInfo.currentIpv4Address);
92
+ expect(found).toBe(true);
93
+ });
81
94
  });
82
95
  describe("atlas-list-db-users", () => {
83
96
  it("should have correct metadata", async () => {
@@ -96,7 +109,7 @@ describeWithAtlas("db users", (integration) => {
96
109
  const response = (await integration
97
110
  .mcpClient()
98
111
  .callTool({ name: "atlas-list-db-users", arguments: { projectId } })) as CallToolResult;
99
- expect(response.content).toBeArray();
112
+ expect(response.content).toBeInstanceOf(Array);
100
113
  expect(response.content).toHaveLength(1);
101
114
  expect(response.content[0]?.text).toContain(userName);
102
115
  });
@@ -1,6 +1,7 @@
1
1
  import { CallToolResult } from "@modelcontextprotocol/sdk/types.js";
2
2
  import { expectDefined } from "../../helpers.js";
3
3
  import { parseTable, describeWithAtlas } from "./atlasHelpers.js";
4
+ import { describe, expect, it } from "vitest";
4
5
 
5
6
  describeWithAtlas("orgs", (integration) => {
6
7
  describe("atlas-list-orgs", () => {
@@ -14,7 +15,7 @@ describeWithAtlas("orgs", (integration) => {
14
15
  const response = (await integration
15
16
  .mcpClient()
16
17
  .callTool({ name: "atlas-list-orgs", arguments: {} })) as CallToolResult;
17
- expect(response.content).toBeArray();
18
+ expect(response.content).toBeInstanceOf(Array);
18
19
  expect(response.content).toHaveLength(1);
19
20
  const data = parseTable(response.content[0]?.text as string);
20
21
  expect(data).toHaveLength(1);
@@ -2,6 +2,7 @@ import { CallToolResult } from "@modelcontextprotocol/sdk/types.js";
2
2
  import { ObjectId } from "mongodb";
3
3
  import { parseTable, describeWithAtlas } from "./atlasHelpers.js";
4
4
  import { expectDefined } from "../../helpers.js";
5
+ import { afterAll, describe, expect, it } from "vitest";
5
6
 
6
7
  const randomId = new ObjectId().toString();
7
8
 
@@ -41,7 +42,7 @@ describeWithAtlas("projects", (integration) => {
41
42
  name: "atlas-create-project",
42
43
  arguments: { projectName: projName },
43
44
  })) as CallToolResult;
44
- expect(response.content).toBeArray();
45
+ expect(response.content).toBeInstanceOf(Array);
45
46
  expect(response.content).toHaveLength(1);
46
47
  expect(response.content[0]?.text).toContain(projName);
47
48
  });
@@ -60,11 +61,11 @@ describeWithAtlas("projects", (integration) => {
60
61
  const response = (await integration
61
62
  .mcpClient()
62
63
  .callTool({ name: "atlas-list-projects", arguments: {} })) as CallToolResult;
63
- expect(response.content).toBeArray();
64
+ expect(response.content).toBeInstanceOf(Array);
64
65
  expect(response.content).toHaveLength(1);
65
66
  expect(response.content[0]?.text).toContain(projName);
66
67
  const data = parseTable(response.content[0]?.text as string);
67
- expect(data).toBeArray();
68
+ expect(data).toBeInstanceOf(Array);
68
69
  expect(data.length).toBeGreaterThan(0);
69
70
  let found = false;
70
71
  for (const project of data) {
@@ -1,9 +1,16 @@
1
1
  import { describeWithMongoDB } from "../mongodbHelpers.js";
2
- import { getResponseContent, validateThrowsForInvalidArguments, validateToolMetadata } from "../../../helpers.js";
3
- import { config } from "../../../../../src/config.js";
2
+ import {
3
+ getResponseContent,
4
+ getResponseElements,
5
+ validateThrowsForInvalidArguments,
6
+ validateToolMetadata,
7
+ } from "../../../helpers.js";
8
+ import { config } from "../../../../../src/common/config.js";
9
+ import { defaultTestConfig, setupIntegrationTest } from "../../../helpers.js";
10
+ import { beforeEach, describe, expect, it } from "vitest";
4
11
 
5
12
  describeWithMongoDB(
6
- "switchConnection tool",
13
+ "SwitchConnection tool",
7
14
  (integration) => {
8
15
  beforeEach(() => {
9
16
  integration.mcpServer().userConfig.connectionString = integration.connectionString();
@@ -77,6 +84,7 @@ describeWithMongoDB(
77
84
  connectionString: mdbIntegration.connectionString(),
78
85
  })
79
86
  );
87
+
80
88
  describeWithMongoDB(
81
89
  "Connect tool",
82
90
  (integration) => {
@@ -126,3 +134,26 @@ describeWithMongoDB(
126
134
  },
127
135
  () => config
128
136
  );
137
+
138
+ describe("Connect tool when disabled", () => {
139
+ const integration = setupIntegrationTest(() => ({
140
+ ...defaultTestConfig,
141
+ disabledTools: ["connect"],
142
+ }));
143
+
144
+ it("is not suggested when querying MongoDB disconnected", async () => {
145
+ const response = await integration.mcpClient().callTool({
146
+ name: "find",
147
+ arguments: { database: "some-db", collection: "some-collection" },
148
+ });
149
+
150
+ const elements = getResponseElements(response);
151
+ expect(elements).toHaveLength(2);
152
+ expect(elements[0]?.text).toContain(
153
+ "You need to connect to a MongoDB instance before you can access its data."
154
+ );
155
+ expect(elements[1]?.text).toContain(
156
+ "There are no tools available to connect. Please update the configuration to include a connection string and restart the server."
157
+ );
158
+ });
159
+ });
@@ -7,6 +7,7 @@ import {
7
7
  validateThrowsForInvalidArguments,
8
8
  databaseCollectionInvalidArgs,
9
9
  } from "../../../helpers.js";
10
+ import { describe, expect, it } from "vitest";
10
11
 
11
12
  describeWithMongoDB("createCollection tool", (integration) => {
12
13
  validateToolMetadata(
@@ -8,6 +8,7 @@ import {
8
8
  expectDefined,
9
9
  } from "../../../helpers.js";
10
10
  import { IndexDirection } from "mongodb";
11
+ import { expect, it } from "vitest";
11
12
 
12
13
  describeWithMongoDB("createIndex tool", (integration) => {
13
14
  validateToolMetadata(integration, "create-index", "Create an index for a collection", [
@@ -7,6 +7,7 @@ import {
7
7
  validateThrowsForInvalidArguments,
8
8
  expectDefined,
9
9
  } from "../../../helpers.js";
10
+ import { expect, it } from "vitest";
10
11
 
11
12
  describeWithMongoDB("insertMany tool", (integration) => {
12
13
  validateToolMetadata(integration, "insert-many", "Insert an array of documents into a MongoDB collection", [
@@ -6,6 +6,7 @@ import {
6
6
  validateToolMetadata,
7
7
  validateThrowsForInvalidArguments,
8
8
  } from "../../../helpers.js";
9
+ import { describe, expect, it } from "vitest";
9
10
 
10
11
  describeWithMongoDB("deleteMany tool", (integration) => {
11
12
  validateToolMetadata(
@@ -1,5 +1,5 @@
1
1
  import { describeWithMongoDB, validateAutoConnectBehavior } from "../mongodbHelpers.js";
2
-
2
+ import { expect, it } from "vitest";
3
3
  import {
4
4
  getResponseContent,
5
5
  databaseCollectionParameters,
@@ -1,4 +1,5 @@
1
1
  import { describeWithMongoDB, validateAutoConnectBehavior } from "../mongodbHelpers.js";
2
+ import { expect, it } from "vitest";
2
3
 
3
4
  import {
4
5
  getResponseContent,
@@ -11,6 +11,7 @@ import {
11
11
  import { Document } from "bson";
12
12
  import { OptionalId } from "mongodb";
13
13
  import { SimplifiedSchema } from "mongodb-schema";
14
+ import { describe, expect, it } from "vitest";
14
15
 
15
16
  describeWithMongoDB("collectionSchema tool", (integration) => {
16
17
  validateToolMetadata(
@@ -9,6 +9,7 @@ import {
9
9
  expectDefined,
10
10
  } from "../../../helpers.js";
11
11
  import * as crypto from "crypto";
12
+ import { describe, expect, it } from "vitest";
12
13
 
13
14
  describeWithMongoDB("collectionStorageSize tool", (integration) => {
14
15
  validateToolMetadata(
@@ -8,6 +8,7 @@ import {
8
8
  } from "../../../helpers.js";
9
9
  import * as crypto from "crypto";
10
10
  import { describeWithMongoDB, validateAutoConnectBehavior } from "../mongodbHelpers.js";
11
+ import { describe, expect, it } from "vitest";
11
12
 
12
13
  describeWithMongoDB("dbStats tool", (integration) => {
13
14
  validateToolMetadata(
@@ -5,6 +5,7 @@ import {
5
5
  getResponseElements,
6
6
  } from "../../../helpers.js";
7
7
  import { describeWithMongoDB, validateAutoConnectBehavior } from "../mongodbHelpers.js";
8
+ import { beforeEach, describe, expect, it } from "vitest";
8
9
 
9
10
  describeWithMongoDB("explain tool", (integration) => {
10
11
  validateToolMetadata(
@@ -8,6 +8,7 @@ import {
8
8
  databaseInvalidArgs,
9
9
  databaseParameters,
10
10
  } from "../../../helpers.js";
11
+ import { describe, expect, it } from "vitest";
11
12
 
12
13
  describeWithMongoDB("listCollections tool", (integration) => {
13
14
  validateToolMetadata(
@@ -1,5 +1,6 @@
1
1
  import { describeWithMongoDB, validateAutoConnectBehavior } from "../mongodbHelpers.js";
2
2
  import { getResponseElements, getParameters, expectDefined } from "../../../helpers.js";
3
+ import { describe, expect, it } from "vitest";
3
4
 
4
5
  describeWithMongoDB("listDatabases tool", (integration) => {
5
6
  const defaultDatabases = ["admin", "config", "local"];
@@ -20,7 +21,7 @@ describeWithMongoDB("listDatabases tool", (integration) => {
20
21
  const response = await integration.mcpClient().callTool({ name: "list-databases", arguments: {} });
21
22
  const dbNames = getDbNames(response.content);
22
23
 
23
- expect(defaultDatabases).toIncludeAllMembers(dbNames);
24
+ expect(defaultDatabases).toStrictEqual(dbNames);
24
25
  });
25
26
  });
26
27
 
@@ -47,7 +48,7 @@ describeWithMongoDB("listDatabases tool", (integration) => {
47
48
  validate: (content) => {
48
49
  const dbNames = getDbNames(content);
49
50
 
50
- expect(defaultDatabases).toIncludeAllMembers(dbNames);
51
+ expect(defaultDatabases).toStrictEqual(dbNames);
51
52
  },
52
53
  };
53
54
  },
@@ -1,3 +1,4 @@
1
+ import { expect, it } from "vitest";
1
2
  import { validateToolMetadata, validateThrowsForInvalidArguments, getResponseElements } from "../../../helpers.js";
2
3
  import { describeWithMongoDB, validateAutoConnectBehavior } from "../mongodbHelpers.js";
3
4
 
@@ -2,12 +2,38 @@ import { MongoCluster } from "mongodb-runner";
2
2
  import path from "path";
3
3
  import { fileURLToPath } from "url";
4
4
  import fs from "fs/promises";
5
- import { MongoClient, ObjectId } from "mongodb";
5
+ import { Document, MongoClient, ObjectId } from "mongodb";
6
6
  import { getResponseContent, IntegrationTest, setupIntegrationTest, defaultTestConfig } from "../../helpers.js";
7
- import { UserConfig } from "../../../../src/config.js";
7
+ import { UserConfig } from "../../../../src/common/config.js";
8
+ import { afterAll, afterEach, beforeAll, beforeEach, describe, expect, it } from "vitest";
8
9
 
9
10
  const __dirname = path.dirname(fileURLToPath(import.meta.url));
10
11
 
12
+ const testDataDumpPath = path.join(__dirname, "..", "..", "..", "accuracy", "test-data-dumps");
13
+
14
+ const testDataPaths = [
15
+ {
16
+ db: "comics",
17
+ collection: "books",
18
+ path: path.join(testDataDumpPath, "comics.books.json"),
19
+ },
20
+ {
21
+ db: "comics",
22
+ collection: "characters",
23
+ path: path.join(testDataDumpPath, "comics.characters.json"),
24
+ },
25
+ {
26
+ db: "mflix",
27
+ collection: "movies",
28
+ path: path.join(testDataDumpPath, "mflix.movies.json"),
29
+ },
30
+ {
31
+ db: "mflix",
32
+ collection: "shows",
33
+ path: path.join(testDataDumpPath, "mflix.shows.json"),
34
+ },
35
+ ];
36
+
11
37
  interface MongoDBIntegrationTest {
12
38
  mongoClient: () => MongoClient;
13
39
  connectionString: () => string;
@@ -70,6 +96,7 @@ export function setupMongoDBIntegrationTest(): MongoDBIntegrationTest {
70
96
  tmpDir: dbsDir,
71
97
  logDir: path.join(tmpDir, "mongodb-runner", "logs"),
72
98
  topology: "standalone",
99
+ version: "8.0.10",
73
100
  });
74
101
 
75
102
  return;
@@ -168,3 +195,41 @@ export function validateAutoConnectBehavior(
168
195
  });
169
196
  });
170
197
  }
198
+
199
+ export function prepareTestData(integration: MongoDBIntegrationTest) {
200
+ const NON_TEST_DBS = ["admin", "config", "local"];
201
+ const testData: {
202
+ db: string;
203
+ collection: string;
204
+ data: Document[];
205
+ }[] = [];
206
+
207
+ beforeAll(async () => {
208
+ for (const { db, collection, path } of testDataPaths) {
209
+ testData.push({
210
+ db,
211
+ collection,
212
+ data: JSON.parse(await fs.readFile(path, "utf8")) as Document[],
213
+ });
214
+ }
215
+ });
216
+
217
+ return {
218
+ async populateTestData(this: void) {
219
+ const client = integration.mongoClient();
220
+ for (const { db, collection, data } of testData) {
221
+ await client.db(db).collection(collection).insertMany(data);
222
+ }
223
+ },
224
+ async cleanupTestDatabases(this: void, integration: MongoDBIntegrationTest) {
225
+ const client = integration.mongoClient();
226
+ const admin = client.db().admin();
227
+ const databases = await admin.listDatabases();
228
+ await Promise.all(
229
+ databases.databases
230
+ .filter(({ name }) => !NON_TEST_DBS.includes(name))
231
+ .map(({ name }) => client.db(name).dropDatabase())
232
+ );
233
+ },
234
+ };
235
+ }
@@ -4,6 +4,7 @@ import {
4
4
  validateThrowsForInvalidArguments,
5
5
  getResponseElements,
6
6
  } from "../../../helpers.js";
7
+ import { expect, it } from "vitest";
7
8
  import { describeWithMongoDB, validateAutoConnectBehavior } from "../mongodbHelpers.js";
8
9
 
9
10
  describeWithMongoDB("aggregate tool", (integration) => {
@@ -20,7 +21,7 @@ describeWithMongoDB("aggregate tool", (integration) => {
20
21
  validateThrowsForInvalidArguments(integration, "aggregate", [
21
22
  {},
22
23
  { database: "test", collection: "foo" },
23
- { database: test, pipeline: [] },
24
+ { database: "test", pipeline: [] },
24
25
  { database: "test", collection: "foo", pipeline: {} },
25
26
  { database: "test", collection: [], pipeline: [] },
26
27
  { database: 123, collection: "foo", pipeline: [] },
@@ -8,6 +8,7 @@ import {
8
8
  expectDefined,
9
9
  } from "../../../helpers.js";
10
10
  import { describeWithMongoDB, validateAutoConnectBehavior } from "../mongodbHelpers.js";
11
+ import { expect, it } from "vitest";
11
12
 
12
13
  describeWithMongoDB("collectionIndexes tool", (integration) => {
13
14
  validateToolMetadata(
@@ -6,6 +6,7 @@ import {
6
6
  validateToolMetadata,
7
7
  validateThrowsForInvalidArguments,
8
8
  } from "../../../helpers.js";
9
+ import { beforeEach, describe, expect, it } from "vitest";
9
10
 
10
11
  describeWithMongoDB("count tool", (integration) => {
11
12
  validateToolMetadata(
@@ -1,3 +1,4 @@
1
+ import { beforeEach, describe, expect, it } from "vitest";
1
2
  import {
2
3
  getResponseContent,
3
4
  databaseCollectionParameters,
@@ -33,7 +34,7 @@ describeWithMongoDB("find tool", (integration) => {
33
34
  {
34
35
  name: "sort",
35
36
  description:
36
- "A document, describing the sort order, matching the syntax of the sort argument of cursor.sort()",
37
+ "A document, describing the sort order, matching the syntax of the sort argument of cursor.sort(). The keys of the object are the fields to sort on, while the values are the sort directions (1 for ascending, -1 for descending).",
37
38
  type: "object",
38
39
  required: false,
39
40
  },
@@ -4,6 +4,7 @@ import {
4
4
  validateToolMetadata,
5
5
  validateThrowsForInvalidArguments,
6
6
  } from "../../../helpers.js";
7
+ import { describe, expect, it } from "vitest";
7
8
  import { describeWithMongoDB, validateAutoConnectBehavior } from "../mongodbHelpers.js";
8
9
 
9
10
  describeWithMongoDB("renameCollection tool", (integration) => {
@@ -4,6 +4,7 @@ import {
4
4
  validateThrowsForInvalidArguments,
5
5
  getResponseContent,
6
6
  } from "../../../helpers.js";
7
+ import { beforeEach, describe, expect, it } from "vitest";
7
8
  import { describeWithMongoDB, validateAutoConnectBehavior } from "../mongodbHelpers.js";
8
9
 
9
10
  describeWithMongoDB("updateMany tool", (integration) => {
@@ -0,0 +1,40 @@
1
+ import { describe, expect, it, beforeAll, afterAll } from "vitest";
2
+ import { StdioClientTransport } from "@modelcontextprotocol/sdk/client/stdio.js";
3
+ import { Client } from "@modelcontextprotocol/sdk/client/index.js";
4
+
5
+ describe("StdioRunner", () => {
6
+ describe("client connects successfully", () => {
7
+ let client: Client;
8
+ let transport: StdioClientTransport;
9
+ beforeAll(async () => {
10
+ transport = new StdioClientTransport({
11
+ command: "node",
12
+ args: ["dist/index.js"],
13
+ env: {
14
+ MDB_MCP_TRANSPORT: "stdio",
15
+ },
16
+ });
17
+ client = new Client({
18
+ name: "test",
19
+ version: "0.0.0",
20
+ });
21
+ await client.connect(transport);
22
+ });
23
+
24
+ afterAll(async () => {
25
+ await client.close();
26
+ await transport.close();
27
+ });
28
+
29
+ it("handles requests and sends responses", async () => {
30
+ const response = await client.listTools();
31
+ expect(response).toBeDefined();
32
+ expect(response.tools).toBeDefined();
33
+ expect(response.tools).toHaveLength(20);
34
+
35
+ const sortedTools = response.tools.sort((a, b) => a.name.localeCompare(b.name));
36
+ expect(sortedTools[0]?.name).toBe("aggregate");
37
+ expect(sortedTools[0]?.description).toBe("Run an aggregation against a MongoDB collection");
38
+ });
39
+ });
40
+ });