mongodb-mcp-server 0.0.4

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 (164) hide show
  1. package/.github/workflows/code_health.yaml +65 -0
  2. package/.github/workflows/prepare_release.yaml +45 -0
  3. package/.github/workflows/publish.yaml +70 -0
  4. package/.prettierignore +5 -0
  5. package/.prettierrc.json +37 -0
  6. package/.vscode/launch.json +17 -0
  7. package/CONTRIBUTING.md +185 -0
  8. package/LICENSE +202 -0
  9. package/README.md +234 -0
  10. package/dist/common/atlas/apiClient.js +147 -0
  11. package/dist/common/atlas/apiClient.js.map +1 -0
  12. package/dist/common/atlas/apiClientError.js +17 -0
  13. package/dist/common/atlas/apiClientError.js.map +1 -0
  14. package/dist/config.js +85 -0
  15. package/dist/config.js.map +1 -0
  16. package/dist/errors.js +12 -0
  17. package/dist/errors.js.map +1 -0
  18. package/dist/index.js +26 -0
  19. package/dist/index.js.map +1 -0
  20. package/dist/logger.js +97 -0
  21. package/dist/logger.js.map +1 -0
  22. package/dist/server.js +45 -0
  23. package/dist/server.js.map +1 -0
  24. package/dist/session.js +30 -0
  25. package/dist/session.js.map +1 -0
  26. package/dist/tools/atlas/atlasTool.js +9 -0
  27. package/dist/tools/atlas/atlasTool.js.map +1 -0
  28. package/dist/tools/atlas/createAccessList.js +64 -0
  29. package/dist/tools/atlas/createAccessList.js.map +1 -0
  30. package/dist/tools/atlas/createDBUser.js +58 -0
  31. package/dist/tools/atlas/createDBUser.js.map +1 -0
  32. package/dist/tools/atlas/createFreeCluster.js +51 -0
  33. package/dist/tools/atlas/createFreeCluster.js.map +1 -0
  34. package/dist/tools/atlas/createProject.js +53 -0
  35. package/dist/tools/atlas/createProject.js.map +1 -0
  36. package/dist/tools/atlas/inspectAccessList.js +41 -0
  37. package/dist/tools/atlas/inspectAccessList.js.map +1 -0
  38. package/dist/tools/atlas/inspectCluster.js +42 -0
  39. package/dist/tools/atlas/inspectCluster.js.map +1 -0
  40. package/dist/tools/atlas/listClusters.js +97 -0
  41. package/dist/tools/atlas/listClusters.js.map +1 -0
  42. package/dist/tools/atlas/listDBUsers.js +52 -0
  43. package/dist/tools/atlas/listDBUsers.js.map +1 -0
  44. package/dist/tools/atlas/listOrgs.js +30 -0
  45. package/dist/tools/atlas/listOrgs.js.map +1 -0
  46. package/dist/tools/atlas/listProjects.js +42 -0
  47. package/dist/tools/atlas/listProjects.js.map +1 -0
  48. package/dist/tools/atlas/tools.js +23 -0
  49. package/dist/tools/atlas/tools.js.map +1 -0
  50. package/dist/tools/mongodb/create/createCollection.js +23 -0
  51. package/dist/tools/mongodb/create/createCollection.js.map +1 -0
  52. package/dist/tools/mongodb/create/createIndex.js +33 -0
  53. package/dist/tools/mongodb/create/createIndex.js.map +1 -0
  54. package/dist/tools/mongodb/create/insertMany.js +33 -0
  55. package/dist/tools/mongodb/create/insertMany.js.map +1 -0
  56. package/dist/tools/mongodb/delete/deleteMany.js +31 -0
  57. package/dist/tools/mongodb/delete/deleteMany.js.map +1 -0
  58. package/dist/tools/mongodb/delete/dropCollection.js +25 -0
  59. package/dist/tools/mongodb/delete/dropCollection.js.map +1 -0
  60. package/dist/tools/mongodb/delete/dropDatabase.js +25 -0
  61. package/dist/tools/mongodb/delete/dropDatabase.js.map +1 -0
  62. package/dist/tools/mongodb/metadata/collectionSchema.js +38 -0
  63. package/dist/tools/mongodb/metadata/collectionSchema.js.map +1 -0
  64. package/dist/tools/mongodb/metadata/collectionStorageSize.js +28 -0
  65. package/dist/tools/mongodb/metadata/collectionStorageSize.js.map +1 -0
  66. package/dist/tools/mongodb/metadata/connect.js +86 -0
  67. package/dist/tools/mongodb/metadata/connect.js.map +1 -0
  68. package/dist/tools/mongodb/metadata/dbStats.js +28 -0
  69. package/dist/tools/mongodb/metadata/dbStats.js.map +1 -0
  70. package/dist/tools/mongodb/metadata/explain.js +77 -0
  71. package/dist/tools/mongodb/metadata/explain.js.map +1 -0
  72. package/dist/tools/mongodb/metadata/listCollections.js +35 -0
  73. package/dist/tools/mongodb/metadata/listCollections.js.map +1 -0
  74. package/dist/tools/mongodb/metadata/listDatabases.js +23 -0
  75. package/dist/tools/mongodb/metadata/listDatabases.js.map +1 -0
  76. package/dist/tools/mongodb/mongodbTool.js +58 -0
  77. package/dist/tools/mongodb/mongodbTool.js.map +1 -0
  78. package/dist/tools/mongodb/read/aggregate.js +38 -0
  79. package/dist/tools/mongodb/read/aggregate.js.map +1 -0
  80. package/dist/tools/mongodb/read/collectionIndexes.js +23 -0
  81. package/dist/tools/mongodb/read/collectionIndexes.js.map +1 -0
  82. package/dist/tools/mongodb/read/count.js +34 -0
  83. package/dist/tools/mongodb/read/count.js.map +1 -0
  84. package/dist/tools/mongodb/read/find.js +51 -0
  85. package/dist/tools/mongodb/read/find.js.map +1 -0
  86. package/dist/tools/mongodb/tools.js +41 -0
  87. package/dist/tools/mongodb/tools.js.map +1 -0
  88. package/dist/tools/mongodb/update/renameCollection.js +31 -0
  89. package/dist/tools/mongodb/update/renameCollection.js.map +1 -0
  90. package/dist/tools/mongodb/update/updateMany.js +56 -0
  91. package/dist/tools/mongodb/update/updateMany.js.map +1 -0
  92. package/dist/tools/tool.js +56 -0
  93. package/dist/tools/tool.js.map +1 -0
  94. package/eslint.config.js +35 -0
  95. package/jest.config.js +22 -0
  96. package/package.json +76 -0
  97. package/scripts/apply.ts +129 -0
  98. package/scripts/filter.ts +67 -0
  99. package/scripts/generate.sh +11 -0
  100. package/src/common/atlas/apiClient.ts +202 -0
  101. package/src/common/atlas/apiClientError.ts +21 -0
  102. package/src/common/atlas/openapi.d.ts +5849 -0
  103. package/src/config.ts +124 -0
  104. package/src/errors.ts +13 -0
  105. package/src/index.ts +30 -0
  106. package/src/logger.ts +117 -0
  107. package/src/server.ts +64 -0
  108. package/src/session.ts +37 -0
  109. package/src/tools/atlas/atlasTool.ts +10 -0
  110. package/src/tools/atlas/createAccessList.ts +78 -0
  111. package/src/tools/atlas/createDBUser.ts +70 -0
  112. package/src/tools/atlas/createFreeCluster.ts +55 -0
  113. package/src/tools/atlas/createProject.ts +63 -0
  114. package/src/tools/atlas/inspectAccessList.ts +44 -0
  115. package/src/tools/atlas/inspectCluster.ts +47 -0
  116. package/src/tools/atlas/listClusters.ts +104 -0
  117. package/src/tools/atlas/listDBUsers.ts +62 -0
  118. package/src/tools/atlas/listOrgs.ts +34 -0
  119. package/src/tools/atlas/listProjects.ts +46 -0
  120. package/src/tools/atlas/tools.ts +23 -0
  121. package/src/tools/mongodb/create/createCollection.ts +26 -0
  122. package/src/tools/mongodb/create/createIndex.ts +41 -0
  123. package/src/tools/mongodb/create/insertMany.ts +40 -0
  124. package/src/tools/mongodb/delete/deleteMany.ts +38 -0
  125. package/src/tools/mongodb/delete/dropCollection.ts +27 -0
  126. package/src/tools/mongodb/delete/dropDatabase.ts +26 -0
  127. package/src/tools/mongodb/metadata/collectionSchema.ts +41 -0
  128. package/src/tools/mongodb/metadata/collectionStorageSize.ts +30 -0
  129. package/src/tools/mongodb/metadata/connect.ts +94 -0
  130. package/src/tools/mongodb/metadata/dbStats.ts +30 -0
  131. package/src/tools/mongodb/metadata/explain.ts +90 -0
  132. package/src/tools/mongodb/metadata/listCollections.ts +38 -0
  133. package/src/tools/mongodb/metadata/listDatabases.ts +26 -0
  134. package/src/tools/mongodb/mongodbTool.ts +69 -0
  135. package/src/tools/mongodb/read/aggregate.ts +45 -0
  136. package/src/tools/mongodb/read/collectionIndexes.ts +24 -0
  137. package/src/tools/mongodb/read/count.ts +39 -0
  138. package/src/tools/mongodb/read/find.ts +62 -0
  139. package/src/tools/mongodb/tools.ts +41 -0
  140. package/src/tools/mongodb/update/renameCollection.ts +37 -0
  141. package/src/tools/mongodb/update/updateMany.ts +65 -0
  142. package/src/tools/tool.ts +90 -0
  143. package/src/types/mongodb-redact.d.ts +4 -0
  144. package/tests/integration/helpers.ts +241 -0
  145. package/tests/integration/inMemoryTransport.ts +58 -0
  146. package/tests/integration/server.test.ts +35 -0
  147. package/tests/integration/tools/atlas/accessLists.test.ts +100 -0
  148. package/tests/integration/tools/atlas/atlasHelpers.ts +110 -0
  149. package/tests/integration/tools/atlas/clusters.test.ts +122 -0
  150. package/tests/integration/tools/atlas/dbUsers.test.ts +80 -0
  151. package/tests/integration/tools/atlas/orgs.test.ts +24 -0
  152. package/tests/integration/tools/atlas/projects.test.ts +80 -0
  153. package/tests/integration/tools/mongodb/create/createCollection.test.ts +138 -0
  154. package/tests/integration/tools/mongodb/create/createIndex.test.ts +249 -0
  155. package/tests/integration/tools/mongodb/create/insertMany.test.ts +141 -0
  156. package/tests/integration/tools/mongodb/delete/deleteMany.test.ts +191 -0
  157. package/tests/integration/tools/mongodb/delete/dropCollection.test.ts +118 -0
  158. package/tests/integration/tools/mongodb/delete/dropDatabase.test.ts +114 -0
  159. package/tests/integration/tools/mongodb/metadata/connect.test.ts +137 -0
  160. package/tests/integration/tools/mongodb/metadata/listCollections.test.ts +104 -0
  161. package/tests/integration/tools/mongodb/metadata/listDatabases.test.ts +67 -0
  162. package/tests/integration/tools/mongodb/read/count.test.ts +138 -0
  163. package/tsconfig.jest.json +10 -0
  164. package/tsconfig.json +19 -0
@@ -0,0 +1,122 @@
1
+ import { Session } from "../../../../src/session.js";
2
+ import { describeAtlas, withProject, sleep, randomId } from "./atlasHelpers.js";
3
+ import { CallToolResult } from "@modelcontextprotocol/sdk/types.js";
4
+
5
+ async function deleteAndWaitCluster(session: Session, projectId: string, clusterName: string) {
6
+ session.ensureAuthenticated();
7
+
8
+ await session.apiClient.deleteCluster({
9
+ params: {
10
+ path: {
11
+ groupId: projectId,
12
+ clusterName: clusterName,
13
+ },
14
+ },
15
+ });
16
+ while (true) {
17
+ try {
18
+ await session.apiClient.getCluster({
19
+ params: {
20
+ path: {
21
+ groupId: projectId,
22
+ clusterName: clusterName,
23
+ },
24
+ },
25
+ });
26
+ await sleep(1000);
27
+ } catch {
28
+ break;
29
+ }
30
+ }
31
+ }
32
+
33
+ describeAtlas("clusters", (integration) => {
34
+ withProject(integration, ({ getProjectId }) => {
35
+ const clusterName = "ClusterTest-" + randomId;
36
+
37
+ afterAll(async () => {
38
+ const projectId = getProjectId();
39
+
40
+ const session: Session = integration.mcpServer().session;
41
+
42
+ await deleteAndWaitCluster(session, projectId, clusterName);
43
+ });
44
+
45
+ describe("atlas-create-free-cluster", () => {
46
+ it("should have correct metadata", async () => {
47
+ const { tools } = await integration.mcpClient().listTools();
48
+ const createFreeCluster = tools.find((tool) => tool.name === "atlas-create-free-cluster")!;
49
+
50
+ expect(createFreeCluster).toBeDefined();
51
+ expect(createFreeCluster.inputSchema.type).toBe("object");
52
+ expect(createFreeCluster.inputSchema.properties).toBeDefined();
53
+ expect(createFreeCluster.inputSchema.properties).toHaveProperty("projectId");
54
+ expect(createFreeCluster.inputSchema.properties).toHaveProperty("name");
55
+ expect(createFreeCluster.inputSchema.properties).toHaveProperty("region");
56
+ });
57
+
58
+ it("should create a free cluster", async () => {
59
+ const projectId = getProjectId();
60
+
61
+ const response = (await integration.mcpClient().callTool({
62
+ name: "atlas-create-free-cluster",
63
+ arguments: {
64
+ projectId,
65
+ name: clusterName,
66
+ region: "US_EAST_1",
67
+ },
68
+ })) as CallToolResult;
69
+ expect(response.content).toBeArray();
70
+ expect(response.content).toHaveLength(1);
71
+ expect(response.content[0].text).toContain("has been created");
72
+ });
73
+ });
74
+
75
+ describe("atlas-inspect-cluster", () => {
76
+ it("should have correct metadata", async () => {
77
+ const { tools } = await integration.mcpClient().listTools();
78
+ const inspectCluster = tools.find((tool) => tool.name === "atlas-inspect-cluster")!;
79
+
80
+ expect(inspectCluster).toBeDefined();
81
+ expect(inspectCluster.inputSchema.type).toBe("object");
82
+ expect(inspectCluster.inputSchema.properties).toBeDefined();
83
+ expect(inspectCluster.inputSchema.properties).toHaveProperty("projectId");
84
+ expect(inspectCluster.inputSchema.properties).toHaveProperty("clusterName");
85
+ });
86
+
87
+ it("returns cluster data", async () => {
88
+ const projectId = getProjectId();
89
+
90
+ const response = (await integration.mcpClient().callTool({
91
+ name: "atlas-inspect-cluster",
92
+ arguments: { projectId, clusterName: clusterName },
93
+ })) as CallToolResult;
94
+ expect(response.content).toBeArray();
95
+ expect(response.content).toHaveLength(1);
96
+ expect(response.content[0].text).toContain(`${clusterName} | `);
97
+ });
98
+ });
99
+
100
+ describe("atlas-list-clusters", () => {
101
+ it("should have correct metadata", async () => {
102
+ const { tools } = await integration.mcpClient().listTools();
103
+ const listClusters = tools.find((tool) => tool.name === "atlas-list-clusters")!;
104
+ expect(listClusters).toBeDefined();
105
+ expect(listClusters.inputSchema.type).toBe("object");
106
+ expect(listClusters.inputSchema.properties).toBeDefined();
107
+ expect(listClusters.inputSchema.properties).toHaveProperty("projectId");
108
+ });
109
+
110
+ it("returns clusters by project", async () => {
111
+ const projectId = getProjectId();
112
+
113
+ const response = (await integration
114
+ .mcpClient()
115
+ .callTool({ name: "atlas-list-clusters", arguments: { projectId } })) as CallToolResult;
116
+ expect(response.content).toBeArray();
117
+ expect(response.content).toHaveLength(2);
118
+ expect(response.content[1].text).toContain(`${clusterName} | `);
119
+ });
120
+ });
121
+ });
122
+ });
@@ -0,0 +1,80 @@
1
+ import { CallToolResult } from "@modelcontextprotocol/sdk/types.js";
2
+ import { Session } from "../../../../src/session.js";
3
+ import { describeAtlas, withProject, randomId } from "./atlasHelpers.js";
4
+
5
+ describeAtlas("db users", (integration) => {
6
+ const userName = "testuser-" + randomId;
7
+ withProject(integration, ({ getProjectId }) => {
8
+ afterAll(async () => {
9
+ const projectId = getProjectId();
10
+
11
+ const session: Session = integration.mcpServer().session;
12
+ session.ensureAuthenticated();
13
+ await session.apiClient.deleteDatabaseUser({
14
+ params: {
15
+ path: {
16
+ groupId: projectId,
17
+ username: userName,
18
+ databaseName: "admin",
19
+ },
20
+ },
21
+ });
22
+ });
23
+
24
+ describe("atlas-create-db-user", () => {
25
+ it("should have correct metadata", async () => {
26
+ const { tools } = await integration.mcpClient().listTools();
27
+ const createDbUser = tools.find((tool) => tool.name === "atlas-create-db-user")!;
28
+ expect(createDbUser).toBeDefined();
29
+ expect(createDbUser.inputSchema.type).toBe("object");
30
+ expect(createDbUser.inputSchema.properties).toBeDefined();
31
+ expect(createDbUser.inputSchema.properties).toHaveProperty("projectId");
32
+ expect(createDbUser.inputSchema.properties).toHaveProperty("username");
33
+ expect(createDbUser.inputSchema.properties).toHaveProperty("password");
34
+ expect(createDbUser.inputSchema.properties).toHaveProperty("roles");
35
+ expect(createDbUser.inputSchema.properties).toHaveProperty("clusters");
36
+ });
37
+ it("should create a database user", async () => {
38
+ const projectId = getProjectId();
39
+
40
+ const response = (await integration.mcpClient().callTool({
41
+ name: "atlas-create-db-user",
42
+ arguments: {
43
+ projectId,
44
+ username: userName,
45
+ password: "testpassword",
46
+ roles: [
47
+ {
48
+ roleName: "readWrite",
49
+ databaseName: "admin",
50
+ },
51
+ ],
52
+ },
53
+ })) as CallToolResult;
54
+ expect(response.content).toBeArray();
55
+ expect(response.content).toHaveLength(1);
56
+ expect(response.content[0].text).toContain("created sucessfully");
57
+ });
58
+ });
59
+ describe("atlas-list-db-users", () => {
60
+ it("should have correct metadata", async () => {
61
+ const { tools } = await integration.mcpClient().listTools();
62
+ const listDbUsers = tools.find((tool) => tool.name === "atlas-list-db-users")!;
63
+ expect(listDbUsers).toBeDefined();
64
+ expect(listDbUsers.inputSchema.type).toBe("object");
65
+ expect(listDbUsers.inputSchema.properties).toBeDefined();
66
+ expect(listDbUsers.inputSchema.properties).toHaveProperty("projectId");
67
+ });
68
+ it("returns database users by project", async () => {
69
+ const projectId = getProjectId();
70
+
71
+ const response = (await integration
72
+ .mcpClient()
73
+ .callTool({ name: "atlas-list-db-users", arguments: { projectId } })) as CallToolResult;
74
+ expect(response.content).toBeArray();
75
+ expect(response.content).toHaveLength(1);
76
+ expect(response.content[0].text).toContain(userName);
77
+ });
78
+ });
79
+ });
80
+ });
@@ -0,0 +1,24 @@
1
+ import { CallToolResult } from "@modelcontextprotocol/sdk/types.js";
2
+ import { setupIntegrationTest } from "../../helpers.js";
3
+ import { parseTable, describeAtlas } from "./atlasHelpers.js";
4
+
5
+ describeAtlas("orgs", (integration) => {
6
+ describe("atlas-list-orgs", () => {
7
+ it("should have correct metadata", async () => {
8
+ const { tools } = await integration.mcpClient().listTools();
9
+ const listOrgs = tools.find((tool) => tool.name === "atlas-list-orgs");
10
+ expect(listOrgs).toBeDefined();
11
+ });
12
+
13
+ it("returns org names", async () => {
14
+ const response = (await integration
15
+ .mcpClient()
16
+ .callTool({ name: "atlas-list-orgs", arguments: {} })) as CallToolResult;
17
+ expect(response.content).toBeArray();
18
+ expect(response.content).toHaveLength(1);
19
+ const data = parseTable(response.content[0].text as string);
20
+ expect(data).toHaveLength(1);
21
+ expect(data[0]["Organization Name"]).toEqual("MongoDB MCP Test");
22
+ });
23
+ });
24
+ });
@@ -0,0 +1,80 @@
1
+ import { CallToolResult } from "@modelcontextprotocol/sdk/types.js";
2
+ import { setupIntegrationTest } from "../../helpers.js";
3
+ import { Session } from "../../../../src/session.js";
4
+ import { ObjectId } from "mongodb";
5
+ import { parseTable, describeAtlas } from "./atlasHelpers.js";
6
+
7
+ const randomId = new ObjectId().toString();
8
+
9
+ describeAtlas("projects", (integration) => {
10
+ const projName = "testProj-" + randomId;
11
+
12
+ afterAll(async () => {
13
+ const session: Session = integration.mcpServer().session;
14
+ session.ensureAuthenticated();
15
+
16
+ const projects = await session.apiClient.listProjects();
17
+ for (const project of projects?.results || []) {
18
+ if (project.name === projName) {
19
+ await session.apiClient.deleteProject({
20
+ params: {
21
+ path: {
22
+ groupId: project.id || "",
23
+ },
24
+ },
25
+ });
26
+ break;
27
+ }
28
+ }
29
+ });
30
+
31
+ describe("atlas-create-project", () => {
32
+ it("should have correct metadata", async () => {
33
+ const { tools } = await integration.mcpClient().listTools();
34
+ const createProject = tools.find((tool) => tool.name === "atlas-create-project")!;
35
+ expect(createProject).toBeDefined();
36
+ expect(createProject.inputSchema.type).toBe("object");
37
+ expect(createProject.inputSchema.properties).toBeDefined();
38
+ expect(createProject.inputSchema.properties).toHaveProperty("projectName");
39
+ expect(createProject.inputSchema.properties).toHaveProperty("organizationId");
40
+ });
41
+ it("should create a project", async () => {
42
+ const response = (await integration.mcpClient().callTool({
43
+ name: "atlas-create-project",
44
+ arguments: { projectName: projName },
45
+ })) as CallToolResult;
46
+ expect(response.content).toBeArray();
47
+ expect(response.content).toHaveLength(1);
48
+ expect(response.content[0].text).toContain(projName);
49
+ });
50
+ });
51
+ describe("atlas-list-projects", () => {
52
+ it("should have correct metadata", async () => {
53
+ const { tools } = await integration.mcpClient().listTools();
54
+ const listProjects = tools.find((tool) => tool.name === "atlas-list-projects")!;
55
+ expect(listProjects).toBeDefined();
56
+ expect(listProjects.inputSchema.type).toBe("object");
57
+ expect(listProjects.inputSchema.properties).toBeDefined();
58
+ expect(listProjects.inputSchema.properties).toHaveProperty("orgId");
59
+ });
60
+
61
+ it("returns project names", async () => {
62
+ const response = (await integration
63
+ .mcpClient()
64
+ .callTool({ name: "atlas-list-projects", arguments: {} })) as CallToolResult;
65
+ expect(response.content).toBeArray();
66
+ expect(response.content).toHaveLength(1);
67
+ expect(response.content[0].text).toContain(projName);
68
+ const data = parseTable(response.content[0].text as string);
69
+ expect(data).toBeArray();
70
+ expect(data.length).toBeGreaterThan(0);
71
+ let found = false;
72
+ for (const project of data) {
73
+ if (project["Project Name"] === projName) {
74
+ found = true;
75
+ }
76
+ }
77
+ expect(found).toBe(true);
78
+ });
79
+ });
80
+ });
@@ -0,0 +1,138 @@
1
+ import {
2
+ getResponseContent,
3
+ validateParameters,
4
+ dbOperationParameters,
5
+ setupIntegrationTest,
6
+ } from "../../../helpers.js";
7
+ import { toIncludeSameMembers } from "jest-extended";
8
+ import { McpError } from "@modelcontextprotocol/sdk/types.js";
9
+ import { ObjectId } from "bson";
10
+ import config from "../../../../../src/config.js";
11
+
12
+ describe("createCollection tool", () => {
13
+ const integration = setupIntegrationTest();
14
+
15
+ it("should have correct metadata", async () => {
16
+ const { tools } = await integration.mcpClient().listTools();
17
+ const listCollections = tools.find((tool) => tool.name === "create-collection")!;
18
+ expect(listCollections).toBeDefined();
19
+ expect(listCollections.description).toBe(
20
+ "Creates a new collection in a database. If the database doesn't exist, it will be created automatically."
21
+ );
22
+
23
+ validateParameters(listCollections, dbOperationParameters);
24
+ });
25
+
26
+ describe("with invalid arguments", () => {
27
+ const args = [
28
+ {},
29
+ { database: 123, collection: "bar" },
30
+ { foo: "bar", database: "test", collection: "bar" },
31
+ { collection: [], database: "test" },
32
+ ];
33
+ for (const arg of args) {
34
+ it(`throws a schema error for: ${JSON.stringify(arg)}`, async () => {
35
+ await integration.connectMcpClient();
36
+ try {
37
+ await integration.mcpClient().callTool({ name: "create-collection", arguments: arg });
38
+ expect.fail("Expected an error to be thrown");
39
+ } catch (error) {
40
+ expect(error).toBeInstanceOf(McpError);
41
+ const mcpError = error as McpError;
42
+ expect(mcpError.code).toEqual(-32602);
43
+ expect(mcpError.message).toContain("Invalid arguments for tool create-collection");
44
+ }
45
+ });
46
+ }
47
+ });
48
+
49
+ describe("with non-existent database", () => {
50
+ it("creates a new collection", async () => {
51
+ const mongoClient = integration.mongoClient();
52
+ let collections = await mongoClient.db(integration.randomDbName()).listCollections().toArray();
53
+ expect(collections).toHaveLength(0);
54
+
55
+ await integration.connectMcpClient();
56
+ const response = await integration.mcpClient().callTool({
57
+ name: "create-collection",
58
+ arguments: { database: integration.randomDbName(), collection: "bar" },
59
+ });
60
+ const content = getResponseContent(response.content);
61
+ expect(content).toEqual(`Collection "bar" created in database "${integration.randomDbName()}".`);
62
+
63
+ collections = await mongoClient.db(integration.randomDbName()).listCollections().toArray();
64
+ expect(collections).toHaveLength(1);
65
+ expect(collections[0].name).toEqual("bar");
66
+ });
67
+ });
68
+
69
+ describe("with existing database", () => {
70
+ it("creates new collection", async () => {
71
+ const mongoClient = integration.mongoClient();
72
+ await mongoClient.db(integration.randomDbName()).createCollection("collection1");
73
+ let collections = await mongoClient.db(integration.randomDbName()).listCollections().toArray();
74
+ expect(collections).toHaveLength(1);
75
+
76
+ await integration.connectMcpClient();
77
+ const response = await integration.mcpClient().callTool({
78
+ name: "create-collection",
79
+ arguments: { database: integration.randomDbName(), collection: "collection2" },
80
+ });
81
+ const content = getResponseContent(response.content);
82
+ expect(content).toEqual(`Collection "collection2" created in database "${integration.randomDbName()}".`);
83
+ collections = await mongoClient.db(integration.randomDbName()).listCollections().toArray();
84
+ expect(collections).toHaveLength(2);
85
+ expect(collections.map((c) => c.name)).toIncludeSameMembers(["collection1", "collection2"]);
86
+ });
87
+
88
+ it("does nothing if collection already exists", async () => {
89
+ const mongoClient = integration.mongoClient();
90
+ await mongoClient.db(integration.randomDbName()).collection("collection1").insertOne({});
91
+ let collections = await mongoClient.db(integration.randomDbName()).listCollections().toArray();
92
+ expect(collections).toHaveLength(1);
93
+ let documents = await mongoClient
94
+ .db(integration.randomDbName())
95
+ .collection("collection1")
96
+ .find({})
97
+ .toArray();
98
+ expect(documents).toHaveLength(1);
99
+
100
+ await integration.connectMcpClient();
101
+ const response = await integration.mcpClient().callTool({
102
+ name: "create-collection",
103
+ arguments: { database: integration.randomDbName(), collection: "collection1" },
104
+ });
105
+ const content = getResponseContent(response.content);
106
+ expect(content).toEqual(`Collection "collection1" created in database "${integration.randomDbName()}".`);
107
+ collections = await mongoClient.db(integration.randomDbName()).listCollections().toArray();
108
+ expect(collections).toHaveLength(1);
109
+ expect(collections[0].name).toEqual("collection1");
110
+
111
+ // Make sure we didn't drop the existing collection
112
+ documents = await mongoClient.db(integration.randomDbName()).collection("collection1").find({}).toArray();
113
+ expect(documents).toHaveLength(1);
114
+ });
115
+ });
116
+
117
+ describe("when not connected", () => {
118
+ it("connects automatically if connection string is configured", async () => {
119
+ config.connectionString = integration.connectionString();
120
+
121
+ const response = await integration.mcpClient().callTool({
122
+ name: "create-collection",
123
+ arguments: { database: integration.randomDbName(), collection: "new-collection" },
124
+ });
125
+ const content = getResponseContent(response.content);
126
+ expect(content).toEqual(`Collection "new-collection" created in database "${integration.randomDbName()}".`);
127
+ });
128
+
129
+ it("throws an error if connection string is not configured", async () => {
130
+ const response = await integration.mcpClient().callTool({
131
+ name: "create-collection",
132
+ arguments: { database: integration.randomDbName(), collection: "new-collection" },
133
+ });
134
+ const content = getResponseContent(response.content);
135
+ expect(content).toContain("You need to connect to a MongoDB instance before you can access its data.");
136
+ });
137
+ });
138
+ });