mongodb-mcp-server 0.1.3 → 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 (267) hide show
  1. package/.github/workflows/accuracy-tests.yml +55 -0
  2. package/.github/workflows/check.yml +1 -1
  3. package/.github/workflows/code_health.yaml +1 -1
  4. package/.github/workflows/code_health_fork.yaml +0 -14
  5. package/.github/workflows/dependabot_pr.yaml +26 -0
  6. package/.github/workflows/jira-issue.yml +72 -0
  7. package/.vscode/extensions.json +1 -1
  8. package/.vscode/launch.json +11 -1
  9. package/.vscode/settings.json +1 -11
  10. package/Dockerfile +1 -0
  11. package/README.md +118 -31
  12. package/dist/common/atlas/accessListUtils.js +36 -0
  13. package/dist/common/atlas/accessListUtils.js.map +1 -0
  14. package/dist/common/atlas/apiClient.js +25 -6
  15. package/dist/common/atlas/apiClient.js.map +1 -1
  16. package/dist/common/atlas/cluster.js +1 -1
  17. package/dist/common/atlas/cluster.js.map +1 -1
  18. package/dist/{config.js → common/config.js} +7 -1
  19. package/dist/common/config.js.map +1 -0
  20. package/dist/common/errors.js.map +1 -0
  21. package/dist/{logger.js → common/logger.js} +20 -18
  22. package/dist/common/logger.js.map +1 -0
  23. package/dist/common/managedTimeout.js +20 -0
  24. package/dist/common/managedTimeout.js.map +1 -0
  25. package/dist/common/packageInfo.js.map +1 -0
  26. package/dist/{session.js → common/session.js} +20 -21
  27. package/dist/common/session.js.map +1 -0
  28. package/dist/common/sessionStore.js +73 -0
  29. package/dist/common/sessionStore.js.map +1 -0
  30. package/dist/helpers/container.js +28 -0
  31. package/dist/helpers/container.js.map +1 -0
  32. package/dist/helpers/generatePassword.js.map +1 -0
  33. package/dist/helpers/indexCheck.js +1 -1
  34. package/dist/helpers/indexCheck.js.map +1 -1
  35. package/dist/index.js +30 -37
  36. package/dist/index.js.map +1 -1
  37. package/dist/server.js +43 -6
  38. package/dist/server.js.map +1 -1
  39. package/dist/telemetry/constants.js +1 -1
  40. package/dist/telemetry/constants.js.map +1 -1
  41. package/dist/telemetry/telemetry.js +28 -21
  42. package/dist/telemetry/telemetry.js.map +1 -1
  43. package/dist/tools/atlas/atlasTool.js +3 -3
  44. package/dist/tools/atlas/atlasTool.js.map +1 -1
  45. package/dist/tools/atlas/connect/connectCluster.js +198 -0
  46. package/dist/tools/atlas/connect/connectCluster.js.map +1 -0
  47. package/dist/tools/atlas/create/createAccessList.js +9 -10
  48. package/dist/tools/atlas/create/createAccessList.js.map +1 -1
  49. package/dist/tools/atlas/create/createDBUser.js +3 -1
  50. package/dist/tools/atlas/create/createDBUser.js.map +1 -1
  51. package/dist/tools/atlas/create/createFreeCluster.js +2 -0
  52. package/dist/tools/atlas/create/createFreeCluster.js.map +1 -1
  53. package/dist/tools/atlas/create/createProject.js.map +1 -1
  54. package/dist/tools/atlas/read/inspectAccessList.js.map +1 -1
  55. package/dist/tools/atlas/read/inspectCluster.js.map +1 -1
  56. package/dist/tools/atlas/read/listAlerts.js.map +1 -1
  57. package/dist/tools/atlas/read/listClusters.js.map +1 -1
  58. package/dist/tools/atlas/read/listDBUsers.js.map +1 -1
  59. package/dist/tools/atlas/read/listOrgs.js.map +1 -1
  60. package/dist/tools/atlas/read/listProjects.js.map +1 -1
  61. package/dist/tools/atlas/tools.js +1 -1
  62. package/dist/tools/atlas/tools.js.map +1 -1
  63. package/dist/tools/mongodb/{metadata → connect}/connect.js +7 -4
  64. package/dist/tools/mongodb/connect/connect.js.map +1 -0
  65. package/dist/tools/mongodb/create/createCollection.js.map +1 -1
  66. package/dist/tools/mongodb/create/createIndex.js +1 -1
  67. package/dist/tools/mongodb/create/createIndex.js.map +1 -1
  68. package/dist/tools/mongodb/create/insertMany.js +1 -1
  69. package/dist/tools/mongodb/create/insertMany.js.map +1 -1
  70. package/dist/tools/mongodb/delete/deleteMany.js +2 -1
  71. package/dist/tools/mongodb/delete/deleteMany.js.map +1 -1
  72. package/dist/tools/mongodb/delete/dropCollection.js.map +1 -1
  73. package/dist/tools/mongodb/delete/dropDatabase.js.map +1 -1
  74. package/dist/tools/mongodb/metadata/collectionSchema.js.map +1 -1
  75. package/dist/tools/mongodb/metadata/collectionStorageSize.js.map +1 -1
  76. package/dist/tools/mongodb/metadata/dbStats.js.map +1 -1
  77. package/dist/tools/mongodb/metadata/explain.js +1 -1
  78. package/dist/tools/mongodb/metadata/explain.js.map +1 -1
  79. package/dist/tools/mongodb/metadata/listCollections.js.map +1 -1
  80. package/dist/tools/mongodb/metadata/listDatabases.js.map +1 -1
  81. package/dist/tools/mongodb/metadata/logs.js.map +1 -1
  82. package/dist/tools/mongodb/mongodbTool.js +37 -10
  83. package/dist/tools/mongodb/mongodbTool.js.map +1 -1
  84. package/dist/tools/mongodb/read/aggregate.js +1 -1
  85. package/dist/tools/mongodb/read/aggregate.js.map +1 -1
  86. package/dist/tools/mongodb/read/collectionIndexes.js.map +1 -1
  87. package/dist/tools/mongodb/read/count.js +2 -1
  88. package/dist/tools/mongodb/read/count.js.map +1 -1
  89. package/dist/tools/mongodb/read/find.js +7 -4
  90. package/dist/tools/mongodb/read/find.js.map +1 -1
  91. package/dist/tools/mongodb/tools.js +1 -1
  92. package/dist/tools/mongodb/tools.js.map +1 -1
  93. package/dist/tools/mongodb/update/renameCollection.js.map +1 -1
  94. package/dist/tools/mongodb/update/updateMany.js +4 -2
  95. package/dist/tools/mongodb/update/updateMany.js.map +1 -1
  96. package/dist/tools/tool.js +8 -5
  97. package/dist/tools/tool.js.map +1 -1
  98. package/dist/transports/base.js +26 -0
  99. package/dist/transports/base.js.map +1 -0
  100. package/dist/{helpers/EJsonTransport.js → transports/stdio.js} +24 -2
  101. package/dist/transports/stdio.js.map +1 -0
  102. package/dist/transports/streamableHttp.js +140 -0
  103. package/dist/transports/streamableHttp.js.map +1 -0
  104. package/eslint.config.js +13 -4
  105. package/package.json +43 -33
  106. package/resources/test-summary-template.html +415 -0
  107. package/scripts/accuracy/generateTestSummary.ts +335 -0
  108. package/scripts/accuracy/runAccuracyTests.sh +45 -0
  109. package/scripts/accuracy/updateAccuracyRunStatus.ts +21 -0
  110. package/src/common/atlas/accessListUtils.ts +54 -0
  111. package/src/common/atlas/apiClient.ts +25 -6
  112. package/src/common/atlas/cluster.ts +1 -1
  113. package/src/{config.ts → common/config.ts} +14 -2
  114. package/src/{logger.ts → common/logger.ts} +21 -23
  115. package/src/common/managedTimeout.ts +27 -0
  116. package/src/{session.ts → common/session.ts} +24 -26
  117. package/src/common/sessionStore.ts +111 -0
  118. package/src/helpers/container.ts +35 -0
  119. package/src/helpers/indexCheck.ts +1 -1
  120. package/src/index.ts +30 -40
  121. package/src/server.ts +54 -10
  122. package/src/telemetry/constants.ts +1 -1
  123. package/src/telemetry/telemetry.ts +34 -26
  124. package/src/telemetry/types.ts +2 -0
  125. package/src/tools/atlas/atlasTool.ts +4 -4
  126. package/src/tools/atlas/connect/connectCluster.ts +259 -0
  127. package/src/tools/atlas/create/createAccessList.ts +15 -13
  128. package/src/tools/atlas/create/createDBUser.ts +5 -3
  129. package/src/tools/atlas/create/createFreeCluster.ts +4 -2
  130. package/src/tools/atlas/create/createProject.ts +2 -2
  131. package/src/tools/atlas/read/inspectAccessList.ts +2 -2
  132. package/src/tools/atlas/read/inspectCluster.ts +2 -2
  133. package/src/tools/atlas/read/listAlerts.ts +2 -2
  134. package/src/tools/atlas/read/listClusters.ts +2 -2
  135. package/src/tools/atlas/read/listDBUsers.ts +2 -2
  136. package/src/tools/atlas/read/listOrgs.ts +2 -2
  137. package/src/tools/atlas/read/listProjects.ts +2 -2
  138. package/src/tools/atlas/tools.ts +1 -1
  139. package/src/tools/mongodb/{metadata → connect}/connect.ts +12 -9
  140. package/src/tools/mongodb/create/createCollection.ts +2 -2
  141. package/src/tools/mongodb/create/createIndex.ts +3 -3
  142. package/src/tools/mongodb/create/insertMany.ts +3 -3
  143. package/src/tools/mongodb/delete/deleteMany.ts +4 -3
  144. package/src/tools/mongodb/delete/dropCollection.ts +2 -2
  145. package/src/tools/mongodb/delete/dropDatabase.ts +2 -2
  146. package/src/tools/mongodb/metadata/collectionSchema.ts +2 -2
  147. package/src/tools/mongodb/metadata/collectionStorageSize.ts +2 -2
  148. package/src/tools/mongodb/metadata/dbStats.ts +2 -2
  149. package/src/tools/mongodb/metadata/explain.ts +3 -3
  150. package/src/tools/mongodb/metadata/listCollections.ts +2 -2
  151. package/src/tools/mongodb/metadata/listDatabases.ts +2 -2
  152. package/src/tools/mongodb/metadata/logs.ts +2 -2
  153. package/src/tools/mongodb/mongodbTool.ts +50 -14
  154. package/src/tools/mongodb/read/aggregate.ts +3 -3
  155. package/src/tools/mongodb/read/collectionIndexes.ts +2 -2
  156. package/src/tools/mongodb/read/count.ts +4 -3
  157. package/src/tools/mongodb/read/find.ts +11 -6
  158. package/src/tools/mongodb/tools.ts +1 -1
  159. package/src/tools/mongodb/update/renameCollection.ts +2 -2
  160. package/src/tools/mongodb/update/updateMany.ts +6 -4
  161. package/src/tools/tool.ts +18 -13
  162. package/src/transports/base.ts +34 -0
  163. package/src/{helpers/EJsonTransport.ts → transports/stdio.ts} +30 -1
  164. package/src/transports/streamableHttp.ts +178 -0
  165. package/tests/accuracy/aggregate.test.ts +27 -0
  166. package/tests/accuracy/collectionIndexes.test.ts +40 -0
  167. package/tests/accuracy/collectionSchema.test.ts +28 -0
  168. package/tests/accuracy/collectionStorageSize.test.ts +41 -0
  169. package/tests/accuracy/count.test.ts +44 -0
  170. package/tests/accuracy/createCollection.test.ts +46 -0
  171. package/tests/accuracy/createIndex.test.ts +37 -0
  172. package/tests/accuracy/dbStats.test.ts +15 -0
  173. package/tests/accuracy/deleteMany.test.ts +44 -0
  174. package/tests/accuracy/dropCollection.test.ts +74 -0
  175. package/tests/accuracy/dropDatabase.test.ts +41 -0
  176. package/tests/accuracy/explain.test.ts +73 -0
  177. package/tests/accuracy/find.test.ts +114 -0
  178. package/tests/accuracy/insertMany.test.ts +48 -0
  179. package/tests/accuracy/listCollections.test.ts +60 -0
  180. package/tests/accuracy/listDatabases.test.ts +31 -0
  181. package/tests/accuracy/logs.test.ts +28 -0
  182. package/tests/accuracy/renameCollection.test.ts +31 -0
  183. package/tests/accuracy/sdk/accuracyResultStorage/diskStorage.ts +189 -0
  184. package/tests/accuracy/sdk/accuracyResultStorage/getAccuracyResultStorage.ts +11 -0
  185. package/tests/accuracy/sdk/accuracyResultStorage/mongodbStorage.ts +151 -0
  186. package/tests/accuracy/sdk/accuracyResultStorage/resultStorage.ts +117 -0
  187. package/tests/accuracy/sdk/accuracyScorer.ts +93 -0
  188. package/tests/accuracy/sdk/accuracyTestingClient.ts +94 -0
  189. package/tests/accuracy/sdk/agent.ts +56 -0
  190. package/tests/accuracy/sdk/constants.ts +26 -0
  191. package/tests/accuracy/sdk/describeAccuracyTests.ts +126 -0
  192. package/tests/accuracy/sdk/gitInfo.ts +7 -0
  193. package/tests/accuracy/sdk/matcher.ts +193 -0
  194. package/tests/accuracy/sdk/models.ts +95 -0
  195. package/tests/accuracy/test-data-dumps/comics.books.json +417 -0
  196. package/tests/accuracy/test-data-dumps/comics.characters.json +402 -0
  197. package/tests/accuracy/test-data-dumps/mflix.movies.json +496 -0
  198. package/tests/accuracy/test-data-dumps/mflix.shows.json +572 -0
  199. package/tests/accuracy/updateMany.test.ts +42 -0
  200. package/tests/integration/helpers.ts +9 -9
  201. package/tests/integration/indexCheck.test.ts +1 -0
  202. package/tests/integration/server.test.ts +1 -0
  203. package/tests/integration/telemetry.test.ts +4 -3
  204. package/tests/integration/tools/atlas/accessLists.test.ts +22 -2
  205. package/tests/integration/tools/atlas/alerts.test.ts +3 -2
  206. package/tests/integration/tools/atlas/atlasHelpers.ts +3 -0
  207. package/tests/integration/tools/atlas/clusters.test.ts +68 -16
  208. package/tests/integration/tools/atlas/dbUsers.test.ts +14 -1
  209. package/tests/integration/tools/atlas/orgs.test.ts +2 -1
  210. package/tests/integration/tools/atlas/projects.test.ts +4 -3
  211. package/tests/integration/tools/mongodb/{metadata → connect}/connect.test.ts +34 -3
  212. package/tests/integration/tools/mongodb/create/createCollection.test.ts +1 -0
  213. package/tests/integration/tools/mongodb/create/createIndex.test.ts +1 -0
  214. package/tests/integration/tools/mongodb/create/insertMany.test.ts +1 -0
  215. package/tests/integration/tools/mongodb/delete/deleteMany.test.ts +1 -0
  216. package/tests/integration/tools/mongodb/delete/dropCollection.test.ts +1 -1
  217. package/tests/integration/tools/mongodb/delete/dropDatabase.test.ts +1 -0
  218. package/tests/integration/tools/mongodb/metadata/collectionSchema.test.ts +1 -0
  219. package/tests/integration/tools/mongodb/metadata/collectionStorageSize.test.ts +1 -0
  220. package/tests/integration/tools/mongodb/metadata/dbStats.test.ts +1 -0
  221. package/tests/integration/tools/mongodb/metadata/explain.test.ts +1 -0
  222. package/tests/integration/tools/mongodb/metadata/listCollections.test.ts +1 -0
  223. package/tests/integration/tools/mongodb/metadata/listDatabases.test.ts +3 -2
  224. package/tests/integration/tools/mongodb/metadata/logs.test.ts +1 -0
  225. package/tests/integration/tools/mongodb/mongodbHelpers.ts +66 -2
  226. package/tests/integration/tools/mongodb/read/aggregate.test.ts +2 -1
  227. package/tests/integration/tools/mongodb/read/collectionIndexes.test.ts +1 -0
  228. package/tests/integration/tools/mongodb/read/count.test.ts +1 -0
  229. package/tests/integration/tools/mongodb/read/find.test.ts +2 -1
  230. package/tests/integration/tools/mongodb/update/renameCollection.test.ts +1 -0
  231. package/tests/integration/tools/mongodb/update/updateMany.test.ts +1 -0
  232. package/tests/integration/transports/stdio.test.ts +40 -0
  233. package/tests/integration/transports/streamableHttp.test.ts +56 -0
  234. package/tests/matchers/toIncludeSameMembers.test.ts +59 -0
  235. package/tests/matchers/toIncludeSameMembers.ts +12 -0
  236. package/tests/setup.ts +7 -0
  237. package/tests/unit/accessListUtils.test.ts +39 -0
  238. package/tests/unit/accuracyScorer.test.ts +390 -0
  239. package/tests/unit/{apiClient.test.ts → common/apiClient.test.ts} +15 -15
  240. package/tests/unit/common/managedTimeout.test.ts +67 -0
  241. package/tests/unit/{session.test.ts → common/session.test.ts} +7 -12
  242. package/tests/unit/{indexCheck.test.ts → helpers/indexCheck.test.ts} +2 -1
  243. package/tests/unit/telemetry.test.ts +52 -42
  244. package/tests/unit/{EJsonTransport.test.ts → transports/stdio.test.ts} +4 -4
  245. package/tests/vitest.d.ts +11 -0
  246. package/tsconfig.json +0 -1
  247. package/{tsconfig.jest.json → tsconfig.test.json} +1 -2
  248. package/vitest.config.ts +41 -0
  249. package/dist/common/atlas/generatePassword.js.map +0 -1
  250. package/dist/config.js.map +0 -1
  251. package/dist/errors.js.map +0 -1
  252. package/dist/helpers/EJsonTransport.js.map +0 -1
  253. package/dist/helpers/packageInfo.js.map +0 -1
  254. package/dist/logger.js.map +0 -1
  255. package/dist/session.js.map +0 -1
  256. package/dist/tools/atlas/metadata/connectCluster.js +0 -100
  257. package/dist/tools/atlas/metadata/connectCluster.js.map +0 -1
  258. package/dist/tools/mongodb/metadata/connect.js.map +0 -1
  259. package/global.d.ts +0 -1
  260. package/jest.config.cjs +0 -22
  261. package/src/tools/atlas/metadata/connectCluster.ts +0 -121
  262. /package/dist/{errors.js → common/errors.js} +0 -0
  263. /package/dist/{helpers → common}/packageInfo.js +0 -0
  264. /package/dist/{common/atlas → helpers}/generatePassword.js +0 -0
  265. /package/src/{errors.ts → common/errors.ts} +0 -0
  266. /package/src/{helpers → common}/packageInfo.ts +0 -0
  267. /package/src/{common/atlas → helpers}/generatePassword.ts +0 -0
@@ -4,11 +4,11 @@ import { ToolArgs, OperationType } from "../../tool.js";
4
4
  import { getSimplifiedSchema } from "mongodb-schema";
5
5
 
6
6
  export class CollectionSchemaTool extends MongoDBToolBase {
7
- protected name = "collection-schema";
7
+ public name = "collection-schema";
8
8
  protected description = "Describe the schema for a collection";
9
9
  protected argsShape = DbOperationArgs;
10
10
 
11
- protected operationType: OperationType = "metadata";
11
+ public operationType: OperationType = "metadata";
12
12
 
13
13
  protected async execute({ database, collection }: ToolArgs<typeof DbOperationArgs>): Promise<CallToolResult> {
14
14
  const provider = await this.ensureConnected();
@@ -3,11 +3,11 @@ import { DbOperationArgs, MongoDBToolBase } from "../mongodbTool.js";
3
3
  import { ToolArgs, OperationType } from "../../tool.js";
4
4
 
5
5
  export class CollectionStorageSizeTool extends MongoDBToolBase {
6
- protected name = "collection-storage-size";
6
+ public name = "collection-storage-size";
7
7
  protected description = "Gets the size of the collection";
8
8
  protected argsShape = DbOperationArgs;
9
9
 
10
- protected operationType: OperationType = "metadata";
10
+ public operationType: OperationType = "metadata";
11
11
 
12
12
  protected async execute({ database, collection }: ToolArgs<typeof DbOperationArgs>): Promise<CallToolResult> {
13
13
  const provider = await this.ensureConnected();
@@ -4,13 +4,13 @@ import { ToolArgs, OperationType } from "../../tool.js";
4
4
  import { EJSON } from "bson";
5
5
 
6
6
  export class DbStatsTool extends MongoDBToolBase {
7
- protected name = "db-stats";
7
+ public name = "db-stats";
8
8
  protected description = "Returns statistics that reflect the use state of a single database";
9
9
  protected argsShape = {
10
10
  database: DbOperationArgs.database,
11
11
  };
12
12
 
13
- protected operationType: OperationType = "metadata";
13
+ public operationType: OperationType = "metadata";
14
14
 
15
15
  protected async execute({ database }: ToolArgs<typeof this.argsShape>): Promise<CallToolResult> {
16
16
  const provider = await this.ensureConnected();
@@ -8,7 +8,7 @@ import { FindArgs } from "../read/find.js";
8
8
  import { CountArgs } from "../read/count.js";
9
9
 
10
10
  export class ExplainTool extends MongoDBToolBase {
11
- protected name = "explain";
11
+ public name = "explain";
12
12
  protected description =
13
13
  "Returns statistics describing the execution of the winning plan chosen by the query optimizer for the evaluated method";
14
14
 
@@ -16,7 +16,7 @@ export class ExplainTool extends MongoDBToolBase {
16
16
  ...DbOperationArgs,
17
17
  method: z
18
18
  .array(
19
- z.union([
19
+ z.discriminatedUnion("name", [
20
20
  z.object({
21
21
  name: z.literal("aggregate"),
22
22
  arguments: z.object(AggregateArgs),
@@ -34,7 +34,7 @@ export class ExplainTool extends MongoDBToolBase {
34
34
  .describe("The method and its arguments to run"),
35
35
  };
36
36
 
37
- protected operationType: OperationType = "metadata";
37
+ public operationType: OperationType = "metadata";
38
38
 
39
39
  static readonly defaultVerbosity = ExplainVerbosity.queryPlanner;
40
40
 
@@ -3,13 +3,13 @@ import { DbOperationArgs, MongoDBToolBase } from "../mongodbTool.js";
3
3
  import { ToolArgs, OperationType } from "../../tool.js";
4
4
 
5
5
  export class ListCollectionsTool extends MongoDBToolBase {
6
- protected name = "list-collections";
6
+ public name = "list-collections";
7
7
  protected description = "List all collections for a given database";
8
8
  protected argsShape = {
9
9
  database: DbOperationArgs.database,
10
10
  };
11
11
 
12
- protected operationType: OperationType = "metadata";
12
+ public operationType: OperationType = "metadata";
13
13
 
14
14
  protected async execute({ database }: ToolArgs<typeof this.argsShape>): Promise<CallToolResult> {
15
15
  const provider = await this.ensureConnected();
@@ -4,10 +4,10 @@ import * as bson from "bson";
4
4
  import { OperationType } from "../../tool.js";
5
5
 
6
6
  export class ListDatabasesTool extends MongoDBToolBase {
7
- protected name = "list-databases";
7
+ public name = "list-databases";
8
8
  protected description = "List all databases for a MongoDB connection";
9
9
  protected argsShape = {};
10
- protected operationType: OperationType = "metadata";
10
+ public operationType: OperationType = "metadata";
11
11
 
12
12
  protected async execute(): Promise<CallToolResult> {
13
13
  const provider = await this.ensureConnected();
@@ -4,7 +4,7 @@ import { ToolArgs, OperationType } from "../../tool.js";
4
4
  import { z } from "zod";
5
5
 
6
6
  export class LogsTool extends MongoDBToolBase {
7
- protected name = "mongodb-logs";
7
+ public name = "mongodb-logs";
8
8
  protected description = "Returns the most recent logged mongod events";
9
9
  protected argsShape = {
10
10
  type: z
@@ -24,7 +24,7 @@ export class LogsTool extends MongoDBToolBase {
24
24
  .describe("The maximum number of log entries to return."),
25
25
  };
26
26
 
27
- protected operationType: OperationType = "metadata";
27
+ public operationType: OperationType = "metadata";
28
28
 
29
29
  protected async execute({ type, limit }: ToolArgs<typeof this.argsShape>): Promise<CallToolResult> {
30
30
  const provider = await this.ensureConnected();
@@ -2,8 +2,9 @@ import { z } from "zod";
2
2
  import { ToolArgs, ToolBase, ToolCategory, TelemetryToolMetadata } from "../tool.js";
3
3
  import { NodeDriverServiceProvider } from "@mongosh/service-provider-node-driver";
4
4
  import { CallToolResult } from "@modelcontextprotocol/sdk/types.js";
5
- import { ErrorCodes, MongoDBError } from "../../errors.js";
6
- import logger, { LogId } from "../../logger.js";
5
+ import { ErrorCodes, MongoDBError } from "../../common/errors.js";
6
+ import logger, { LogId } from "../../common/logger.js";
7
+ import { Server } from "../../server.js";
7
8
 
8
9
  export const DbOperationArgs = {
9
10
  database: z.string().describe("Database name"),
@@ -11,19 +12,29 @@ export const DbOperationArgs = {
11
12
  };
12
13
 
13
14
  export abstract class MongoDBToolBase extends ToolBase {
14
- protected category: ToolCategory = "mongodb";
15
+ private server?: Server;
16
+ public category: ToolCategory = "mongodb";
15
17
 
16
18
  protected async ensureConnected(): Promise<NodeDriverServiceProvider> {
17
- if (!this.session.serviceProvider && this.config.connectionString) {
18
- try {
19
- await this.connectToMongoDB(this.config.connectionString);
20
- } catch (error) {
21
- logger.error(
22
- LogId.mongodbConnectFailure,
23
- "mongodbTool",
24
- `Failed to connect to MongoDB instance using the connection string from the config: ${error as string}`
19
+ if (!this.session.serviceProvider) {
20
+ if (this.session.connectedAtlasCluster) {
21
+ throw new MongoDBError(
22
+ ErrorCodes.NotConnectedToMongoDB,
23
+ `Attempting to connect to Atlas cluster "${this.session.connectedAtlasCluster.clusterName}", try again in a few seconds.`
25
24
  );
26
- throw new MongoDBError(ErrorCodes.MisconfiguredConnectionString, "Not connected to MongoDB.");
25
+ }
26
+
27
+ if (this.config.connectionString) {
28
+ try {
29
+ await this.connectToMongoDB(this.config.connectionString);
30
+ } catch (error) {
31
+ logger.error(
32
+ LogId.mongodbConnectFailure,
33
+ "mongodbTool",
34
+ `Failed to connect to MongoDB instance using the connection string from the config: ${error as string}`
35
+ );
36
+ throw new MongoDBError(ErrorCodes.MisconfiguredConnectionString, "Not connected to MongoDB.");
37
+ }
27
38
  }
28
39
  }
29
40
 
@@ -34,11 +45,28 @@ export abstract class MongoDBToolBase extends ToolBase {
34
45
  return this.session.serviceProvider;
35
46
  }
36
47
 
48
+ public register(server: Server): boolean {
49
+ this.server = server;
50
+ return super.register(server);
51
+ }
52
+
37
53
  protected handleError(
38
54
  error: unknown,
39
55
  args: ToolArgs<typeof this.argsShape>
40
56
  ): Promise<CallToolResult> | CallToolResult {
41
57
  if (error instanceof MongoDBError) {
58
+ const connectTools = this.server?.tools
59
+ .filter((t) => t.operationType === "connect")
60
+ .sort((a, b) => a.category.localeCompare(b.category)); // Sort Altas tools before MongoDB tools
61
+
62
+ // Find the first Atlas connect tool if available and suggest to the LLM to use it.
63
+ // Note: if we ever have multiple Atlas connect tools, we may want to refine this logic to select the most appropriate one.
64
+ const atlasConnectTool = connectTools?.find((t) => t.category === "atlas");
65
+ const llmConnectHint = atlasConnectTool
66
+ ? `Note to LLM: prefer using the "${atlasConnectTool.name}" tool to connect to an Atlas cluster over using a connection string. Make sure to ask the user to specify a cluster name they want to connect to or ask them if they want to use the "list-clusters" tool to list all their clusters. Do not invent cluster names or connection strings unless the user has explicitly specified them. If they've previously connected to MongoDB using MCP, you can ask them if they want to reconnect using the same cluster/connection.`
67
+ : "Note to LLM: do not invent connection strings and explicitly ask the user to provide one. If they have previously connected to MongoDB using MCP, you can ask them if they want to reconnect using the same connection string.";
68
+
69
+ const connectToolsNames = connectTools?.map((t) => `"${t.name}"`).join(", ");
42
70
  switch (error.code) {
43
71
  case ErrorCodes.NotConnectedToMongoDB:
44
72
  return {
@@ -49,7 +77,9 @@ export abstract class MongoDBToolBase extends ToolBase {
49
77
  },
50
78
  {
51
79
  type: "text",
52
- text: "Please use the 'connect' or 'switch-connection' tool to connect to a MongoDB instance.",
80
+ text: connectToolsNames
81
+ ? `Please use one of the following tools: ${connectToolsNames} to connect to a MongoDB instance or update the MCP server configuration to include a connection string. ${llmConnectHint}`
82
+ : "There are no tools available to connect. Please update the configuration to include a connection string and restart the server.",
53
83
  },
54
84
  ],
55
85
  isError: true,
@@ -59,7 +89,13 @@ export abstract class MongoDBToolBase extends ToolBase {
59
89
  content: [
60
90
  {
61
91
  type: "text",
62
- text: "The configured connection string is not valid. Please check the connection string and confirm it points to a valid MongoDB instance. Alternatively, use the 'switch-connection' tool to connect to a different instance.",
92
+ text: "The configured connection string is not valid. Please check the connection string and confirm it points to a valid MongoDB instance.",
93
+ },
94
+ {
95
+ type: "text",
96
+ text: connectTools
97
+ ? `Alternatively, you can use one of the following tools: ${connectToolsNames} to connect to a MongoDB instance. ${llmConnectHint}`
98
+ : "Please update the configuration to use a valid connection string and restart the server.",
63
99
  },
64
100
  ],
65
101
  isError: true,
@@ -6,17 +6,17 @@ import { EJSON } from "bson";
6
6
  import { checkIndexUsage } from "../../../helpers/indexCheck.js";
7
7
 
8
8
  export const AggregateArgs = {
9
- pipeline: z.array(z.record(z.string(), z.unknown())).describe("An array of aggregation stages to execute"),
9
+ pipeline: z.array(z.object({}).passthrough()).describe("An array of aggregation stages to execute"),
10
10
  };
11
11
 
12
12
  export class AggregateTool extends MongoDBToolBase {
13
- protected name = "aggregate";
13
+ public name = "aggregate";
14
14
  protected description = "Run an aggregation against a MongoDB collection";
15
15
  protected argsShape = {
16
16
  ...DbOperationArgs,
17
17
  ...AggregateArgs,
18
18
  };
19
- protected operationType: OperationType = "read";
19
+ public operationType: OperationType = "read";
20
20
 
21
21
  protected async execute({
22
22
  database,
@@ -3,10 +3,10 @@ import { DbOperationArgs, MongoDBToolBase } from "../mongodbTool.js";
3
3
  import { ToolArgs, OperationType } from "../../tool.js";
4
4
 
5
5
  export class CollectionIndexesTool extends MongoDBToolBase {
6
- protected name = "collection-indexes";
6
+ public name = "collection-indexes";
7
7
  protected description = "Describe the indexes for a collection";
8
8
  protected argsShape = DbOperationArgs;
9
- protected operationType: OperationType = "read";
9
+ public operationType: OperationType = "read";
10
10
 
11
11
  protected async execute({ database, collection }: ToolArgs<typeof DbOperationArgs>): Promise<CallToolResult> {
12
12
  const provider = await this.ensureConnected();
@@ -6,7 +6,8 @@ import { checkIndexUsage } from "../../../helpers/indexCheck.js";
6
6
 
7
7
  export const CountArgs = {
8
8
  query: z
9
- .record(z.string(), z.unknown())
9
+ .object({})
10
+ .passthrough()
10
11
  .optional()
11
12
  .describe(
12
13
  "A filter/query parameter. Allows users to filter the documents to count. Matches the syntax of the filter argument of db.collection.count()."
@@ -14,7 +15,7 @@ export const CountArgs = {
14
15
  };
15
16
 
16
17
  export class CountTool extends MongoDBToolBase {
17
- protected name = "count";
18
+ public name = "count";
18
19
  protected description =
19
20
  "Gets the number of documents in a MongoDB collection using db.collection.count() and query as an optional filter parameter";
20
21
  protected argsShape = {
@@ -22,7 +23,7 @@ export class CountTool extends MongoDBToolBase {
22
23
  ...CountArgs,
23
24
  };
24
25
 
25
- protected operationType: OperationType = "read";
26
+ public operationType: OperationType = "read";
26
27
 
27
28
  protected async execute({ database, collection, query }: ToolArgs<typeof this.argsShape>): Promise<CallToolResult> {
28
29
  const provider = await this.ensureConnected();
@@ -8,28 +8,33 @@ import { checkIndexUsage } from "../../../helpers/indexCheck.js";
8
8
 
9
9
  export const FindArgs = {
10
10
  filter: z
11
- .record(z.string(), z.unknown())
11
+ .object({})
12
+ .passthrough()
12
13
  .optional()
13
14
  .describe("The query filter, matching the syntax of the query argument of db.collection.find()"),
14
15
  projection: z
15
- .record(z.string(), z.unknown())
16
+ .object({})
17
+ .passthrough()
16
18
  .optional()
17
19
  .describe("The projection, matching the syntax of the projection argument of db.collection.find()"),
18
20
  limit: z.number().optional().default(10).describe("The maximum number of documents to return"),
19
21
  sort: z
20
- .record(z.string(), z.custom<SortDirection>())
22
+ .object({})
23
+ .catchall(z.custom<SortDirection>())
21
24
  .optional()
22
- .describe("A document, describing the sort order, matching the syntax of the sort argument of cursor.sort()"),
25
+ .describe(
26
+ "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)."
27
+ ),
23
28
  };
24
29
 
25
30
  export class FindTool extends MongoDBToolBase {
26
- protected name = "find";
31
+ public name = "find";
27
32
  protected description = "Run a find query against a MongoDB collection";
28
33
  protected argsShape = {
29
34
  ...DbOperationArgs,
30
35
  ...FindArgs,
31
36
  };
32
- protected operationType: OperationType = "read";
37
+ public operationType: OperationType = "read";
33
38
 
34
39
  protected async execute({
35
40
  database,
@@ -1,4 +1,4 @@
1
- import { ConnectTool } from "./metadata/connect.js";
1
+ import { ConnectTool } from "./connect/connect.js";
2
2
  import { ListCollectionsTool } from "./metadata/listCollections.js";
3
3
  import { CollectionIndexesTool } from "./read/collectionIndexes.js";
4
4
  import { ListDatabasesTool } from "./metadata/listDatabases.js";
@@ -4,14 +4,14 @@ import { DbOperationArgs, MongoDBToolBase } from "../mongodbTool.js";
4
4
  import { ToolArgs, OperationType } from "../../tool.js";
5
5
 
6
6
  export class RenameCollectionTool extends MongoDBToolBase {
7
- protected name = "rename-collection";
7
+ public name = "rename-collection";
8
8
  protected description = "Renames a collection in a MongoDB database";
9
9
  protected argsShape = {
10
10
  ...DbOperationArgs,
11
11
  newName: z.string().describe("The new name for the collection"),
12
12
  dropTarget: z.boolean().optional().default(false).describe("If true, drops the target collection if it exists"),
13
13
  };
14
- protected operationType: OperationType = "update";
14
+ public operationType: OperationType = "update";
15
15
 
16
16
  protected async execute({
17
17
  database,
@@ -5,25 +5,27 @@ import { ToolArgs, OperationType } from "../../tool.js";
5
5
  import { checkIndexUsage } from "../../../helpers/indexCheck.js";
6
6
 
7
7
  export class UpdateManyTool extends MongoDBToolBase {
8
- protected name = "update-many";
8
+ public name = "update-many";
9
9
  protected description = "Updates all documents that match the specified filter for a collection";
10
10
  protected argsShape = {
11
11
  ...DbOperationArgs,
12
12
  filter: z
13
- .record(z.string(), z.unknown())
13
+ .object({})
14
+ .passthrough()
14
15
  .optional()
15
16
  .describe(
16
17
  "The selection criteria for the update, matching the syntax of the filter argument of db.collection.updateOne()"
17
18
  ),
18
19
  update: z
19
- .record(z.string(), z.unknown())
20
+ .object({})
21
+ .passthrough()
20
22
  .describe("An update document describing the modifications to apply using update operator expressions"),
21
23
  upsert: z
22
24
  .boolean()
23
25
  .optional()
24
26
  .describe("Controls whether to insert a new document if no documents match the filter"),
25
27
  };
26
- protected operationType: OperationType = "update";
28
+ public operationType: OperationType = "update";
27
29
 
28
30
  protected async execute({
29
31
  database,
package/src/tools/tool.ts CHANGED
@@ -1,15 +1,16 @@
1
1
  import { z, type ZodRawShape, type ZodNever, AnyZodObject } from "zod";
2
- import type { McpServer, RegisteredTool, ToolCallback } from "@modelcontextprotocol/sdk/server/mcp.js";
2
+ import type { RegisteredTool, ToolCallback } from "@modelcontextprotocol/sdk/server/mcp.js";
3
3
  import type { CallToolResult, ToolAnnotations } from "@modelcontextprotocol/sdk/types.js";
4
- import { Session } from "../session.js";
5
- import logger, { LogId } from "../logger.js";
4
+ import { Session } from "../common/session.js";
5
+ import logger, { LogId } from "../common/logger.js";
6
6
  import { Telemetry } from "../telemetry/telemetry.js";
7
7
  import { type ToolEvent } from "../telemetry/types.js";
8
- import { UserConfig } from "../config.js";
8
+ import { UserConfig } from "../common/config.js";
9
+ import { Server } from "../server.js";
9
10
 
10
11
  export type ToolArgs<Args extends ZodRawShape> = z.objectOutputType<Args, ZodNever>;
11
12
 
12
- export type OperationType = "metadata" | "read" | "create" | "delete" | "update";
13
+ export type OperationType = "metadata" | "read" | "create" | "delete" | "update" | "connect";
13
14
  export type ToolCategory = "mongodb" | "atlas";
14
15
  export type TelemetryToolMetadata = {
15
16
  projectId?: string;
@@ -17,11 +18,11 @@ export type TelemetryToolMetadata = {
17
18
  };
18
19
 
19
20
  export abstract class ToolBase {
20
- protected abstract name: string;
21
+ public abstract name: string;
21
22
 
22
- protected abstract category: ToolCategory;
23
+ public abstract category: ToolCategory;
23
24
 
24
- protected abstract operationType: OperationType;
25
+ public abstract operationType: OperationType;
25
26
 
26
27
  protected abstract description: string;
27
28
 
@@ -36,6 +37,7 @@ export abstract class ToolBase {
36
37
  switch (this.operationType) {
37
38
  case "read":
38
39
  case "metadata":
40
+ case "connect":
39
41
  annotations.readOnlyHint = true;
40
42
  annotations.destructiveHint = false;
41
43
  break;
@@ -63,9 +65,9 @@ export abstract class ToolBase {
63
65
  protected readonly telemetry: Telemetry
64
66
  ) {}
65
67
 
66
- public register(server: McpServer): void {
68
+ public register(server: Server): boolean {
67
69
  if (!this.verifyAllowed()) {
68
- return;
70
+ return false;
69
71
  }
70
72
 
71
73
  const callback: ToolCallback<typeof this.argsShape> = async (...args) => {
@@ -84,14 +86,15 @@ export abstract class ToolBase {
84
86
  }
85
87
  };
86
88
 
87
- server.tool(this.name, this.description, this.argsShape, this.annotations, callback);
89
+ server.mcpServer.tool(this.name, this.description, this.argsShape, this.annotations, callback);
88
90
 
89
91
  // This is very similar to RegisteredTool.update, but without the bugs around the name.
90
92
  // In the upstream update method, the name is captured in the closure and not updated when
91
93
  // the tool name changes. This means that you only get one name update before things end up
92
94
  // in a broken state.
95
+ // See https://github.com/modelcontextprotocol/typescript-sdk/issues/414 for more details.
93
96
  this.update = (updates: { name?: string; description?: string; inputSchema?: AnyZodObject }) => {
94
- const tools = server["_registeredTools"] as { [toolName: string]: RegisteredTool };
97
+ const tools = server.mcpServer["_registeredTools"] as { [toolName: string]: RegisteredTool };
95
98
  const existingTool = tools[this.name];
96
99
 
97
100
  if (!existingTool) {
@@ -118,8 +121,10 @@ export abstract class ToolBase {
118
121
  existingTool.inputSchema = updates.inputSchema;
119
122
  }
120
123
 
121
- server.sendToolListChanged();
124
+ server.mcpServer.sendToolListChanged();
122
125
  };
126
+
127
+ return true;
123
128
  }
124
129
 
125
130
  protected update?: (updates: { name?: string; description?: string; inputSchema?: AnyZodObject }) => void;
@@ -0,0 +1,34 @@
1
+ import { UserConfig } from "../common/config.js";
2
+ import { packageInfo } from "../common/packageInfo.js";
3
+ import { Server } from "../server.js";
4
+ import { Session } from "../common/session.js";
5
+ import { Telemetry } from "../telemetry/telemetry.js";
6
+ import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
7
+
8
+ export abstract class TransportRunnerBase {
9
+ protected setupServer(userConfig: UserConfig): Server {
10
+ const session = new Session({
11
+ apiBaseUrl: userConfig.apiBaseUrl,
12
+ apiClientId: userConfig.apiClientId,
13
+ apiClientSecret: userConfig.apiClientSecret,
14
+ });
15
+
16
+ const telemetry = Telemetry.create(session, userConfig);
17
+
18
+ const mcpServer = new McpServer({
19
+ name: packageInfo.mcpServerName,
20
+ version: packageInfo.version,
21
+ });
22
+
23
+ return new Server({
24
+ mcpServer,
25
+ session,
26
+ telemetry,
27
+ userConfig,
28
+ });
29
+ }
30
+
31
+ abstract start(): Promise<void>;
32
+
33
+ abstract close(): Promise<void>;
34
+ }
@@ -1,6 +1,10 @@
1
+ import logger, { LogId } from "../common/logger.js";
2
+ import { Server } from "../server.js";
3
+ import { TransportRunnerBase } from "./base.js";
1
4
  import { JSONRPCMessage, JSONRPCMessageSchema } from "@modelcontextprotocol/sdk/types.js";
2
5
  import { EJSON } from "bson";
3
6
  import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
7
+ import { UserConfig } from "../common/config.js";
4
8
 
5
9
  // This is almost a copy of ReadBuffer from @modelcontextprotocol/sdk
6
10
  // but it uses EJSON.parse instead of JSON.parse to handle BSON types
@@ -39,9 +43,34 @@ export class EJsonReadBuffer {
39
43
  //
40
44
  // This function creates a StdioServerTransport and replaces the internal readBuffer with EJsonReadBuffer
41
45
  // that uses EJson.parse instead.
42
- export function createEJsonTransport(): StdioServerTransport {
46
+ export function createStdioTransport(): StdioServerTransport {
43
47
  const server = new StdioServerTransport();
44
48
  server["_readBuffer"] = new EJsonReadBuffer();
45
49
 
46
50
  return server;
47
51
  }
52
+
53
+ export class StdioRunner extends TransportRunnerBase {
54
+ private server: Server | undefined;
55
+
56
+ constructor(private userConfig: UserConfig) {
57
+ super();
58
+ }
59
+
60
+ async start() {
61
+ try {
62
+ this.server = this.setupServer(this.userConfig);
63
+
64
+ const transport = createStdioTransport();
65
+
66
+ await this.server.connect(transport);
67
+ } catch (error: unknown) {
68
+ logger.emergency(LogId.serverStartFailure, "server", `Fatal error running server: ${error as string}`);
69
+ process.exit(1);
70
+ }
71
+ }
72
+
73
+ async close(): Promise<void> {
74
+ await this.server?.close();
75
+ }
76
+ }