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,13 +1,12 @@
1
1
  import { z } from "zod";
2
- import { MongoDBToolBase } from "../mongodbTool.js";
2
+ import { DbOperationArgs, MongoDBToolBase } from "../mongodbTool.js";
3
3
  export class RenameCollectionTool extends MongoDBToolBase {
4
4
  constructor() {
5
5
  super(...arguments);
6
6
  this.name = "rename-collection";
7
7
  this.description = "Renames a collection in a MongoDB database";
8
8
  this.argsShape = {
9
- collection: z.string().describe("Collection name"),
10
- database: z.string().describe("Database name"),
9
+ ...DbOperationArgs,
11
10
  newName: z.string().describe("The new name for the collection"),
12
11
  dropTarget: z.boolean().optional().default(false).describe("If true, drops the target collection if it exists"),
13
12
  };
@@ -21,11 +20,36 @@ export class RenameCollectionTool extends MongoDBToolBase {
21
20
  return {
22
21
  content: [
23
22
  {
24
- text: `Collection \`${collection}\` renamed to \`${result.collectionName}\` in database \`${database}\`.`,
23
+ text: `Collection "${collection}" renamed to "${result.collectionName}" in database "${database}".`,
25
24
  type: "text",
26
25
  },
27
26
  ],
28
27
  };
29
28
  }
29
+ handleError(error, args) {
30
+ if (error instanceof Error && "codeName" in error) {
31
+ switch (error.codeName) {
32
+ case "NamespaceNotFound":
33
+ return {
34
+ content: [
35
+ {
36
+ text: `Cannot rename "${args.database}.${args.collection}" because it doesn't exist.`,
37
+ type: "text",
38
+ },
39
+ ],
40
+ };
41
+ case "NamespaceExists":
42
+ return {
43
+ content: [
44
+ {
45
+ text: `Cannot rename "${args.database}.${args.collection}" to "${args.newName}" because the target collection already exists. If you want to overwrite it, set the "dropTarget" argument to true.`,
46
+ type: "text",
47
+ },
48
+ ],
49
+ };
50
+ }
51
+ }
52
+ return super.handleError(error, args);
53
+ }
30
54
  }
31
55
  //# sourceMappingURL=renameCollection.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"renameCollection.js","sourceRoot":"","sources":["../../../../src/tools/mongodb/update/renameCollection.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAExB,OAAO,EAAE,eAAe,EAAE,MAAM,mBAAmB,CAAC;AAGpD,MAAM,OAAO,oBAAqB,SAAQ,eAAe;IAAzD;;QACc,SAAI,GAAG,mBAAmB,CAAC;QAC3B,gBAAW,GAAG,4CAA4C,CAAC;QAC3D,cAAS,GAAG;YAClB,UAAU,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,CAAC,iBAAiB,CAAC;YAClD,QAAQ,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,CAAC,eAAe,CAAC;YAC9C,OAAO,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,CAAC,iCAAiC,CAAC;YAC/D,UAAU,EAAE,CAAC,CAAC,OAAO,EAAE,CAAC,QAAQ,EAAE,CAAC,OAAO,CAAC,KAAK,CAAC,CAAC,QAAQ,CAAC,mDAAmD,CAAC;SAClH,CAAC;QACQ,kBAAa,GAAkB,QAAQ,CAAC;IAsBtD,CAAC;IApBa,KAAK,CAAC,OAAO,CAAC,EACpB,QAAQ,EACR,UAAU,EACV,OAAO,EACP,UAAU,GACoB;QAC9B,MAAM,QAAQ,GAAG,MAAM,IAAI,CAAC,eAAe,EAAE,CAAC;QAC9C,MAAM,MAAM,GAAG,MAAM,QAAQ,CAAC,gBAAgB,CAAC,QAAQ,EAAE,UAAU,EAAE,OAAO,EAAE;YAC1E,UAAU;SACb,CAAC,CAAC;QAEH,OAAO;YACH,OAAO,EAAE;gBACL;oBACI,IAAI,EAAE,gBAAgB,UAAU,mBAAmB,MAAM,CAAC,cAAc,oBAAoB,QAAQ,KAAK;oBACzG,IAAI,EAAE,MAAM;iBACf;aACJ;SACJ,CAAC;IACN,CAAC;CACJ"}
1
+ {"version":3,"file":"renameCollection.js","sourceRoot":"","sources":["../../../../src/tools/mongodb/update/renameCollection.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAExB,OAAO,EAAE,eAAe,EAAE,eAAe,EAAE,MAAM,mBAAmB,CAAC;AAGrE,MAAM,OAAO,oBAAqB,SAAQ,eAAe;IAAzD;;QACc,SAAI,GAAG,mBAAmB,CAAC;QAC3B,gBAAW,GAAG,4CAA4C,CAAC;QAC3D,cAAS,GAAG;YAClB,GAAG,eAAe;YAClB,OAAO,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,CAAC,iCAAiC,CAAC;YAC/D,UAAU,EAAE,CAAC,CAAC,OAAO,EAAE,CAAC,QAAQ,EAAE,CAAC,OAAO,CAAC,KAAK,CAAC,CAAC,QAAQ,CAAC,mDAAmD,CAAC;SAClH,CAAC;QACQ,kBAAa,GAAkB,QAAQ,CAAC;IAoDtD,CAAC;IAlDa,KAAK,CAAC,OAAO,CAAC,EACpB,QAAQ,EACR,UAAU,EACV,OAAO,EACP,UAAU,GACoB;QAC9B,MAAM,QAAQ,GAAG,MAAM,IAAI,CAAC,eAAe,EAAE,CAAC;QAC9C,MAAM,MAAM,GAAG,MAAM,QAAQ,CAAC,gBAAgB,CAAC,QAAQ,EAAE,UAAU,EAAE,OAAO,EAAE;YAC1E,UAAU;SACb,CAAC,CAAC;QAEH,OAAO;YACH,OAAO,EAAE;gBACL;oBACI,IAAI,EAAE,eAAe,UAAU,iBAAiB,MAAM,CAAC,cAAc,kBAAkB,QAAQ,IAAI;oBACnG,IAAI,EAAE,MAAM;iBACf;aACJ;SACJ,CAAC;IACN,CAAC;IAES,WAAW,CACjB,KAAc,EACd,IAAqC;QAErC,IAAI,KAAK,YAAY,KAAK,IAAI,UAAU,IAAI,KAAK,EAAE,CAAC;YAChD,QAAQ,KAAK,CAAC,QAAQ,EAAE,CAAC;gBACrB,KAAK,mBAAmB;oBACpB,OAAO;wBACH,OAAO,EAAE;4BACL;gCACI,IAAI,EAAE,kBAAkB,IAAI,CAAC,QAAQ,IAAI,IAAI,CAAC,UAAU,6BAA6B;gCACrF,IAAI,EAAE,MAAM;6BACf;yBACJ;qBACJ,CAAC;gBACN,KAAK,iBAAiB;oBAClB,OAAO;wBACH,OAAO,EAAE;4BACL;gCACI,IAAI,EAAE,kBAAkB,IAAI,CAAC,QAAQ,IAAI,IAAI,CAAC,UAAU,SAAS,IAAI,CAAC,OAAO,qHAAqH;gCAClM,IAAI,EAAE,MAAM;6BACf;yBACJ;qBACJ,CAAC;YACV,CAAC;QACL,CAAC;QAED,OAAO,KAAK,CAAC,WAAW,CAAC,KAAK,EAAE,IAAI,CAAC,CAAC;IAC1C,CAAC;CACJ"}
@@ -1,22 +1,18 @@
1
1
  import { z } from "zod";
2
- import { MongoDBToolBase } from "../mongodbTool.js";
2
+ import { DbOperationArgs, MongoDBToolBase } from "../mongodbTool.js";
3
3
  export class UpdateManyTool extends MongoDBToolBase {
4
4
  constructor() {
5
5
  super(...arguments);
6
6
  this.name = "update-many";
7
7
  this.description = "Updates all documents that match the specified filter for a collection";
8
8
  this.argsShape = {
9
- collection: z.string().describe("Collection name"),
10
- database: z.string().describe("Database name"),
9
+ ...DbOperationArgs,
11
10
  filter: z
12
- .object({})
13
- .passthrough()
11
+ .record(z.string(), z.unknown())
14
12
  .optional()
15
13
  .describe("The selection criteria for the update, matching the syntax of the filter argument of db.collection.updateOne()"),
16
14
  update: z
17
- .object({})
18
- .passthrough()
19
- .optional()
15
+ .record(z.string(), z.unknown())
20
16
  .describe("An update document describing the modifications to apply using update operator expressions"),
21
17
  upsert: z
22
18
  .boolean()
@@ -31,8 +27,8 @@ export class UpdateManyTool extends MongoDBToolBase {
31
27
  upsert,
32
28
  });
33
29
  let message = "";
34
- if (result.matchedCount === 0) {
35
- message = `No documents matched the filter.`;
30
+ if (result.matchedCount === 0 && result.modifiedCount === 0 && result.upsertedCount === 0) {
31
+ message = "No documents matched the filter.";
36
32
  }
37
33
  else {
38
34
  message = `Matched ${result.matchedCount} document(s).`;
@@ -40,7 +36,7 @@ export class UpdateManyTool extends MongoDBToolBase {
40
36
  message += ` Modified ${result.modifiedCount} document(s).`;
41
37
  }
42
38
  if (result.upsertedCount > 0) {
43
- message += ` Upserted ${result.upsertedCount} document(s) (with id: ${result.upsertedId?.toString()}).`;
39
+ message += ` Upserted ${result.upsertedCount} document with id: ${result.upsertedId?.toString()}.`;
44
40
  }
45
41
  }
46
42
  return {
@@ -1 +1 @@
1
- {"version":3,"file":"updateMany.js","sourceRoot":"","sources":["../../../../src/tools/mongodb/update/updateMany.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAExB,OAAO,EAAE,eAAe,EAAE,MAAM,mBAAmB,CAAC;AAGpD,MAAM,OAAO,cAAe,SAAQ,eAAe;IAAnD;;QACc,SAAI,GAAG,aAAa,CAAC;QACrB,gBAAW,GAAG,wEAAwE,CAAC;QACvF,cAAS,GAAG;YAClB,UAAU,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,CAAC,iBAAiB,CAAC;YAClD,QAAQ,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,CAAC,eAAe,CAAC;YAC9C,MAAM,EAAE,CAAC;iBACJ,MAAM,CAAC,EAAE,CAAC;iBACV,WAAW,EAAE;iBACb,QAAQ,EAAE;iBACV,QAAQ,CACL,gHAAgH,CACnH;YACL,MAAM,EAAE,CAAC;iBACJ,MAAM,CAAC,EAAE,CAAC;iBACV,WAAW,EAAE;iBACb,QAAQ,EAAE;iBACV,QAAQ,CAAC,4FAA4F,CAAC;YAC3G,MAAM,EAAE,CAAC;iBACJ,OAAO,EAAE;iBACT,QAAQ,EAAE;iBACV,QAAQ,CAAC,4EAA4E,CAAC;SAC9F,CAAC;QACQ,kBAAa,GAAkB,QAAQ,CAAC;IAoCtD,CAAC;IAlCa,KAAK,CAAC,OAAO,CAAC,EACpB,QAAQ,EACR,UAAU,EACV,MAAM,EACN,MAAM,EACN,MAAM,GACwB;QAC9B,MAAM,QAAQ,GAAG,MAAM,IAAI,CAAC,eAAe,EAAE,CAAC;QAC9C,MAAM,MAAM,GAAG,MAAM,QAAQ,CAAC,UAAU,CAAC,QAAQ,EAAE,UAAU,EAAE,MAAM,EAAE,MAAM,EAAE;YAC3E,MAAM;SACT,CAAC,CAAC;QAEH,IAAI,OAAO,GAAG,EAAE,CAAC;QACjB,IAAI,MAAM,CAAC,YAAY,KAAK,CAAC,EAAE,CAAC;YAC5B,OAAO,GAAG,kCAAkC,CAAC;QACjD,CAAC;aAAM,CAAC;YACJ,OAAO,GAAG,WAAW,MAAM,CAAC,YAAY,eAAe,CAAC;YACxD,IAAI,MAAM,CAAC,aAAa,GAAG,CAAC,EAAE,CAAC;gBAC3B,OAAO,IAAI,aAAa,MAAM,CAAC,aAAa,eAAe,CAAC;YAChE,CAAC;YACD,IAAI,MAAM,CAAC,aAAa,GAAG,CAAC,EAAE,CAAC;gBAC3B,OAAO,IAAI,aAAa,MAAM,CAAC,aAAa,0BAA0B,MAAM,CAAC,UAAU,EAAE,QAAQ,EAAE,IAAI,CAAC;YAC5G,CAAC;QACL,CAAC;QAED,OAAO;YACH,OAAO,EAAE;gBACL;oBACI,IAAI,EAAE,OAAO;oBACb,IAAI,EAAE,MAAM;iBACf;aACJ;SACJ,CAAC;IACN,CAAC;CACJ"}
1
+ {"version":3,"file":"updateMany.js","sourceRoot":"","sources":["../../../../src/tools/mongodb/update/updateMany.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAExB,OAAO,EAAE,eAAe,EAAE,eAAe,EAAE,MAAM,mBAAmB,CAAC;AAGrE,MAAM,OAAO,cAAe,SAAQ,eAAe;IAAnD;;QACc,SAAI,GAAG,aAAa,CAAC;QACrB,gBAAW,GAAG,wEAAwE,CAAC;QACvF,cAAS,GAAG;YAClB,GAAG,eAAe;YAClB,MAAM,EAAE,CAAC;iBACJ,MAAM,CAAC,CAAC,CAAC,MAAM,EAAE,EAAE,CAAC,CAAC,OAAO,EAAE,CAAC;iBAC/B,QAAQ,EAAE;iBACV,QAAQ,CACL,gHAAgH,CACnH;YACL,MAAM,EAAE,CAAC;iBACJ,MAAM,CAAC,CAAC,CAAC,MAAM,EAAE,EAAE,CAAC,CAAC,OAAO,EAAE,CAAC;iBAC/B,QAAQ,CAAC,4FAA4F,CAAC;YAC3G,MAAM,EAAE,CAAC;iBACJ,OAAO,EAAE;iBACT,QAAQ,EAAE;iBACV,QAAQ,CAAC,4EAA4E,CAAC;SAC9F,CAAC;QACQ,kBAAa,GAAkB,QAAQ,CAAC;IAoCtD,CAAC;IAlCa,KAAK,CAAC,OAAO,CAAC,EACpB,QAAQ,EACR,UAAU,EACV,MAAM,EACN,MAAM,EACN,MAAM,GACwB;QAC9B,MAAM,QAAQ,GAAG,MAAM,IAAI,CAAC,eAAe,EAAE,CAAC;QAC9C,MAAM,MAAM,GAAG,MAAM,QAAQ,CAAC,UAAU,CAAC,QAAQ,EAAE,UAAU,EAAE,MAAM,EAAE,MAAM,EAAE;YAC3E,MAAM;SACT,CAAC,CAAC;QAEH,IAAI,OAAO,GAAG,EAAE,CAAC;QACjB,IAAI,MAAM,CAAC,YAAY,KAAK,CAAC,IAAI,MAAM,CAAC,aAAa,KAAK,CAAC,IAAI,MAAM,CAAC,aAAa,KAAK,CAAC,EAAE,CAAC;YACxF,OAAO,GAAG,kCAAkC,CAAC;QACjD,CAAC;aAAM,CAAC;YACJ,OAAO,GAAG,WAAW,MAAM,CAAC,YAAY,eAAe,CAAC;YACxD,IAAI,MAAM,CAAC,aAAa,GAAG,CAAC,EAAE,CAAC;gBAC3B,OAAO,IAAI,aAAa,MAAM,CAAC,aAAa,eAAe,CAAC;YAChE,CAAC;YACD,IAAI,MAAM,CAAC,aAAa,GAAG,CAAC,EAAE,CAAC;gBAC3B,OAAO,IAAI,aAAa,MAAM,CAAC,aAAa,sBAAsB,MAAM,CAAC,UAAU,EAAE,QAAQ,EAAE,GAAG,CAAC;YACvG,CAAC;QACL,CAAC;QAED,OAAO;YACH,OAAO,EAAE;gBACL;oBACI,IAAI,EAAE,OAAO;oBACb,IAAI,EAAE,MAAM;iBACf;aACJ;SACJ,CAAC;IACN,CAAC;CACJ"}
@@ -1,55 +1,108 @@
1
- import logger from "../logger.js";
2
- import { mongoLogId } from "mongodb-log-writer";
3
- import config from "../config.js";
1
+ import logger, { LogId } from "../logger.js";
4
2
  export class ToolBase {
5
- constructor(session) {
3
+ constructor(session, config, telemetry) {
6
4
  this.session = session;
5
+ this.config = config;
6
+ this.telemetry = telemetry;
7
+ }
8
+ /**
9
+ * Creates and emits a tool telemetry event
10
+ * @param startTime - Start time in milliseconds
11
+ * @param result - Whether the command succeeded or failed
12
+ * @param error - Optional error if the command failed
13
+ */
14
+ async emitToolEvent(startTime, result) {
15
+ const duration = Date.now() - startTime;
16
+ const event = {
17
+ timestamp: new Date().toISOString(),
18
+ source: "mdbmcp",
19
+ properties: {
20
+ ...this.telemetry.getCommonProperties(),
21
+ command: this.name,
22
+ category: this.category,
23
+ component: "tool",
24
+ duration_ms: duration,
25
+ result: result.isError ? "failure" : "success",
26
+ },
27
+ };
28
+ await this.telemetry.emitEvents([event]);
7
29
  }
8
30
  register(server) {
9
31
  if (!this.verifyAllowed()) {
10
32
  return;
11
33
  }
12
34
  const callback = async (...args) => {
35
+ const startTime = Date.now();
13
36
  try {
14
- // TODO: add telemetry here
15
- logger.debug(mongoLogId(1000006), "tool", `Executing ${this.name} with args: ${JSON.stringify(args)}`);
16
- return await this.execute(...args);
37
+ logger.debug(LogId.toolExecute, "tool", `Executing ${this.name} with args: ${JSON.stringify(args)}`);
38
+ const result = await this.execute(...args);
39
+ await this.emitToolEvent(startTime, result);
40
+ return result;
17
41
  }
18
42
  catch (error) {
19
- logger.error(mongoLogId(1000000), "tool", `Error executing ${this.name}: ${error}`);
20
- return await this.handleError(error);
43
+ logger.error(LogId.toolExecuteFailure, "tool", `Error executing ${this.name}: ${error}`);
44
+ const toolResult = await this.handleError(error, args[0]);
45
+ await this.emitToolEvent(startTime, toolResult).catch(() => { });
46
+ return toolResult;
21
47
  }
22
48
  };
23
49
  server.tool(this.name, this.description, this.argsShape, callback);
50
+ // This is very similar to RegisteredTool.update, but without the bugs around the name.
51
+ // In the upstream update method, the name is captured in the closure and not updated when
52
+ // the tool name changes. This means that you only get one name update before things end up
53
+ // in a broken state.
54
+ this.update = (updates) => {
55
+ const tools = server["_registeredTools"];
56
+ const existingTool = tools[this.name];
57
+ if (updates.name && updates.name !== this.name) {
58
+ delete tools[this.name];
59
+ this.name = updates.name;
60
+ tools[this.name] = existingTool;
61
+ }
62
+ if (updates.description) {
63
+ existingTool.description = updates.description;
64
+ this.description = updates.description;
65
+ }
66
+ if (updates.inputSchema) {
67
+ existingTool.inputSchema = updates.inputSchema;
68
+ }
69
+ server.sendToolListChanged();
70
+ };
24
71
  }
25
72
  // Checks if a tool is allowed to run based on the config
26
73
  verifyAllowed() {
27
74
  let errorClarification;
28
- if (config.disabledTools.includes(this.category)) {
75
+ // Check read-only mode first
76
+ if (this.config.readOnly && !["read", "metadata"].includes(this.operationType)) {
77
+ errorClarification = `read-only mode is enabled, its operation type, \`${this.operationType}\`,`;
78
+ }
79
+ else if (this.config.disabledTools.includes(this.category)) {
29
80
  errorClarification = `its category, \`${this.category}\`,`;
30
81
  }
31
- else if (config.disabledTools.includes(this.operationType)) {
82
+ else if (this.config.disabledTools.includes(this.operationType)) {
32
83
  errorClarification = `its operation type, \`${this.operationType}\`,`;
33
84
  }
34
- else if (config.disabledTools.includes(this.name)) {
85
+ else if (this.config.disabledTools.includes(this.name)) {
35
86
  errorClarification = `it`;
36
87
  }
37
88
  if (errorClarification) {
38
- logger.debug(mongoLogId(1000010), "tool", `Prevented registration of ${this.name} because ${errorClarification} is disabled in the config`);
89
+ logger.debug(LogId.toolDisabled, "tool", `Prevented registration of ${this.name} because ${errorClarification} is disabled in the config`);
39
90
  return false;
40
91
  }
41
92
  return true;
42
93
  }
43
94
  // This method is intended to be overridden by subclasses to handle errors
44
- handleError(error) {
95
+ handleError(error,
96
+ // eslint-disable-next-line @typescript-eslint/no-unused-vars
97
+ args) {
45
98
  return {
46
99
  content: [
47
100
  {
48
101
  type: "text",
49
102
  text: `Error running ${this.name}: ${error instanceof Error ? error.message : String(error)}`,
103
+ isError: true,
50
104
  },
51
105
  ],
52
- isError: true,
53
106
  };
54
107
  }
55
108
  }
@@ -1 +1 @@
1
- {"version":3,"file":"tool.js","sourceRoot":"","sources":["../../src/tools/tool.ts"],"names":[],"mappings":"AAIA,OAAO,MAAM,MAAM,cAAc,CAAC;AAClC,OAAO,EAAE,UAAU,EAAE,MAAM,oBAAoB,CAAC;AAChD,OAAO,MAAM,MAAM,cAAc,CAAC;AAOlC,MAAM,OAAgB,QAAQ;IAa1B,YAAgC,OAAgB;QAAhB,YAAO,GAAP,OAAO,CAAS;IAAG,CAAC;IAE7C,QAAQ,CAAC,MAAiB;QAC7B,IAAI,CAAC,IAAI,CAAC,aAAa,EAAE,EAAE,CAAC;YACxB,OAAO;QACX,CAAC;QAED,MAAM,QAAQ,GAAwC,KAAK,EAAE,GAAG,IAAI,EAAE,EAAE;YACpE,IAAI,CAAC;gBACD,2BAA2B;gBAC3B,MAAM,CAAC,KAAK,CACR,UAAU,CAAC,OAAS,CAAC,EACrB,MAAM,EACN,aAAa,IAAI,CAAC,IAAI,eAAe,IAAI,CAAC,SAAS,CAAC,IAAI,CAAC,EAAE,CAC9D,CAAC;gBAEF,OAAO,MAAM,IAAI,CAAC,OAAO,CAAC,GAAG,IAAI,CAAC,CAAC;YACvC,CAAC;YAAC,OAAO,KAAc,EAAE,CAAC;gBACtB,MAAM,CAAC,KAAK,CAAC,UAAU,CAAC,OAAS,CAAC,EAAE,MAAM,EAAE,mBAAmB,IAAI,CAAC,IAAI,KAAK,KAAe,EAAE,CAAC,CAAC;gBAEhG,OAAO,MAAM,IAAI,CAAC,WAAW,CAAC,KAAK,CAAC,CAAC;YACzC,CAAC;QACL,CAAC,CAAC;QAEF,MAAM,CAAC,IAAI,CAAC,IAAI,CAAC,IAAI,EAAE,IAAI,CAAC,WAAW,EAAE,IAAI,CAAC,SAAS,EAAE,QAAQ,CAAC,CAAC;IACvE,CAAC;IAED,yDAAyD;IACjD,aAAa;QACjB,IAAI,kBAAsC,CAAC;QAC3C,IAAI,MAAM,CAAC,aAAa,CAAC,QAAQ,CAAC,IAAI,CAAC,QAAQ,CAAC,EAAE,CAAC;YAC/C,kBAAkB,GAAG,mBAAmB,IAAI,CAAC,QAAQ,KAAK,CAAC;QAC/D,CAAC;aAAM,IAAI,MAAM,CAAC,aAAa,CAAC,QAAQ,CAAC,IAAI,CAAC,aAAa,CAAC,EAAE,CAAC;YAC3D,kBAAkB,GAAG,yBAAyB,IAAI,CAAC,aAAa,KAAK,CAAC;QAC1E,CAAC;aAAM,IAAI,MAAM,CAAC,aAAa,CAAC,QAAQ,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC;YAClD,kBAAkB,GAAG,IAAI,CAAC;QAC9B,CAAC;QAED,IAAI,kBAAkB,EAAE,CAAC;YACrB,MAAM,CAAC,KAAK,CACR,UAAU,CAAC,OAAS,CAAC,EACrB,MAAM,EACN,6BAA6B,IAAI,CAAC,IAAI,YAAY,kBAAkB,4BAA4B,CACnG,CAAC;YAEF,OAAO,KAAK,CAAC;QACjB,CAAC;QAED,OAAO,IAAI,CAAC;IAChB,CAAC;IAED,0EAA0E;IAChE,WAAW,CAAC,KAAc;QAChC,OAAO;YACH,OAAO,EAAE;gBACL;oBACI,IAAI,EAAE,MAAM;oBACZ,IAAI,EAAE,iBAAiB,IAAI,CAAC,IAAI,KAAK,KAAK,YAAY,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,EAAE;iBAChG;aACJ;YACD,OAAO,EAAE,IAAI;SAChB,CAAC;IACN,CAAC;CACJ"}
1
+ {"version":3,"file":"tool.js","sourceRoot":"","sources":["../../src/tools/tool.ts"],"names":[],"mappings":"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"}
package/eslint.config.js CHANGED
@@ -2,18 +2,34 @@ import { defineConfig, globalIgnores } from "eslint/config";
2
2
  import js from "@eslint/js";
3
3
  import globals from "globals";
4
4
  import tseslint from "typescript-eslint";
5
- import eslintConfigPrettier from "eslint-config-prettier/flat";
5
+ import eslintPluginPrettierRecommended from "eslint-plugin-prettier/recommended";
6
+ import jestPlugin from "eslint-plugin-jest";
6
7
 
7
- const files = ["src/**/*.ts", "scripts/**/*.ts", "tests/**/*.test.ts", "eslint.config.js", "jest.config.js"];
8
+ const testFiles = ["tests/**/*.test.ts", "tests/**/*.ts"];
9
+
10
+ const files = [...testFiles, "src/**/*.ts", "scripts/**/*.ts"];
8
11
 
9
12
  export default defineConfig([
10
13
  { files, plugins: { js }, extends: ["js/recommended"] },
11
14
  { files, languageOptions: { globals: globals.node } },
15
+ {
16
+ files: testFiles,
17
+ plugins: {
18
+ jest: jestPlugin,
19
+ },
20
+ languageOptions: {
21
+ globals: {
22
+ ...globals.node,
23
+ ...jestPlugin.environments.globals.globals,
24
+ },
25
+ },
26
+ },
12
27
  tseslint.configs.recommendedTypeChecked,
13
28
  {
29
+ files,
14
30
  languageOptions: {
15
31
  parserOptions: {
16
- projectService: true,
32
+ project: "./tsconfig.lint.json",
17
33
  tsconfigRootDir: import.meta.dirname,
18
34
  },
19
35
  },
@@ -25,11 +41,14 @@ export default defineConfig([
25
41
  "@typescript-eslint/no-non-null-assertion": "error",
26
42
  },
27
43
  },
28
- // Ignore features specific to TypeScript resolved rules
29
- tseslint.config({
30
- // TODO: Configure tests and scripts to work with this.
31
- ignores: ["eslint.config.js", "jest.config.js", "tests/**/*.ts", "scripts/**/*.ts"],
32
- }),
33
- globalIgnores(["node_modules", "dist", "src/common/atlas/openapi.d.ts", "coverage"]),
34
- eslintConfigPrettier,
44
+ globalIgnores([
45
+ "node_modules",
46
+ "dist",
47
+ "src/common/atlas/openapi.d.ts",
48
+ "coverage",
49
+ "global.d.ts",
50
+ "eslint.config.js",
51
+ "jest.config.js",
52
+ ]),
53
+ eslintPluginPrettierRecommended,
35
54
  ]);
package/global.d.ts ADDED
@@ -0,0 +1 @@
1
+ import "jest-extended";
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "mongodb-mcp-server",
3
3
  "description": "MongoDB Model Context Protocol Server",
4
- "version": "0.0.4",
4
+ "version": "0.0.6",
5
5
  "main": "dist/index.js",
6
6
  "author": "MongoDB <info@mongodb.com>",
7
7
  "homepage": "https://github.com/mongodb-js/mongodb-mcp-server",
@@ -34,19 +34,22 @@
34
34
  "devDependencies": {
35
35
  "@eslint/js": "^9.24.0",
36
36
  "@jest/globals": "^29.7.0",
37
- "@modelcontextprotocol/inspector": "^0.8.2",
37
+ "@modelcontextprotocol/inspector": "^0.10.2",
38
38
  "@redocly/cli": "^1.34.2",
39
39
  "@types/jest": "^29.5.14",
40
40
  "@types/node": "^22.14.0",
41
41
  "@types/simple-oauth2": "^5.0.7",
42
42
  "@types/yargs-parser": "^21.0.3",
43
43
  "eslint": "^9.24.0",
44
- "eslint-config-prettier": "^10.1.1",
44
+ "eslint-config-prettier": "^10.1.2",
45
+ "eslint-plugin-jest": "^28.11.0",
46
+ "eslint-plugin-prettier": "^5.2.6",
45
47
  "globals": "^16.0.0",
46
48
  "jest": "^29.7.0",
47
49
  "jest-environment-node": "^29.7.0",
48
50
  "jest-extended": "^4.0.2",
49
51
  "mongodb-runner": "^5.8.2",
52
+ "native-machine-id": "^0.1.0",
50
53
  "openapi-types": "^12.1.3",
51
54
  "openapi-typescript": "^7.6.1",
52
55
  "prettier": "^3.5.3",
@@ -61,6 +64,7 @@
61
64
  "@mongodb-js/devtools-connect": "^3.7.2",
62
65
  "@mongosh/service-provider-node-driver": "^3.6.0",
63
66
  "bson": "^6.10.3",
67
+ "lru-cache": "^11.1.0",
64
68
  "mongodb": "^6.15.0",
65
69
  "mongodb-log-writer": "^2.4.1",
66
70
  "mongodb-redact": "^1.1.6",
package/scripts/apply.ts CHANGED
@@ -9,13 +9,15 @@ function findObjectFromRef<T>(obj: T | OpenAPIV3_1.ReferenceObject, openapi: Ope
9
9
  }
10
10
  const paramParts = ref.split("/");
11
11
  paramParts.shift(); // Remove the first part which is always '#'
12
- let foundObj: any = openapi; // eslint-disable-line @typescript-eslint/no-explicit-any
12
+
13
+ let foundObj: Record<string, unknown> = openapi;
13
14
  while (true) {
14
15
  const part = paramParts.shift();
15
16
  if (!part) {
16
17
  break;
17
18
  }
18
- foundObj = foundObj[part];
19
+
20
+ foundObj = foundObj[part] as Record<string, unknown>;
19
21
  }
20
22
  return foundObj as T;
21
23
  }
@@ -28,7 +30,7 @@ async function main() {
28
30
  process.exit(1);
29
31
  }
30
32
 
31
- const specFile = (await fs.readFile(spec, "utf8")) as string;
33
+ const specFile = await fs.readFile(spec as string, "utf8");
32
34
 
33
35
  const operations: {
34
36
  path: string;
@@ -42,7 +44,7 @@ async function main() {
42
44
  const openapi = JSON.parse(specFile) as OpenAPIV3_1.Document;
43
45
  for (const path in openapi.paths) {
44
46
  for (const method in openapi.paths[path]) {
45
- const operation: OpenAPIV3_1.OperationObject = openapi.paths[path][method];
47
+ const operation = openapi.paths[path][method] as OpenAPIV3_1.OperationObject;
46
48
 
47
49
  if (!operation.operationId || !operation.tags?.length) {
48
50
  continue;
@@ -101,9 +103,9 @@ async function main() {
101
103
  })
102
104
  .join("\n");
103
105
 
104
- const templateFile = (await fs.readFile(file, "utf8")) as string;
106
+ const templateFile = await fs.readFile(file as string, "utf8");
105
107
  const templateLines = templateFile.split("\n");
106
- let outputLines: string[] = [];
108
+ const outputLines: string[] = [];
107
109
  let addLines = true;
108
110
  for (const line of templateLines) {
109
111
  if (line.includes("DO NOT EDIT. This is auto-generated code.")) {
@@ -120,7 +122,7 @@ async function main() {
120
122
  }
121
123
  const output = outputLines.join("\n");
122
124
 
123
- await fs.writeFile(file, output, "utf8");
125
+ await fs.writeFile(file as string, output, "utf8");
124
126
  }
125
127
 
126
128
  main().catch((error) => {
package/scripts/filter.ts CHANGED
@@ -8,6 +8,7 @@ async function readStdin() {
8
8
  reject(err);
9
9
  });
10
10
  process.stdin.on("data", (chunk) => {
11
+ // eslint-disable-next-line @typescript-eslint/restrict-plus-operands
11
12
  data += chunk;
12
13
  });
13
14
  process.stdin.on("end", () => {
@@ -42,8 +43,8 @@ function filterOpenapi(openapi: OpenAPIV3_1.Document): OpenAPIV3_1.Document {
42
43
  for (const path in openapi.paths) {
43
44
  const filteredMethods = {} as OpenAPIV3_1.PathItemObject;
44
45
  for (const method in openapi.paths[path]) {
45
- if (allowedOperations.includes(openapi.paths[path][method].operationId)) {
46
- filteredMethods[method] = openapi.paths[path][method];
46
+ if (allowedOperations.includes((openapi.paths[path][method] as { operationId: string }).operationId)) {
47
+ filteredMethods[method] = openapi.paths[path][method] as OpenAPIV3_1.OperationObject;
47
48
  }
48
49
  }
49
50
  if (Object.keys(filteredMethods).length > 0) {
@@ -1,17 +1,21 @@
1
- import config from "../../config.js";
2
- import createClient, { Client, FetchOptions, Middleware } from "openapi-fetch";
1
+ import createClient, { Client, Middleware } from "openapi-fetch";
2
+ import type { FetchOptions } from "openapi-fetch";
3
3
  import { AccessToken, ClientCredentials } from "simple-oauth2";
4
4
  import { ApiClientError } from "./apiClientError.js";
5
5
  import { paths, operations } from "./openapi.js";
6
+ import { BaseEvent } from "../../telemetry/types.js";
7
+ import { packageInfo } from "../../packageInfo.js";
6
8
 
7
9
  const ATLAS_API_VERSION = "2025-03-12";
8
10
 
11
+ export interface ApiClientCredentials {
12
+ clientId: string;
13
+ clientSecret: string;
14
+ }
15
+
9
16
  export interface ApiClientOptions {
10
- credentials?: {
11
- clientId: string;
12
- clientSecret: string;
13
- };
14
- baseUrl?: string;
17
+ credentials?: ApiClientCredentials;
18
+ baseUrl: string;
15
19
  userAgent?: string;
16
20
  }
17
21
 
@@ -59,13 +63,12 @@ export class ApiClient {
59
63
  },
60
64
  };
61
65
 
62
- constructor(options?: ApiClientOptions) {
66
+ constructor(options: ApiClientOptions) {
63
67
  this.options = {
64
68
  ...options,
65
- baseUrl: options?.baseUrl || "https://cloud.mongodb.com/",
66
69
  userAgent:
67
- options?.userAgent ||
68
- `AtlasMCP/${config.version} (${process.platform}; ${process.arch}; ${process.env.HOSTNAME || "unknown"})`,
70
+ options.userAgent ||
71
+ `AtlasMCP/${packageInfo.version} (${process.platform}; ${process.arch}; ${process.env.HOSTNAME || "unknown"})`,
69
72
  };
70
73
 
71
74
  this.client = createClient<paths>({
@@ -91,6 +94,10 @@ export class ApiClient {
91
94
  this.client.use(this.errorMiddleware);
92
95
  }
93
96
 
97
+ public hasCredentials(): boolean {
98
+ return !!(this.oauth2Client && this.accessToken);
99
+ }
100
+
94
101
  public async getIpInfo(): Promise<{
95
102
  currentIpv4Address: string;
96
103
  }> {
@@ -116,6 +123,32 @@ export class ApiClient {
116
123
  }>;
117
124
  }
118
125
 
126
+ async sendEvents(events: BaseEvent[]): Promise<void> {
127
+ let endpoint = "api/private/unauth/telemetry/events";
128
+ const headers: Record<string, string> = {
129
+ Accept: "application/json",
130
+ "Content-Type": "application/json",
131
+ "User-Agent": this.options.userAgent,
132
+ };
133
+
134
+ const accessToken = await this.getAccessToken();
135
+ if (accessToken) {
136
+ endpoint = "api/private/v1.0/telemetry/events";
137
+ headers["Authorization"] = `Bearer ${accessToken}`;
138
+ }
139
+
140
+ const url = new URL(endpoint, this.options.baseUrl);
141
+ const response = await fetch(url, {
142
+ method: "POST",
143
+ headers,
144
+ body: JSON.stringify(events),
145
+ });
146
+
147
+ if (!response.ok) {
148
+ throw await ApiClientError.fromResponse(response);
149
+ }
150
+ }
151
+
119
152
  // DO NOT EDIT. This is auto-generated code.
120
153
  async listClustersForAllProjects(options?: FetchOptions<operations["listClustersForAllProjects"]>) {
121
154
  const { data } = await this.client.GET("/api/atlas/v2/clusters", options);
package/src/config.ts CHANGED
@@ -2,27 +2,31 @@ import path from "path";
2
2
  import os from "os";
3
3
  import argv from "yargs-parser";
4
4
 
5
- import packageJson from "../package.json" with { type: "json" };
6
5
  import { ReadConcernLevel, ReadPreferenceMode, W } from "mongodb";
7
6
 
7
+ export interface ConnectOptions {
8
+ readConcern: ReadConcernLevel;
9
+ readPreference: ReadPreferenceMode;
10
+ writeConcern: W;
11
+ timeoutMS: number;
12
+ }
13
+
8
14
  // If we decide to support non-string config options, we'll need to extend the mechanism for parsing
9
15
  // env variables.
10
- interface UserConfig {
11
- apiBaseUrl?: string;
16
+ export interface UserConfig {
17
+ apiBaseUrl: string;
12
18
  apiClientId?: string;
13
19
  apiClientSecret?: string;
20
+ telemetry?: "enabled" | "disabled";
14
21
  logPath: string;
15
22
  connectionString?: string;
16
- connectOptions: {
17
- readConcern: ReadConcernLevel;
18
- readPreference: ReadPreferenceMode;
19
- writeConcern: W;
20
- timeoutMS: number;
21
- };
23
+ connectOptions: ConnectOptions;
22
24
  disabledTools: Array<string>;
25
+ readOnly?: boolean;
23
26
  }
24
27
 
25
28
  const defaults: UserConfig = {
29
+ apiBaseUrl: "https://cloud.mongodb.com/",
26
30
  logPath: getLogPath(),
27
31
  connectOptions: {
28
32
  readConcern: "local",
@@ -31,21 +35,16 @@ const defaults: UserConfig = {
31
35
  timeoutMS: 30_000,
32
36
  },
33
37
  disabledTools: [],
38
+ telemetry: "disabled",
39
+ readOnly: false,
34
40
  };
35
41
 
36
- const mergedUserConfig = {
42
+ export const config = {
37
43
  ...defaults,
38
44
  ...getEnvConfig(),
39
45
  ...getCliConfig(),
40
46
  };
41
47
 
42
- const config = {
43
- ...mergedUserConfig,
44
- version: packageJson.version,
45
- };
46
-
47
- export default config;
48
-
49
48
  function getLogPath(): string {
50
49
  const localDataPath =
51
50
  process.platform === "win32"
package/src/errors.ts CHANGED
@@ -1,6 +1,6 @@
1
1
  export enum ErrorCodes {
2
2
  NotConnectedToMongoDB = 1_000_000,
3
- InvalidParams = 1_000_001,
3
+ MisconfiguredConnectionString = 1_000_001,
4
4
  }
5
5
 
6
6
  export class MongoDBError extends Error {
package/src/index.ts CHANGED
@@ -1,30 +1,34 @@
1
1
  #!/usr/bin/env node
2
2
 
3
3
  import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
4
- import logger from "./logger.js";
5
- import { mongoLogId } from "mongodb-log-writer";
4
+ import logger, { LogId } from "./logger.js";
6
5
  import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
7
- import config from "./config.js";
6
+ import { config } from "./config.js";
8
7
  import { Session } from "./session.js";
9
8
  import { Server } from "./server.js";
9
+ import { packageInfo } from "./packageInfo.js";
10
10
 
11
11
  try {
12
- const session = new Session();
12
+ const session = new Session({
13
+ apiBaseUrl: config.apiBaseUrl,
14
+ apiClientId: config.apiClientId,
15
+ apiClientSecret: config.apiClientSecret,
16
+ });
13
17
  const mcpServer = new McpServer({
14
- name: "MongoDB Atlas",
15
- version: config.version,
18
+ name: packageInfo.mcpServerName,
19
+ version: packageInfo.version,
16
20
  });
17
21
 
18
22
  const server = new Server({
19
23
  mcpServer,
20
24
  session,
25
+ userConfig: config,
21
26
  });
22
27
 
23
28
  const transport = new StdioServerTransport();
24
29
 
25
30
  await server.connect(transport);
26
31
  } catch (error: unknown) {
27
- logger.emergency(mongoLogId(1_000_004), "server", `Fatal error running server: ${error as string}`);
28
-
32
+ logger.emergency(LogId.serverStartFailure, "server", `Fatal error running server: ${error as string}`);
29
33
  process.exit(1);
30
34
  }