From ea1a6384e177667a30bd5970a2e92fc14fc2c224 Mon Sep 17 00:00:00 2001 From: Himanshu Singh Date: Wed, 26 Nov 2025 18:04:48 +0100 Subject: [PATCH 1/4] chore: disallow overriding of internal tool impl --- src/server.ts | 41 +++++++++++++++---- src/transports/base.ts | 10 ++--- tests/integration/customTools.test.ts | 8 ++-- .../tools/mongodb/mongodbTool.test.ts | 30 ++++++++++---- 4 files changed, 64 insertions(+), 25 deletions(-) diff --git a/src/server.ts b/src/server.ts index 3c99b376a..3579c347e 100644 --- a/src/server.ts +++ b/src/server.ts @@ -31,11 +31,11 @@ export interface ServerOptions { elicitation: Elicitation; connectionErrorHandler: ConnectionErrorHandler; /** - * Custom tool constructors to register with the server. - * This will override any default tools. You can use both existing and custom tools by using the `mongodb-mcp-server/tools` export. + * Custom tool constructors to register with the server. The tools provided + * here will be registered in addition to the default tools. * * ```ts - * import { AllTools, ToolBase } from "mongodb-mcp-server/tools"; + * import { ToolBase } from "mongodb-mcp-server/tools"; * class CustomTool extends ToolBase { * name = "custom_tool"; * // ... @@ -47,11 +47,11 @@ export interface ServerOptions { * telemetry: myTelemetry, * elicitation: myElicitation, * connectionErrorHandler: myConnectionErrorHandler, - * tools: [...AllTools, CustomTool], + * additionalTools: [CustomTool], * }); * ``` */ - tools?: (new (params: ToolConstructorParams) => ToolBase)[]; + additionalTools?: (new (params: ToolConstructorParams) => ToolBase)[]; } export class Server { @@ -60,7 +60,8 @@ export class Server { private readonly telemetry: Telemetry; public readonly userConfig: UserConfig; public readonly elicitation: Elicitation; - private readonly toolConstructors: (new (params: ToolConstructorParams) => ToolBase)[]; + private readonly internalToolImplementations: (new (params: ToolConstructorParams) => ToolBase)[] = AllTools; + private readonly additionalToolImplementations: (new (params: ToolConstructorParams) => ToolBase)[]; public readonly tools: ToolBase[] = []; public readonly connectionErrorHandler: ConnectionErrorHandler; @@ -80,7 +81,7 @@ export class Server { telemetry, connectionErrorHandler, elicitation, - tools, + additionalTools, }: ServerOptions) { this.startTime = Date.now(); this.session = session; @@ -89,7 +90,7 @@ export class Server { this.userConfig = userConfig; this.elicitation = elicitation; this.connectionErrorHandler = connectionErrorHandler; - this.toolConstructors = tools ?? AllTools; + this.additionalToolImplementations = additionalTools ?? []; } async connect(transport: Transport): Promise { @@ -240,15 +241,37 @@ export class Server { } private registerTools(): void { - for (const toolConstructor of this.toolConstructors) { + const toolImplementations = [ + ...this.internalToolImplementations.map((toolConstructor) => ({ + toolConstructor, + source: "internal", + })), + ...this.additionalToolImplementations.map((toolConstructor) => ({ + toolConstructor, + source: "additional", + })), + ] as const; + const registeredTools: Set = new Set(); + + for (const { source, toolConstructor } of toolImplementations) { const tool = new toolConstructor({ session: this.session, config: this.userConfig, telemetry: this.telemetry, elicitation: this.elicitation, }); + + if (registeredTools.has(tool.name)) { + throw new Error( + source === "internal" + ? `Tool name collision detected for internal tool - '${tool.name}'` + : `Tool name collision detected for additional tool - '${tool.name}'. Cannot register an additional tool with the same name as that of an internal tool.` + ); + } + if (tool.register(this)) { this.tools.push(tool); + registeredTools.add(tool.name); } } } diff --git a/src/transports/base.ts b/src/transports/base.ts index 787df80a8..70e0f02b1 100644 --- a/src/transports/base.ts +++ b/src/transports/base.ts @@ -40,7 +40,7 @@ export type TransportRunnerConfig = { createAtlasLocalClient?: AtlasLocalClientFactoryFn; additionalLoggers?: LoggerBase[]; telemetryProperties?: Partial; - tools?: (new (params: ToolConstructorParams) => ToolBase)[]; + additionalTools?: (new (params: ToolConstructorParams) => ToolBase)[]; /** * Hook which allows library consumers to fetch configuration from external sources (e.g., secrets managers, APIs) * or modify the existing configuration before the session is created. @@ -56,7 +56,7 @@ export abstract class TransportRunnerBase { private readonly connectionErrorHandler: ConnectionErrorHandler; private readonly atlasLocalClient: Promise; private readonly telemetryProperties: Partial; - private readonly tools?: (new (params: ToolConstructorParams) => ToolBase)[]; + private readonly additionalTools?: (new (params: ToolConstructorParams) => ToolBase)[]; private readonly createSessionConfig?: CreateSessionConfigFn; protected constructor({ @@ -66,7 +66,7 @@ export abstract class TransportRunnerBase { createAtlasLocalClient = defaultCreateAtlasLocalClient, additionalLoggers = [], telemetryProperties = {}, - tools, + additionalTools, createSessionConfig, }: TransportRunnerConfig) { this.userConfig = userConfig; @@ -74,7 +74,7 @@ export abstract class TransportRunnerBase { this.connectionErrorHandler = connectionErrorHandler; this.atlasLocalClient = createAtlasLocalClient(); this.telemetryProperties = telemetryProperties; - this.tools = tools; + this.additionalTools = additionalTools; this.createSessionConfig = createSessionConfig; const loggers: LoggerBase[] = [...additionalLoggers]; if (this.userConfig.loggers.includes("stderr")) { @@ -149,7 +149,7 @@ export abstract class TransportRunnerBase { userConfig, connectionErrorHandler: this.connectionErrorHandler, elicitation, - tools: this.tools, + additionalTools: this.additionalTools, }); // We need to create the MCP logger after the server is constructed diff --git a/tests/integration/customTools.test.ts b/tests/integration/customTools.test.ts index 955999d35..09835c9f2 100644 --- a/tests/integration/customTools.test.ts +++ b/tests/integration/customTools.test.ts @@ -8,11 +8,11 @@ import { defaultTestConfig, setupIntegrationTest } from "./helpers.js"; describe("Custom Tools", () => { const { mcpClient, mcpServer } = setupIntegrationTest(() => ({ ...defaultTestConfig }), { serverOptions: { - tools: [CustomGreetingTool, CustomCalculatorTool], + additionalTools: [CustomGreetingTool, CustomCalculatorTool], }, }); - it("should register custom tools instead of default tools", async () => { + it("should register custom tools in addition to default tools", async () => { // Check that custom tools are registered const tools = await mcpClient().listTools(); const customGreetingTool = tools.tools.find((t) => t.name === "custom_greeting"); @@ -21,9 +21,9 @@ describe("Custom Tools", () => { expect(customGreetingTool).toBeDefined(); expect(customCalculatorTool).toBeDefined(); - // Check that default tools are NOT registered since we only provided custom tools + // Check that default tools are also registered const defaultTool = tools.tools.find((t) => t.name === "list-databases"); - expect(defaultTool).toBeUndefined(); + expect(defaultTool).toBeDefined(); }); it("should execute custom tools", async () => { diff --git a/tests/integration/tools/mongodb/mongodbTool.test.ts b/tests/integration/tools/mongodb/mongodbTool.test.ts index 12d374699..1fa129a10 100644 --- a/tests/integration/tools/mongodb/mongodbTool.test.ts +++ b/tests/integration/tools/mongodb/mongodbTool.test.ts @@ -19,7 +19,6 @@ import { setupMongoDBIntegrationTest } from "./mongodbHelpers.js"; import { ErrorCodes } from "../../../../src/common/errors.js"; import { Keychain } from "../../../../src/common/keychain.js"; import { Elicitation } from "../../../../src/elicitation.js"; -import { MongoDbTools } from "../../../../src/tools/mongodb/tools.js"; import { VectorSearchEmbeddingsManager } from "../../../../src/common/search/vectorSearchEmbeddingsManager.js"; const injectedErrorHandler: ConnectionErrorHandler = (error) => { @@ -89,7 +88,7 @@ describe("MongoDBTool implementations", () => { async function cleanupAndStartServer( config: Partial | undefined = {}, - toolConstructors: (new (params: ToolConstructorParams) => ToolBase)[] = [...MongoDbTools, RandomTool], + additionalTools: (new (params: ToolConstructorParams) => ToolBase)[] = [RandomTool], errorHandler: ConnectionErrorHandler | undefined = connectionErrorHandler ): Promise { await cleanup(); @@ -140,7 +139,7 @@ describe("MongoDBTool implementations", () => { mcpServer: internalMcpServer, connectionErrorHandler: errorHandler, elicitation, - tools: toolConstructors, + additionalTools, }); await mcpServer.connect(serverTransport); @@ -237,7 +236,7 @@ describe("MongoDBTool implementations", () => { describe("when MCP is using injected connection error handler", () => { beforeEach(async () => { - await cleanupAndStartServer(defaultTestConfig, [...MongoDbTools, RandomTool], injectedErrorHandler); + await cleanupAndStartServer(defaultTestConfig, [RandomTool], injectedErrorHandler); }); describe("and comes across a MongoDB Error - NotConnectedToMongoDB", () => { @@ -263,7 +262,7 @@ describe("MongoDBTool implementations", () => { // This is a misconfigured connection string await cleanupAndStartServer( { connectionString: "mongodb://localhost:1234" }, - [...MongoDbTools, RandomTool], + [RandomTool], injectedErrorHandler ); const toolResponse = await mcpClient?.callTool({ @@ -287,7 +286,7 @@ describe("MongoDBTool implementations", () => { // This is a misconfigured connection string await cleanupAndStartServer( { connectionString: mdbIntegration.connectionString(), indexCheck: true }, - [...MongoDbTools, RandomTool], + [RandomTool], injectedErrorHandler ); const toolResponse = await mcpClient?.callTool({ @@ -318,11 +317,28 @@ describe("MongoDBTool implementations", () => { injectedErrorHandler ); const tools = await mcpClient?.listTools({}); - expect(tools?.tools).toHaveLength(1); + expect(tools?.tools.find((tool) => tool.name === "Random")).toBeDefined(); expect(tools?.tools.find((tool) => tool.name === "UnusableVoyageTool")).toBeUndefined(); }); }); + describe("when an external tool with same name as that of internal tool is attempted to be registered", () => { + it("server should throw during initialization itself", async () => { + const serverStartPromise = cleanupAndStartServer( + { connectionString: mdbIntegration.connectionString(), indexCheck: true }, + [ + class SimilarRandomTool extends RandomTool { + override name = "find"; + }, + ], + injectedErrorHandler + ); + await expect(serverStartPromise).rejects.toThrow( + "Tool name collision detected for additional tool - 'find'. Cannot register an additional tool with the same name as that of an internal tool." + ); + }); + }); + describe("resolveTelemetryMetadata", () => { it("should return empty metadata when not connected", async () => { await cleanupAndStartServer(); From 776c8e3dd8f34afcae0e05a7ddb761cfb75c1217 Mon Sep 17 00:00:00 2001 From: Himanshu Singh Date: Thu, 27 Nov 2025 13:41:18 +0100 Subject: [PATCH 2/4] chore: updated exports for library --- src/lib.ts | 17 ++++++++++------- 1 file changed, 10 insertions(+), 7 deletions(-) diff --git a/src/lib.ts b/src/lib.ts index babdbde77..3b05eb54f 100644 --- a/src/lib.ts +++ b/src/lib.ts @@ -1,6 +1,7 @@ export { Server, type ServerOptions } from "./server.js"; export { Session, type SessionOptions } from "./common/session.js"; -export { type UserConfig } from "./common/config/userConfig.js"; +export { type UserConfig, UserConfigSchema } from "./common/config/userConfig.js"; +export { createUserConfig as parseCliArgumentsAsUserConfig } from "./common/config/createUserConfig.js"; export { LoggerBase, type LogPayload, type LoggerType, type LogLevel } from "./common/logger.js"; export { StreamableHttpRunner } from "./transports/streamableHttp.js"; export { StdioRunner } from "./transports/stdio.js"; @@ -8,19 +9,21 @@ export { TransportRunnerBase, type TransportRunnerConfig } from "./transports/ba export { ConnectionManager, ConnectionStateConnected, + createMCPConnectionManager, type AnyConnectionState, type ConnectionState, type ConnectionStateDisconnected, type ConnectionStateErrored, type ConnectionManagerFactoryFn, } from "./common/connectionManager.js"; -export type { - ConnectionErrorHandler, - ConnectionErrorHandled, - ConnectionErrorUnhandled, - ConnectionErrorHandlerContext, +export { + connectionErrorHandler, + type ConnectionErrorHandler, + type ConnectionErrorHandled, + type ConnectionErrorUnhandled, + type ConnectionErrorHandlerContext, } from "./common/connectionErrorHandler.js"; -export { ErrorCodes } from "./common/errors.js"; +export { ErrorCodes, MongoDBError } from "./common/errors.js"; export { Telemetry } from "./telemetry/telemetry.js"; export { Keychain, registerGlobalSecretToRedact } from "./common/keychain.js"; export type { Secret } from "./common/keychain.js"; From 9cc5526f2592b1054eff793a357ccd0af084c9a9 Mon Sep 17 00:00:00 2001 From: Himanshu Singh Date: Thu, 27 Nov 2025 15:45:22 +0100 Subject: [PATCH 3/4] chore: make name, category, operationType static To allow library consumers to make use of existing tool names, category and operationType, we are marking the name, category and operationType properties of a Tool as static so that library consumers can simply import AllTools from mongodb-mcp-server/tools export and make decision based on these static properties. --- src/server.ts | 11 +++-- src/tools/atlas/atlasTool.ts | 2 +- src/tools/atlas/connect/connectCluster.ts | 4 +- src/tools/atlas/create/createAccessList.ts | 4 +- src/tools/atlas/create/createDBUser.ts | 4 +- src/tools/atlas/create/createFreeCluster.ts | 4 +- src/tools/atlas/create/createProject.ts | 4 +- src/tools/atlas/read/getPerformanceAdvisor.ts | 4 +- src/tools/atlas/read/inspectAccessList.ts | 4 +- src/tools/atlas/read/inspectCluster.ts | 4 +- src/tools/atlas/read/listAlerts.ts | 4 +- src/tools/atlas/read/listClusters.ts | 4 +- src/tools/atlas/read/listDBUsers.ts | 4 +- src/tools/atlas/read/listOrgs.ts | 4 +- src/tools/atlas/read/listProjects.ts | 4 +- src/tools/atlasLocal/atlasLocalTool.ts | 2 +- .../atlasLocal/connect/connectDeployment.ts | 4 +- .../atlasLocal/create/createDeployment.ts | 4 +- .../atlasLocal/delete/deleteDeployment.ts | 4 +- src/tools/atlasLocal/read/listDeployments.ts | 4 +- src/tools/mongodb/connect/connect.ts | 12 +++--- src/tools/mongodb/connect/switchConnection.ts | 12 +++--- src/tools/mongodb/create/createCollection.ts | 4 +- src/tools/mongodb/create/createIndex.ts | 4 +- src/tools/mongodb/create/insertMany.ts | 4 +- src/tools/mongodb/delete/deleteMany.ts | 4 +- src/tools/mongodb/delete/dropCollection.ts | 4 +- src/tools/mongodb/delete/dropDatabase.ts | 4 +- src/tools/mongodb/delete/dropIndex.ts | 4 +- .../mongodb/metadata/collectionIndexes.ts | 4 +- .../mongodb/metadata/collectionSchema.ts | 4 +- .../mongodb/metadata/collectionStorageSize.ts | 4 +- src/tools/mongodb/metadata/dbStats.ts | 4 +- src/tools/mongodb/metadata/explain.ts | 4 +- src/tools/mongodb/metadata/listCollections.ts | 4 +- src/tools/mongodb/metadata/listDatabases.ts | 4 +- src/tools/mongodb/metadata/logs.ts | 4 +- src/tools/mongodb/mongodbTool.ts | 2 +- src/tools/mongodb/read/aggregate.ts | 4 +- src/tools/mongodb/read/count.ts | 4 +- src/tools/mongodb/read/export.ts | 4 +- src/tools/mongodb/read/find.ts | 4 +- src/tools/mongodb/update/renameCollection.ts | 4 +- src/tools/mongodb/update/updateMany.ts | 4 +- src/tools/tool.ts | 42 +++++++++++++++++-- src/transports/base.ts | 6 +-- tests/integration/customTools.test.ts | 12 +++--- .../tools/mongodb/mongodbTool.test.ts | 12 +++--- tests/unit/toolBase.test.ts | 9 ++-- 49 files changed, 157 insertions(+), 117 deletions(-) diff --git a/src/server.ts b/src/server.ts index 3579c347e..5d1dc790a 100644 --- a/src/server.ts +++ b/src/server.ts @@ -16,7 +16,7 @@ import { UnsubscribeRequestSchema, } from "@modelcontextprotocol/sdk/types.js"; import assert from "assert"; -import type { ToolBase, ToolCategory, ToolConstructorParams } from "./tools/tool.js"; +import type { ToolBase, ToolCategory, ToolClass } from "./tools/tool.js"; import { validateConnectionString } from "./helpers/connectionOptions.js"; import { packageInfo } from "./common/packageInfo.js"; import { type ConnectionErrorHandler } from "./common/connectionErrorHandler.js"; @@ -51,7 +51,7 @@ export interface ServerOptions { * }); * ``` */ - additionalTools?: (new (params: ToolConstructorParams) => ToolBase)[]; + additionalTools?: ToolClass[]; } export class Server { @@ -60,8 +60,8 @@ export class Server { private readonly telemetry: Telemetry; public readonly userConfig: UserConfig; public readonly elicitation: Elicitation; - private readonly internalToolImplementations: (new (params: ToolConstructorParams) => ToolBase)[] = AllTools; - private readonly additionalToolImplementations: (new (params: ToolConstructorParams) => ToolBase)[]; + private readonly internalToolImplementations: ToolClass[] = AllTools; + private readonly additionalToolImplementations: ToolClass[]; public readonly tools: ToolBase[] = []; public readonly connectionErrorHandler: ConnectionErrorHandler; @@ -255,6 +255,9 @@ export class Server { for (const { source, toolConstructor } of toolImplementations) { const tool = new toolConstructor({ + name: toolConstructor.toolName, + category: toolConstructor.category, + operationType: toolConstructor.operationType, session: this.session, config: this.userConfig, telemetry: this.telemetry, diff --git a/src/tools/atlas/atlasTool.ts b/src/tools/atlas/atlasTool.ts index f1d6ee4e0..da7488d57 100644 --- a/src/tools/atlas/atlasTool.ts +++ b/src/tools/atlas/atlasTool.ts @@ -7,7 +7,7 @@ import { z } from "zod"; import { ApiClientError } from "../../common/atlas/apiClientError.js"; export abstract class AtlasToolBase extends ToolBase { - public category: ToolCategory = "atlas"; + static category: ToolCategory = "atlas"; protected verifyAllowed(): boolean { if (!this.config.apiClientId || !this.config.apiClientSecret) { diff --git a/src/tools/atlas/connect/connectCluster.ts b/src/tools/atlas/connect/connectCluster.ts index 96a0a51c3..7ab3fec57 100644 --- a/src/tools/atlas/connect/connectCluster.ts +++ b/src/tools/atlas/connect/connectCluster.ts @@ -30,9 +30,9 @@ export const ConnectClusterArgs = { }; export class ConnectClusterTool extends AtlasToolBase { - public name = "atlas-connect-cluster"; + static toolName = "atlas-connect-cluster"; protected description = "Connect to MongoDB Atlas cluster"; - public operationType: OperationType = "connect"; + static operationType: OperationType = "connect"; protected argsShape = { ...ConnectClusterArgs, }; diff --git a/src/tools/atlas/create/createAccessList.ts b/src/tools/atlas/create/createAccessList.ts index 2bf1649b9..2586f2d58 100644 --- a/src/tools/atlas/create/createAccessList.ts +++ b/src/tools/atlas/create/createAccessList.ts @@ -17,9 +17,9 @@ export const CreateAccessListArgs = { }; export class CreateAccessListTool extends AtlasToolBase { - public name = "atlas-create-access-list"; + static toolName = "atlas-create-access-list"; protected description = "Allow Ip/CIDR ranges to access your MongoDB Atlas clusters."; - public operationType: OperationType = "create"; + static operationType: OperationType = "create"; protected argsShape = { ...CreateAccessListArgs, }; diff --git a/src/tools/atlas/create/createDBUser.ts b/src/tools/atlas/create/createDBUser.ts index c8e8ea014..2620267f2 100644 --- a/src/tools/atlas/create/createDBUser.ts +++ b/src/tools/atlas/create/createDBUser.ts @@ -34,9 +34,9 @@ export const CreateDBUserArgs = { }; export class CreateDBUserTool extends AtlasToolBase { - public name = "atlas-create-db-user"; + static toolName = "atlas-create-db-user"; protected description = "Create an MongoDB Atlas database user"; - public operationType: OperationType = "create"; + static operationType: OperationType = "create"; protected argsShape = { ...CreateDBUserArgs, }; diff --git a/src/tools/atlas/create/createFreeCluster.ts b/src/tools/atlas/create/createFreeCluster.ts index 6b1ac98eb..30b22f9fc 100644 --- a/src/tools/atlas/create/createFreeCluster.ts +++ b/src/tools/atlas/create/createFreeCluster.ts @@ -6,9 +6,9 @@ import { ensureCurrentIpInAccessList } from "../../../common/atlas/accessListUti import { AtlasArgs } from "../../args.js"; export class CreateFreeClusterTool extends AtlasToolBase { - public name = "atlas-create-free-cluster"; + static toolName = "atlas-create-free-cluster"; protected description = "Create a free MongoDB Atlas cluster"; - public operationType: OperationType = "create"; + static operationType: OperationType = "create"; protected argsShape = { projectId: AtlasArgs.projectId().describe("Atlas project ID to create the cluster in"), name: AtlasArgs.clusterName().describe("Name of the cluster"), diff --git a/src/tools/atlas/create/createProject.ts b/src/tools/atlas/create/createProject.ts index 3ce9f0259..ac95fca0e 100644 --- a/src/tools/atlas/create/createProject.ts +++ b/src/tools/atlas/create/createProject.ts @@ -5,9 +5,9 @@ import type { Group } from "../../../common/atlas/openapi.js"; import { AtlasArgs } from "../../args.js"; export class CreateProjectTool extends AtlasToolBase { - public name = "atlas-create-project"; + static toolName = "atlas-create-project"; protected description = "Create a MongoDB Atlas project"; - public operationType: OperationType = "create"; + static operationType: OperationType = "create"; protected argsShape = { projectName: AtlasArgs.projectName().optional().describe("Name for the new project"), organizationId: AtlasArgs.organizationId().optional().describe("Organization ID for the new project"), diff --git a/src/tools/atlas/read/getPerformanceAdvisor.ts b/src/tools/atlas/read/getPerformanceAdvisor.ts index f62450106..87574330e 100644 --- a/src/tools/atlas/read/getPerformanceAdvisor.ts +++ b/src/tools/atlas/read/getPerformanceAdvisor.ts @@ -24,9 +24,9 @@ const PerformanceAdvisorOperationType = z.enum([ ]); export class GetPerformanceAdvisorTool extends AtlasToolBase { - public name = "atlas-get-performance-advisor"; + static toolName = "atlas-get-performance-advisor"; protected description = `Get MongoDB Atlas performance advisor recommendations, which includes the operations: suggested indexes, drop index suggestions, schema suggestions, and a sample of the most recent (max ${DEFAULT_SLOW_QUERY_LOGS_LIMIT}) slow query logs`; - public operationType: OperationType = "read"; + static operationType: OperationType = "read"; protected argsShape = { projectId: AtlasArgs.projectId().describe( "Atlas project ID to get performance advisor recommendations. The project ID is a hexadecimal identifier of 24 characters. If the user has only specified the name, use the `atlas-list-projects` tool to retrieve the user's projects with their ids." diff --git a/src/tools/atlas/read/inspectAccessList.ts b/src/tools/atlas/read/inspectAccessList.ts index 7db73e7b2..7e95774ff 100644 --- a/src/tools/atlas/read/inspectAccessList.ts +++ b/src/tools/atlas/read/inspectAccessList.ts @@ -8,9 +8,9 @@ export const InspectAccessListArgs = { }; export class InspectAccessListTool extends AtlasToolBase { - public name = "atlas-inspect-access-list"; + static toolName = "atlas-inspect-access-list"; protected description = "Inspect Ip/CIDR ranges with access to your MongoDB Atlas clusters."; - public operationType: OperationType = "read"; + static operationType: OperationType = "read"; protected argsShape = { ...InspectAccessListArgs, }; diff --git a/src/tools/atlas/read/inspectCluster.ts b/src/tools/atlas/read/inspectCluster.ts index fd8806105..57aafc615 100644 --- a/src/tools/atlas/read/inspectCluster.ts +++ b/src/tools/atlas/read/inspectCluster.ts @@ -11,9 +11,9 @@ export const InspectClusterArgs = { }; export class InspectClusterTool extends AtlasToolBase { - public name = "atlas-inspect-cluster"; + static toolName = "atlas-inspect-cluster"; protected description = "Inspect MongoDB Atlas cluster"; - public operationType: OperationType = "read"; + static operationType: OperationType = "read"; protected argsShape = { ...InspectClusterArgs, }; diff --git a/src/tools/atlas/read/listAlerts.ts b/src/tools/atlas/read/listAlerts.ts index 1e3a6998e..3f383bb1e 100644 --- a/src/tools/atlas/read/listAlerts.ts +++ b/src/tools/atlas/read/listAlerts.ts @@ -8,9 +8,9 @@ export const ListAlertsArgs = { }; export class ListAlertsTool extends AtlasToolBase { - public name = "atlas-list-alerts"; + static toolName = "atlas-list-alerts"; protected description = "List MongoDB Atlas alerts"; - public operationType: OperationType = "read"; + static operationType: OperationType = "read"; protected argsShape = { ...ListAlertsArgs, }; diff --git a/src/tools/atlas/read/listClusters.ts b/src/tools/atlas/read/listClusters.ts index 1d1e3656a..11a92600b 100644 --- a/src/tools/atlas/read/listClusters.ts +++ b/src/tools/atlas/read/listClusters.ts @@ -16,9 +16,9 @@ export const ListClustersArgs = { }; export class ListClustersTool extends AtlasToolBase { - public name = "atlas-list-clusters"; + static toolName = "atlas-list-clusters"; protected description = "List MongoDB Atlas clusters"; - public operationType: OperationType = "read"; + static operationType: OperationType = "read"; protected argsShape = { ...ListClustersArgs, }; diff --git a/src/tools/atlas/read/listDBUsers.ts b/src/tools/atlas/read/listDBUsers.ts index 7103f2664..e1e890593 100644 --- a/src/tools/atlas/read/listDBUsers.ts +++ b/src/tools/atlas/read/listDBUsers.ts @@ -9,9 +9,9 @@ export const ListDBUsersArgs = { }; export class ListDBUsersTool extends AtlasToolBase { - public name = "atlas-list-db-users"; + static toolName = "atlas-list-db-users"; protected description = "List MongoDB Atlas database users"; - public operationType: OperationType = "read"; + static operationType: OperationType = "read"; protected argsShape = { ...ListDBUsersArgs, }; diff --git a/src/tools/atlas/read/listOrgs.ts b/src/tools/atlas/read/listOrgs.ts index f8bb3200b..5dfe52661 100644 --- a/src/tools/atlas/read/listOrgs.ts +++ b/src/tools/atlas/read/listOrgs.ts @@ -4,9 +4,9 @@ import type { OperationType } from "../../tool.js"; import { formatUntrustedData } from "../../tool.js"; export class ListOrganizationsTool extends AtlasToolBase { - public name = "atlas-list-orgs"; + static toolName = "atlas-list-orgs"; protected description = "List MongoDB Atlas organizations"; - public operationType: OperationType = "read"; + static operationType: OperationType = "read"; protected argsShape = {}; protected async execute(): Promise { diff --git a/src/tools/atlas/read/listProjects.ts b/src/tools/atlas/read/listProjects.ts index 5ee2ae8d6..20facb5ea 100644 --- a/src/tools/atlas/read/listProjects.ts +++ b/src/tools/atlas/read/listProjects.ts @@ -6,9 +6,9 @@ import type { ToolArgs } from "../../tool.js"; import { AtlasArgs } from "../../args.js"; export class ListProjectsTool extends AtlasToolBase { - public name = "atlas-list-projects"; + static toolName = "atlas-list-projects"; protected description = "List MongoDB Atlas projects"; - public operationType: OperationType = "read"; + static operationType: OperationType = "read"; protected argsShape = { orgId: AtlasArgs.organizationId() .describe("Atlas organization ID to filter projects. If not provided, projects for all orgs are returned.") diff --git a/src/tools/atlasLocal/atlasLocalTool.ts b/src/tools/atlasLocal/atlasLocalTool.ts index 67b668726..c7ef7b12c 100644 --- a/src/tools/atlasLocal/atlasLocalTool.ts +++ b/src/tools/atlasLocal/atlasLocalTool.ts @@ -9,7 +9,7 @@ import type { ConnectionMetadata } from "../../telemetry/types.js"; export const AtlasLocalToolMetadataDeploymentIdKey = "deploymentId"; export abstract class AtlasLocalToolBase extends ToolBase { - public category: ToolCategory = "atlas-local"; + static category: ToolCategory = "atlas-local"; protected verifyAllowed(): boolean { return this.session.atlasLocalClient !== undefined && super.verifyAllowed(); diff --git a/src/tools/atlasLocal/connect/connectDeployment.ts b/src/tools/atlasLocal/connect/connectDeployment.ts index 7bf8db4bb..33d2500ee 100644 --- a/src/tools/atlasLocal/connect/connectDeployment.ts +++ b/src/tools/atlasLocal/connect/connectDeployment.ts @@ -6,9 +6,9 @@ import { CommonArgs } from "../../args.js"; import type { ConnectionMetadata } from "../../../telemetry/types.js"; export class ConnectDeploymentTool extends AtlasLocalToolBase { - public name = "atlas-local-connect-deployment"; + static toolName = "atlas-local-connect-deployment"; protected description = "Connect to a MongoDB Atlas Local deployment"; - public operationType: OperationType = "connect"; + static operationType: OperationType = "connect"; protected argsShape = { deploymentName: CommonArgs.string().describe("Name of the deployment to connect to"), }; diff --git a/src/tools/atlasLocal/create/createDeployment.ts b/src/tools/atlasLocal/create/createDeployment.ts index 54f28e8af..fb51b311d 100644 --- a/src/tools/atlasLocal/create/createDeployment.ts +++ b/src/tools/atlasLocal/create/createDeployment.ts @@ -5,9 +5,9 @@ import type { Client, CreateDeploymentOptions } from "@mongodb-js/atlas-local"; import { CommonArgs } from "../../args.js"; export class CreateDeploymentTool extends AtlasLocalToolBase { - public name = "atlas-local-create-deployment"; + static toolName = "atlas-local-create-deployment"; protected description = "Create a MongoDB Atlas local deployment"; - public operationType: OperationType = "create"; + static operationType: OperationType = "create"; protected argsShape = { deploymentName: CommonArgs.string().describe("Name of the deployment to create").optional(), }; diff --git a/src/tools/atlasLocal/delete/deleteDeployment.ts b/src/tools/atlasLocal/delete/deleteDeployment.ts index 669a1ab05..15e83bb2a 100644 --- a/src/tools/atlasLocal/delete/deleteDeployment.ts +++ b/src/tools/atlasLocal/delete/deleteDeployment.ts @@ -5,9 +5,9 @@ import type { Client } from "@mongodb-js/atlas-local"; import { CommonArgs } from "../../args.js"; export class DeleteDeploymentTool extends AtlasLocalToolBase { - public name = "atlas-local-delete-deployment"; + static toolName = "atlas-local-delete-deployment"; protected description = "Delete a MongoDB Atlas local deployment"; - public operationType: OperationType = "delete"; + static operationType: OperationType = "delete"; protected argsShape = { deploymentName: CommonArgs.string().describe("Name of the deployment to delete"), }; diff --git a/src/tools/atlasLocal/read/listDeployments.ts b/src/tools/atlasLocal/read/listDeployments.ts index 32a541174..86a6af55d 100644 --- a/src/tools/atlasLocal/read/listDeployments.ts +++ b/src/tools/atlasLocal/read/listDeployments.ts @@ -6,9 +6,9 @@ import type { Deployment } from "@mongodb-js/atlas-local"; import type { Client } from "@mongodb-js/atlas-local"; export class ListDeploymentsTool extends AtlasLocalToolBase { - public name = "atlas-local-list-deployments"; + static toolName = "atlas-local-list-deployments"; protected description = "List MongoDB Atlas local deployments"; - public operationType: OperationType = "read"; + static operationType: OperationType = "read"; protected argsShape = {}; protected async executeWithAtlasLocalClient(client: Client): Promise { diff --git a/src/tools/mongodb/connect/connect.ts b/src/tools/mongodb/connect/connect.ts index fd5dc0caf..011bb3654 100644 --- a/src/tools/mongodb/connect/connect.ts +++ b/src/tools/mongodb/connect/connect.ts @@ -4,7 +4,7 @@ import { MongoDBToolBase } from "../mongodbTool.js"; import type { ToolArgs, OperationType, ToolConstructorParams } from "../../tool.js"; import type { Server } from "../../../server.js"; export class ConnectTool extends MongoDBToolBase { - public override name = "connect"; + static toolName = "connect"; protected override description = "Connect to a MongoDB instance. The config resource captures if the server is already connected to a MongoDB cluster. If the user has configured a connection string or has previously called the connect tool, a connection is already established and there's no need to call this tool unless the user has explicitly requested to switch to a new MongoDB cluster."; @@ -14,15 +14,15 @@ export class ConnectTool extends MongoDBToolBase { connectionString: z.string().describe("MongoDB connection string (in the mongodb:// or mongodb+srv:// format)"), }; - public override operationType: OperationType = "connect"; + static operationType: OperationType = "connect"; - constructor({ session, config, telemetry, elicitation }: ToolConstructorParams) { - super({ session, config, telemetry, elicitation }); - session.on("connect", () => { + constructor(params: ToolConstructorParams) { + super(params); + params.session.on("connect", () => { this.disable(); }); - session.on("disconnect", () => { + params.session.on("disconnect", () => { this.enable(); }); } diff --git a/src/tools/mongodb/connect/switchConnection.ts b/src/tools/mongodb/connect/switchConnection.ts index 53ce500c2..7f1cfefe8 100644 --- a/src/tools/mongodb/connect/switchConnection.ts +++ b/src/tools/mongodb/connect/switchConnection.ts @@ -6,7 +6,7 @@ import { type ToolArgs, type OperationType, type ToolConstructorParams } from ". import type { Server } from "../../../server.js"; export class SwitchConnectionTool extends MongoDBToolBase { - public override name = "switch-connection"; + static toolName = "switch-connection"; protected override description = "Switch to a different MongoDB connection. If the user has configured a connection string or has previously called the connect tool, a connection is already established and there's no need to call this tool unless the user has explicitly requested to switch to a new instance."; @@ -19,15 +19,15 @@ export class SwitchConnectionTool extends MongoDBToolBase { ), }; - public override operationType: OperationType = "connect"; + static operationType: OperationType = "connect"; - constructor({ session, config, telemetry, elicitation }: ToolConstructorParams) { - super({ session, config, telemetry, elicitation }); - session.on("connect", () => { + constructor(params: ToolConstructorParams) { + super(params); + params.session.on("connect", () => { this.enable(); }); - session.on("disconnect", () => { + params.session.on("disconnect", () => { this.disable(); }); } diff --git a/src/tools/mongodb/create/createCollection.ts b/src/tools/mongodb/create/createCollection.ts index 22f9336f6..aaa5f4f2d 100644 --- a/src/tools/mongodb/create/createCollection.ts +++ b/src/tools/mongodb/create/createCollection.ts @@ -3,12 +3,12 @@ import { DbOperationArgs, MongoDBToolBase } from "../mongodbTool.js"; import type { OperationType, ToolArgs } from "../../tool.js"; export class CreateCollectionTool extends MongoDBToolBase { - public name = "create-collection"; + static toolName = "create-collection"; protected description = "Creates a new collection in a database. If the database doesn't exist, it will be created automatically."; protected argsShape = DbOperationArgs; - public operationType: OperationType = "create"; + static operationType: OperationType = "create"; protected async execute({ collection, database }: ToolArgs): Promise { const provider = await this.ensureConnected(); diff --git a/src/tools/mongodb/create/createIndex.ts b/src/tools/mongodb/create/createIndex.ts index d0aca2469..638e33835 100644 --- a/src/tools/mongodb/create/createIndex.ts +++ b/src/tools/mongodb/create/createIndex.ts @@ -64,7 +64,7 @@ export class CreateIndexTool extends MongoDBToolBase { ), }); - public name = "create-index"; + static toolName = "create-index"; protected description = "Create an index for a collection"; protected argsShape = { ...DbOperationArgs, @@ -84,7 +84,7 @@ export class CreateIndexTool extends MongoDBToolBase { ), }; - public operationType: OperationType = "create"; + static operationType: OperationType = "create"; protected async execute({ database, diff --git a/src/tools/mongodb/create/insertMany.ts b/src/tools/mongodb/create/insertMany.ts index 61c24303e..88e44e1b2 100644 --- a/src/tools/mongodb/create/insertMany.ts +++ b/src/tools/mongodb/create/insertMany.ts @@ -16,7 +16,7 @@ const zSupportedEmbeddingParametersWithInput = zSupportedEmbeddingParameters.ext }); export class InsertManyTool extends MongoDBToolBase { - public name = "insert-many"; + static toolName = "insert-many"; protected description = "Insert an array of documents into a MongoDB collection"; protected argsShape = { ...DbOperationArgs, @@ -35,7 +35,7 @@ export class InsertManyTool extends MongoDBToolBase { } : {}), }; - public operationType: OperationType = "create"; + static operationType: OperationType = "create"; protected async execute({ database, diff --git a/src/tools/mongodb/delete/deleteMany.ts b/src/tools/mongodb/delete/deleteMany.ts index 835cbb4ab..dddc91a58 100644 --- a/src/tools/mongodb/delete/deleteMany.ts +++ b/src/tools/mongodb/delete/deleteMany.ts @@ -6,7 +6,7 @@ import { EJSON } from "bson"; import { zEJSON } from "../../args.js"; export class DeleteManyTool extends MongoDBToolBase { - public name = "delete-many"; + static toolName = "delete-many"; protected description = "Removes all documents that match the filter from a MongoDB collection"; protected argsShape = { ...DbOperationArgs, @@ -16,7 +16,7 @@ export class DeleteManyTool extends MongoDBToolBase { "The query filter, specifying the deletion criteria. Matches the syntax of the filter argument of db.collection.deleteMany()" ), }; - public operationType: OperationType = "delete"; + static operationType: OperationType = "delete"; protected async execute({ database, diff --git a/src/tools/mongodb/delete/dropCollection.ts b/src/tools/mongodb/delete/dropCollection.ts index 50bd008a7..8d5bfe785 100644 --- a/src/tools/mongodb/delete/dropCollection.ts +++ b/src/tools/mongodb/delete/dropCollection.ts @@ -3,13 +3,13 @@ import { DbOperationArgs, MongoDBToolBase } from "../mongodbTool.js"; import type { ToolArgs, OperationType } from "../../tool.js"; export class DropCollectionTool extends MongoDBToolBase { - public name = "drop-collection"; + static toolName = "drop-collection"; protected description = "Removes a collection or view from the database. The method also removes any indexes associated with the dropped collection."; protected argsShape = { ...DbOperationArgs, }; - public operationType: OperationType = "delete"; + static operationType: OperationType = "delete"; protected async execute({ database, collection }: ToolArgs): Promise { const provider = await this.ensureConnected(); diff --git a/src/tools/mongodb/delete/dropDatabase.ts b/src/tools/mongodb/delete/dropDatabase.ts index d33682ce3..9618fa570 100644 --- a/src/tools/mongodb/delete/dropDatabase.ts +++ b/src/tools/mongodb/delete/dropDatabase.ts @@ -3,12 +3,12 @@ import { DbOperationArgs, MongoDBToolBase } from "../mongodbTool.js"; import type { ToolArgs, OperationType } from "../../tool.js"; export class DropDatabaseTool extends MongoDBToolBase { - public name = "drop-database"; + static toolName = "drop-database"; protected description = "Removes the specified database, deleting the associated data files"; protected argsShape = { database: DbOperationArgs.database, }; - public operationType: OperationType = "delete"; + static operationType: OperationType = "delete"; protected async execute({ database }: ToolArgs): Promise { const provider = await this.ensureConnected(); diff --git a/src/tools/mongodb/delete/dropIndex.ts b/src/tools/mongodb/delete/dropIndex.ts index 7c62dd0b9..da5191a38 100644 --- a/src/tools/mongodb/delete/dropIndex.ts +++ b/src/tools/mongodb/delete/dropIndex.ts @@ -5,7 +5,7 @@ import { DbOperationArgs, MongoDBToolBase } from "../mongodbTool.js"; import { type ToolArgs, type OperationType, formatUntrustedData } from "../../tool.js"; export class DropIndexTool extends MongoDBToolBase { - public name = "drop-index"; + static toolName = "drop-index"; protected description = "Drop an index for the provided database and collection."; protected argsShape = { ...DbOperationArgs, @@ -21,7 +21,7 @@ export class DropIndexTool extends MongoDBToolBase { .default("classic") .describe("The type of index to be deleted. Is always set to 'classic'."), }; - public operationType: OperationType = "delete"; + static operationType: OperationType = "delete"; protected async execute(toolArgs: ToolArgs): Promise { const provider = await this.ensureConnected(); diff --git a/src/tools/mongodb/metadata/collectionIndexes.ts b/src/tools/mongodb/metadata/collectionIndexes.ts index 7f2df71ea..613beaf49 100644 --- a/src/tools/mongodb/metadata/collectionIndexes.ts +++ b/src/tools/mongodb/metadata/collectionIndexes.ts @@ -17,10 +17,10 @@ type IndexStatus = { }; export class CollectionIndexesTool extends MongoDBToolBase { - public name = "collection-indexes"; + static toolName = "collection-indexes"; protected description = "Describe the indexes for a collection"; protected argsShape = DbOperationArgs; - public operationType: OperationType = "metadata"; + static operationType: OperationType = "metadata"; protected async execute({ database, collection }: ToolArgs): Promise { const provider = await this.ensureConnected(); diff --git a/src/tools/mongodb/metadata/collectionSchema.ts b/src/tools/mongodb/metadata/collectionSchema.ts index ad74e9e74..7bc205842 100644 --- a/src/tools/mongodb/metadata/collectionSchema.ts +++ b/src/tools/mongodb/metadata/collectionSchema.ts @@ -11,7 +11,7 @@ import { isObjectEmpty } from "../../../helpers/isObjectEmpty.js"; const MAXIMUM_SAMPLE_SIZE_HARD_LIMIT = 50_000; export class CollectionSchemaTool extends MongoDBToolBase { - public name = "collection-schema"; + static toolName = "collection-schema"; protected description = "Describe the schema for a collection"; protected argsShape = { ...DbOperationArgs, @@ -25,7 +25,7 @@ export class CollectionSchemaTool extends MongoDBToolBase { ), }; - public operationType: OperationType = "metadata"; + static operationType: OperationType = "metadata"; protected async execute( { database, collection, sampleSize, responseBytesLimit }: ToolArgs, diff --git a/src/tools/mongodb/metadata/collectionStorageSize.ts b/src/tools/mongodb/metadata/collectionStorageSize.ts index c38ccc076..326f31a38 100644 --- a/src/tools/mongodb/metadata/collectionStorageSize.ts +++ b/src/tools/mongodb/metadata/collectionStorageSize.ts @@ -3,11 +3,11 @@ import { DbOperationArgs, MongoDBToolBase } from "../mongodbTool.js"; import type { ToolArgs, OperationType } from "../../tool.js"; export class CollectionStorageSizeTool extends MongoDBToolBase { - public name = "collection-storage-size"; + static toolName = "collection-storage-size"; protected description = "Gets the size of the collection"; protected argsShape = DbOperationArgs; - public operationType: OperationType = "metadata"; + static operationType: OperationType = "metadata"; protected async execute({ database, collection }: ToolArgs): Promise { const provider = await this.ensureConnected(); diff --git a/src/tools/mongodb/metadata/dbStats.ts b/src/tools/mongodb/metadata/dbStats.ts index 830df410f..2055308e0 100644 --- a/src/tools/mongodb/metadata/dbStats.ts +++ b/src/tools/mongodb/metadata/dbStats.ts @@ -5,13 +5,13 @@ import { formatUntrustedData } from "../../tool.js"; import { EJSON } from "bson"; export class DbStatsTool extends MongoDBToolBase { - public name = "db-stats"; + static toolName = "db-stats"; protected description = "Returns statistics that reflect the use state of a single database"; protected argsShape = { database: DbOperationArgs.database, }; - public operationType: OperationType = "metadata"; + static operationType: OperationType = "metadata"; protected async execute({ database }: ToolArgs): Promise { const provider = await this.ensureConnected(); diff --git a/src/tools/mongodb/metadata/explain.ts b/src/tools/mongodb/metadata/explain.ts index 89cb5f356..5b0a1627a 100644 --- a/src/tools/mongodb/metadata/explain.ts +++ b/src/tools/mongodb/metadata/explain.ts @@ -9,7 +9,7 @@ import { FindArgs } from "../read/find.js"; import { CountArgs } from "../read/count.js"; export class ExplainTool extends MongoDBToolBase { - public name = "explain"; + static toolName = "explain"; protected description = "Returns statistics describing the execution of the winning plan chosen by the query optimizer for the evaluated method"; @@ -42,7 +42,7 @@ export class ExplainTool extends MongoDBToolBase { ), }; - public operationType: OperationType = "metadata"; + static operationType: OperationType = "metadata"; protected async execute({ database, diff --git a/src/tools/mongodb/metadata/listCollections.ts b/src/tools/mongodb/metadata/listCollections.ts index fb879cadc..7e384217d 100644 --- a/src/tools/mongodb/metadata/listCollections.ts +++ b/src/tools/mongodb/metadata/listCollections.ts @@ -4,13 +4,13 @@ import type { ToolArgs, OperationType } from "../../tool.js"; import { formatUntrustedData } from "../../tool.js"; export class ListCollectionsTool extends MongoDBToolBase { - public name = "list-collections"; + static toolName = "list-collections"; protected description = "List all collections for a given database"; protected argsShape = { database: DbOperationArgs.database, }; - public operationType: OperationType = "metadata"; + static operationType: OperationType = "metadata"; protected async execute({ database }: ToolArgs): Promise { const provider = await this.ensureConnected(); diff --git a/src/tools/mongodb/metadata/listDatabases.ts b/src/tools/mongodb/metadata/listDatabases.ts index e89b25493..02d02b63a 100644 --- a/src/tools/mongodb/metadata/listDatabases.ts +++ b/src/tools/mongodb/metadata/listDatabases.ts @@ -5,10 +5,10 @@ import type { OperationType } from "../../tool.js"; import { formatUntrustedData } from "../../tool.js"; export class ListDatabasesTool extends MongoDBToolBase { - public name = "list-databases"; + static toolName = "list-databases"; protected description = "List all databases for a MongoDB connection"; protected argsShape = {}; - public operationType: OperationType = "metadata"; + static operationType: OperationType = "metadata"; protected async execute(): Promise { const provider = await this.ensureConnected(); diff --git a/src/tools/mongodb/metadata/logs.ts b/src/tools/mongodb/metadata/logs.ts index b19fa72c2..173566b55 100644 --- a/src/tools/mongodb/metadata/logs.ts +++ b/src/tools/mongodb/metadata/logs.ts @@ -4,7 +4,7 @@ import { type ToolArgs, type OperationType, formatUntrustedData } from "../../to import { z } from "zod"; export class LogsTool extends MongoDBToolBase { - public name = "mongodb-logs"; + static toolName = "mongodb-logs"; protected description = "Returns the most recent logged mongod events"; protected argsShape = { type: z @@ -24,7 +24,7 @@ export class LogsTool extends MongoDBToolBase { .describe("The maximum number of log entries to return."), }; - public operationType: OperationType = "metadata"; + static operationType: OperationType = "metadata"; protected async execute({ type, limit }: ToolArgs): Promise { const provider = await this.ensureConnected(); diff --git a/src/tools/mongodb/mongodbTool.ts b/src/tools/mongodb/mongodbTool.ts index 1d2969f3c..e86de9192 100644 --- a/src/tools/mongodb/mongodbTool.ts +++ b/src/tools/mongodb/mongodbTool.ts @@ -16,7 +16,7 @@ export const DbOperationArgs = { export abstract class MongoDBToolBase extends ToolBase { protected server?: Server; - public category: ToolCategory = "mongodb"; + static category: ToolCategory = "mongodb"; protected async ensureConnected(): Promise { if (!this.session.isConnectedToMongoDB) { diff --git a/src/tools/mongodb/read/aggregate.ts b/src/tools/mongodb/read/aggregate.ts index ad4eb33a5..ebead0762 100644 --- a/src/tools/mongodb/read/aggregate.ts +++ b/src/tools/mongodb/read/aggregate.ts @@ -48,13 +48,13 @@ Note to LLM: If the entire aggregation result is required, use the "export" tool }) as const; export class AggregateTool extends MongoDBToolBase { - public name = "aggregate"; + static toolName = "aggregate"; protected description = "Run an aggregation against a MongoDB collection"; protected argsShape = { ...DbOperationArgs, ...getAggregateArgs(this.isFeatureEnabled("search")), }; - public operationType: OperationType = "read"; + static operationType: OperationType = "read"; protected async execute( { database, collection, pipeline, responseBytesLimit }: ToolArgs, diff --git a/src/tools/mongodb/read/count.ts b/src/tools/mongodb/read/count.ts index 435c2c772..7b45cebf6 100644 --- a/src/tools/mongodb/read/count.ts +++ b/src/tools/mongodb/read/count.ts @@ -13,7 +13,7 @@ export const CountArgs = { }; export class CountTool extends MongoDBToolBase { - public name = "count"; + static toolName = "count"; protected description = "Gets the number of documents in a MongoDB collection using db.collection.count() and query as an optional filter parameter"; protected argsShape = { @@ -21,7 +21,7 @@ export class CountTool extends MongoDBToolBase { ...CountArgs, }; - public operationType: OperationType = "read"; + static operationType: OperationType = "read"; protected async execute({ database, collection, query }: ToolArgs): Promise { const provider = await this.ensureConnected(); diff --git a/src/tools/mongodb/read/export.ts b/src/tools/mongodb/read/export.ts index 6f24aa889..cb140d0c5 100644 --- a/src/tools/mongodb/read/export.ts +++ b/src/tools/mongodb/read/export.ts @@ -9,7 +9,7 @@ import { jsonExportFormat } from "../../../common/exportsManager.js"; import { getAggregateArgs } from "./aggregate.js"; export class ExportTool extends MongoDBToolBase { - public name = "export"; + static toolName = "export"; protected description = "Export a query or aggregation results in the specified EJSON format."; protected argsShape = { ...DbOperationArgs, @@ -49,7 +49,7 @@ export class ExportTool extends MongoDBToolBase { ].join("\n") ), }; - public operationType: OperationType = "read"; + static operationType: OperationType = "read"; protected async execute({ database, diff --git a/src/tools/mongodb/read/find.ts b/src/tools/mongodb/read/find.ts index eb006f335..8b791027e 100644 --- a/src/tools/mongodb/read/find.ts +++ b/src/tools/mongodb/read/find.ts @@ -36,13 +36,13 @@ Note to LLM: If the entire query result is required, use the "export" tool inste }; export class FindTool extends MongoDBToolBase { - public name = "find"; + static toolName = "find"; protected description = "Run a find query against a MongoDB collection"; protected argsShape = { ...DbOperationArgs, ...FindArgs, }; - public operationType: OperationType = "read"; + static operationType: OperationType = "read"; protected async execute( { database, collection, filter, projection, limit, sort, responseBytesLimit }: ToolArgs, diff --git a/src/tools/mongodb/update/renameCollection.ts b/src/tools/mongodb/update/renameCollection.ts index 4992a3227..dca6e32d2 100644 --- a/src/tools/mongodb/update/renameCollection.ts +++ b/src/tools/mongodb/update/renameCollection.ts @@ -4,14 +4,14 @@ import { DbOperationArgs, MongoDBToolBase } from "../mongodbTool.js"; import type { ToolArgs, OperationType } from "../../tool.js"; export class RenameCollectionTool extends MongoDBToolBase { - public name = "rename-collection"; + static toolName = "rename-collection"; protected description = "Renames a collection in a MongoDB database"; protected argsShape = { ...DbOperationArgs, newName: z.string().describe("The new name for the collection"), dropTarget: z.boolean().optional().default(false).describe("If true, drops the target collection if it exists"), }; - public operationType: OperationType = "update"; + static operationType: OperationType = "update"; protected async execute({ database, diff --git a/src/tools/mongodb/update/updateMany.ts b/src/tools/mongodb/update/updateMany.ts index 9d936757f..bda34ec3e 100644 --- a/src/tools/mongodb/update/updateMany.ts +++ b/src/tools/mongodb/update/updateMany.ts @@ -6,7 +6,7 @@ import { checkIndexUsage } from "../../../helpers/indexCheck.js"; import { zEJSON } from "../../args.js"; export class UpdateManyTool extends MongoDBToolBase { - public name = "update-many"; + static toolName = "update-many"; protected description = "Updates all documents that match the specified filter for a collection"; protected argsShape = { ...DbOperationArgs, @@ -23,7 +23,7 @@ export class UpdateManyTool extends MongoDBToolBase { .optional() .describe("Controls whether to insert a new document if no documents match the filter"), }; - public operationType: OperationType = "update"; + static operationType: OperationType = "update"; protected async execute({ database, diff --git a/src/tools/tool.ts b/src/tools/tool.ts index 8173b8d20..769f3efdf 100644 --- a/src/tools/tool.ts +++ b/src/tools/tool.ts @@ -40,18 +40,49 @@ export type OperationType = "metadata" | "read" | "create" | "delete" | "update" export type ToolCategory = "mongodb" | "atlas" | "atlas-local"; export type ToolConstructorParams = { + name: string; + category: ToolCategory; + operationType: OperationType; session: Session; config: UserConfig; telemetry: Telemetry; elicitation: Elicitation; }; +/** + * The type for a ToolImplementation that the MongoDB MCP Server works with. + * This is the same as `ToolBase` abstract class plus some static properties + * that are used by the `Server` to automatically inject some constructor + * parameters when initializing individual tools. + */ +export type ToolClass = { + new (params: ToolConstructorParams): ToolBase; + toolName: string; + category: ToolCategory; + operationType: OperationType; +}; + +/** + * @class + * Abstract base class for all MCP tools. + * + * Tool implementations must extend this class to ensure a consistent interface. + * + * Subclasses must additionally conform to `ToolImplementation` type so the + * following properties can automatically be set by Server, via the constructor: + * - `name: string` — The unique name of the tool, derived from + * `ToolImplementation.toolName`. + * - `category: ToolCategory` — The category of the tool (e.g., "mongodb", + * "atlas"), derived from `ToolImplementation.category`. + * - `operationType: OperationType` — The type of operation the tool performs, + * derived from `ToolImplementation.operationType`. + */ export abstract class ToolBase { - public abstract name: string; + public name: string; - public abstract category: ToolCategory; + public category: ToolCategory; - public abstract operationType: OperationType; + public operationType: OperationType; protected abstract description: string; @@ -110,7 +141,10 @@ export abstract class ToolBase { protected readonly config: UserConfig; protected readonly telemetry: Telemetry; protected readonly elicitation: Elicitation; - constructor({ session, config, telemetry, elicitation }: ToolConstructorParams) { + constructor({ name, category, operationType, session, config, telemetry, elicitation }: ToolConstructorParams) { + this.name = name; + this.category = category; + this.operationType = operationType; this.session = session; this.config = config; this.telemetry = telemetry; diff --git a/src/transports/base.ts b/src/transports/base.ts index 70e0f02b1..89d488f29 100644 --- a/src/transports/base.ts +++ b/src/transports/base.ts @@ -20,7 +20,7 @@ import type { AtlasLocalClientFactoryFn } from "../common/atlasLocal.js"; import { defaultCreateAtlasLocalClient } from "../common/atlasLocal.js"; import type { Client } from "@mongodb-js/atlas-local"; import { VectorSearchEmbeddingsManager } from "../common/search/vectorSearchEmbeddingsManager.js"; -import type { ToolBase, ToolConstructorParams } from "../tools/tool.js"; +import type { ToolClass } from "../tools/tool.js"; import { applyConfigOverrides } from "../common/config/configOverrides.js"; export type RequestContext = { @@ -40,7 +40,7 @@ export type TransportRunnerConfig = { createAtlasLocalClient?: AtlasLocalClientFactoryFn; additionalLoggers?: LoggerBase[]; telemetryProperties?: Partial; - additionalTools?: (new (params: ToolConstructorParams) => ToolBase)[]; + additionalTools?: ToolClass[]; /** * Hook which allows library consumers to fetch configuration from external sources (e.g., secrets managers, APIs) * or modify the existing configuration before the session is created. @@ -56,7 +56,7 @@ export abstract class TransportRunnerBase { private readonly connectionErrorHandler: ConnectionErrorHandler; private readonly atlasLocalClient: Promise; private readonly telemetryProperties: Partial; - private readonly additionalTools?: (new (params: ToolConstructorParams) => ToolBase)[]; + private readonly additionalTools?: ToolClass[]; private readonly createSessionConfig?: CreateSessionConfigFn; protected constructor({ diff --git a/tests/integration/customTools.test.ts b/tests/integration/customTools.test.ts index 09835c9f2..0fa972bd0 100644 --- a/tests/integration/customTools.test.ts +++ b/tests/integration/customTools.test.ts @@ -79,9 +79,9 @@ describe("Custom Tools", () => { * Example custom tool that can be provided by library consumers */ class CustomGreetingTool extends ToolBase { - name = "custom_greeting"; - category = "mongodb" as const; - operationType = "read" as const; + static toolName = "custom_greeting"; + static category = "mongodb" as const; + static operationType = "read" as const; protected description = "A custom tool that greets the user"; protected argsShape = { name: z.string().describe("The name to greet"), @@ -107,9 +107,9 @@ class CustomGreetingTool extends ToolBase { * Another example custom tool that performs a calculation */ class CustomCalculatorTool extends ToolBase { - name = "custom_calculator"; - category = "mongodb" as const; - operationType = "read" as const; + static toolName = "custom_calculator"; + static category = "mongodb" as const; + static operationType = "read" as const; protected description = "A custom tool that performs calculations"; protected argsShape = { a: z.number().describe("First number"), diff --git a/tests/integration/tools/mongodb/mongodbTool.test.ts b/tests/integration/tools/mongodb/mongodbTool.test.ts index 1fa129a10..ebf9257f6 100644 --- a/tests/integration/tools/mongodb/mongodbTool.test.ts +++ b/tests/integration/tools/mongodb/mongodbTool.test.ts @@ -3,7 +3,7 @@ import { type CallToolResult } from "@modelcontextprotocol/sdk/types.js"; import { Client } from "@modelcontextprotocol/sdk/client/index.js"; import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js"; import { MongoDBToolBase } from "../../../../src/tools/mongodb/mongodbTool.js"; -import { type ToolBase, type ToolConstructorParams, type OperationType } from "../../../../src/tools/tool.js"; +import { type OperationType, type ToolClass } from "../../../../src/tools/tool.js"; import { type UserConfig } from "../../../../src/common/config/userConfig.js"; import { MCPConnectionManager } from "../../../../src/common/connectionManager.js"; import { Session } from "../../../../src/common/session.js"; @@ -53,8 +53,8 @@ const injectedErrorHandler: ConnectionErrorHandler = (error) => { }; class RandomTool extends MongoDBToolBase { - name = "Random"; - operationType: OperationType = "read"; + static toolName = "Random"; + static operationType: OperationType = "read"; protected description = "This is a tool."; protected argsShape = {}; public async execute(): Promise { @@ -64,8 +64,8 @@ class RandomTool extends MongoDBToolBase { } class UnusableVoyageTool extends MongoDBToolBase { - name = "UnusableVoyageTool"; - operationType: OperationType = "read"; + static toolName = "UnusableVoyageTool"; + static operationType: OperationType = "read"; protected description = "This is a Voyage tool."; protected argsShape = {}; @@ -88,7 +88,7 @@ describe("MongoDBTool implementations", () => { async function cleanupAndStartServer( config: Partial | undefined = {}, - additionalTools: (new (params: ToolConstructorParams) => ToolBase)[] = [RandomTool], + additionalTools: ToolClass[] = [RandomTool], errorHandler: ConnectionErrorHandler | undefined = connectionErrorHandler ): Promise { await cleanup(); diff --git a/tests/unit/toolBase.test.ts b/tests/unit/toolBase.test.ts index 002b04266..da8a872ed 100644 --- a/tests/unit/toolBase.test.ts +++ b/tests/unit/toolBase.test.ts @@ -60,6 +60,9 @@ describe("ToolBase", () => { } as unknown as Elicitation; const constructorParams: ToolConstructorParams = { + name: TestTool.toolName, + category: TestTool.category, + operationType: TestTool.operationType, session: mockSession, config: mockConfig, telemetry: mockTelemetry, @@ -262,9 +265,9 @@ describe("ToolBase", () => { }); class TestTool extends ToolBase { - public name = "test-tool"; - public category: ToolCategory = "mongodb"; - public operationType: OperationType = "delete"; + static toolName = "test-tool"; + static category: ToolCategory = "mongodb"; + static operationType: OperationType = "delete"; protected description = "A test tool for verification tests"; protected argsShape = { param1: z.string().describe("Test parameter 1"), From 680d0723ff976dbffad955d44ac1ee9a30c7dc0b Mon Sep 17 00:00:00 2001 From: Himanshu Singh Date: Thu, 27 Nov 2025 18:03:34 +0100 Subject: [PATCH 4/4] chore: replace internalToolImplementations with AllTools --- src/server.ts | 3 +-- src/tools/atlas/tools.ts | 3 ++- src/tools/atlasLocal/tools.ts | 8 +++++++- src/tools/index.ts | 3 ++- src/tools/mongodb/tools.ts | 3 ++- 5 files changed, 14 insertions(+), 6 deletions(-) diff --git a/src/server.ts b/src/server.ts index 5d1dc790a..88a1dbf7e 100644 --- a/src/server.ts +++ b/src/server.ts @@ -60,7 +60,6 @@ export class Server { private readonly telemetry: Telemetry; public readonly userConfig: UserConfig; public readonly elicitation: Elicitation; - private readonly internalToolImplementations: ToolClass[] = AllTools; private readonly additionalToolImplementations: ToolClass[]; public readonly tools: ToolBase[] = []; public readonly connectionErrorHandler: ConnectionErrorHandler; @@ -242,7 +241,7 @@ export class Server { private registerTools(): void { const toolImplementations = [ - ...this.internalToolImplementations.map((toolConstructor) => ({ + ...AllTools.map((toolConstructor) => ({ toolConstructor, source: "internal", })), diff --git a/src/tools/atlas/tools.ts b/src/tools/atlas/tools.ts index c2822ec55..dc36fb462 100644 --- a/src/tools/atlas/tools.ts +++ b/src/tools/atlas/tools.ts @@ -11,8 +11,9 @@ import { ListOrganizationsTool } from "./read/listOrgs.js"; import { ConnectClusterTool } from "./connect/connectCluster.js"; import { ListAlertsTool } from "./read/listAlerts.js"; import { GetPerformanceAdvisorTool } from "./read/getPerformanceAdvisor.js"; +import type { ToolClass } from "../tool.js"; -export const AtlasTools = [ +export const AtlasTools: ToolClass[] = [ ListClustersTool, ListProjectsTool, InspectClusterTool, diff --git a/src/tools/atlasLocal/tools.ts b/src/tools/atlasLocal/tools.ts index 451362ce6..e73c979e4 100644 --- a/src/tools/atlasLocal/tools.ts +++ b/src/tools/atlasLocal/tools.ts @@ -2,5 +2,11 @@ import { DeleteDeploymentTool } from "./delete/deleteDeployment.js"; import { ListDeploymentsTool } from "./read/listDeployments.js"; import { CreateDeploymentTool } from "./create/createDeployment.js"; import { ConnectDeploymentTool } from "./connect/connectDeployment.js"; +import type { ToolClass } from "../tool.js"; -export const AtlasLocalTools = [ListDeploymentsTool, DeleteDeploymentTool, CreateDeploymentTool, ConnectDeploymentTool]; +export const AtlasLocalTools: ToolClass[] = [ + ListDeploymentsTool, + DeleteDeploymentTool, + CreateDeploymentTool, + ConnectDeploymentTool, +]; diff --git a/src/tools/index.ts b/src/tools/index.ts index ded820067..262755d84 100644 --- a/src/tools/index.ts +++ b/src/tools/index.ts @@ -1,8 +1,9 @@ import { AtlasTools } from "./atlas/tools.js"; import { AtlasLocalTools } from "./atlasLocal/tools.js"; import { MongoDbTools } from "./mongodb/tools.js"; +import type { ToolClass } from "./tool.js"; -const AllTools = [...MongoDbTools, ...AtlasTools, ...AtlasLocalTools]; +const AllTools: ToolClass[] = [...MongoDbTools, ...AtlasTools, ...AtlasLocalTools]; export { AllTools, MongoDbTools, AtlasTools, AtlasLocalTools }; diff --git a/src/tools/mongodb/tools.ts b/src/tools/mongodb/tools.ts index ffbb71d80..c03b85ae8 100644 --- a/src/tools/mongodb/tools.ts +++ b/src/tools/mongodb/tools.ts @@ -21,8 +21,9 @@ import { LogsTool } from "./metadata/logs.js"; import { ExportTool } from "./read/export.js"; import { DropIndexTool } from "./delete/dropIndex.js"; import { SwitchConnectionTool } from "./connect/switchConnection.js"; +import type { ToolClass } from "../tool.js"; -export const MongoDbTools = [ +export const MongoDbTools: ToolClass[] = [ ConnectTool, SwitchConnectionTool, ListCollectionsTool,