Skip to content
Closed
Show file tree
Hide file tree
Changes from 3 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
17 changes: 10 additions & 7 deletions src/lib.ts
Original file line number Diff line number Diff line change
@@ -1,26 +1,29 @@
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";
export { TransportRunnerBase, type TransportRunnerConfig } from "./transports/base.js";
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";
Expand Down
46 changes: 36 additions & 10 deletions src/server.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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";
Expand All @@ -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";
* // ...
Expand All @@ -47,11 +47,11 @@ export interface ServerOptions {
* telemetry: myTelemetry,
* elicitation: myElicitation,
* connectionErrorHandler: myConnectionErrorHandler,
* tools: [...AllTools, CustomTool],
* additionalTools: [CustomTool],
* });
* ```
*/
tools?: (new (params: ToolConstructorParams) => ToolBase)[];
additionalTools?: ToolClass[];
}

export class Server {
Expand All @@ -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: ToolClass[] = AllTools;
private readonly additionalToolImplementations: ToolClass[];
public readonly tools: ToolBase[] = [];
public readonly connectionErrorHandler: ConnectionErrorHandler;

Expand All @@ -80,7 +81,7 @@ export class Server {
telemetry,
connectionErrorHandler,
elicitation,
tools,
additionalTools,
}: ServerOptions) {
this.startTime = Date.now();
this.session = session;
Expand All @@ -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<void> {
Expand Down Expand Up @@ -240,15 +241,40 @@ 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<string> = new Set();

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,
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);
}
}
}
Expand Down
2 changes: 1 addition & 1 deletion src/tools/atlas/atlasTool.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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) {
Expand Down
4 changes: 2 additions & 2 deletions src/tools/atlas/connect/connectCluster.ts
Original file line number Diff line number Diff line change
Expand Up @@ -30,9 +30,9 @@ export const ConnectClusterArgs = {
};

export class ConnectClusterTool extends AtlasToolBase {
public name = "atlas-connect-cluster";
static toolName = "atlas-connect-cluster";
Copy link
Collaborator

@gagik gagik Nov 27, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ConnectClusterTool.toolName? it's better to avoid stutter

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The static property name is unfortunately reserved for Function.name and TS forbids overriding that.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

TIL

protected description = "Connect to MongoDB Atlas cluster";
public operationType: OperationType = "connect";
static operationType: OperationType = "connect";
protected argsShape = {
...ConnectClusterArgs,
};
Expand Down
4 changes: 2 additions & 2 deletions src/tools/atlas/create/createAccessList.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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,
};
Expand Down
4 changes: 2 additions & 2 deletions src/tools/atlas/create/createDBUser.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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,
};
Expand Down
4 changes: 2 additions & 2 deletions src/tools/atlas/create/createFreeCluster.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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"),
Expand Down
4 changes: 2 additions & 2 deletions src/tools/atlas/create/createProject.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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"),
Expand Down
4 changes: 2 additions & 2 deletions src/tools/atlas/read/getPerformanceAdvisor.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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."
Expand Down
4 changes: 2 additions & 2 deletions src/tools/atlas/read/inspectAccessList.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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,
};
Expand Down
4 changes: 2 additions & 2 deletions src/tools/atlas/read/inspectCluster.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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,
};
Expand Down
4 changes: 2 additions & 2 deletions src/tools/atlas/read/listAlerts.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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,
};
Expand Down
4 changes: 2 additions & 2 deletions src/tools/atlas/read/listClusters.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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,
};
Expand Down
4 changes: 2 additions & 2 deletions src/tools/atlas/read/listDBUsers.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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,
};
Expand Down
4 changes: 2 additions & 2 deletions src/tools/atlas/read/listOrgs.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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<CallToolResult> {
Expand Down
4 changes: 2 additions & 2 deletions src/tools/atlas/read/listProjects.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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.")
Expand Down
2 changes: 1 addition & 1 deletion src/tools/atlasLocal/atlasLocalTool.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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();
Expand Down
4 changes: 2 additions & 2 deletions src/tools/atlasLocal/connect/connectDeployment.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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"),
};
Expand Down
4 changes: 2 additions & 2 deletions src/tools/atlasLocal/create/createDeployment.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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(),
};
Expand Down
4 changes: 2 additions & 2 deletions src/tools/atlasLocal/delete/deleteDeployment.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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"),
};
Expand Down
4 changes: 2 additions & 2 deletions src/tools/atlasLocal/read/listDeployments.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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<CallToolResult> {
Expand Down
12 changes: 6 additions & 6 deletions src/tools/mongodb/connect/connect.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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.";

Expand All @@ -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();
});
}
Expand Down
Loading