Skip to content
Merged
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
1,119 changes: 1,119 additions & 0 deletions MCP_SERVER_LIBRARY.md

Large diffs are not rendered by default.

9 changes: 8 additions & 1 deletion knip.json
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,14 @@
"scripts/**/*.ts",
"eslint-rules/*.js"
],
"ignore": ["tests/integration/fixtures/curl.mjs", "tests/vitest.d.ts"],
"ignore": [
// Knip, for some reason, is not able to link the exported tools to the
// final exports in `tools/index.ts` and complains about unused exports. For
// that reason we're ignoring the tool definitions.
"src/tools/**/*.ts",
"tests/integration/fixtures/curl.mjs",
"tests/vitest.d.ts"
],
"ignoreDependencies": ["@mongodb-js/atlas-local"],
"ignoreExportsUsedInFile": true
}
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
26 changes: 18 additions & 8 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 @@ -35,10 +35,19 @@ export interface ServerOptions {
* This will override any default tools. You can use both existing and custom tools by using the `mongodb-mcp-server/tools` export.
*
* ```ts
* import { AllTools, ToolBase } from "mongodb-mcp-server/tools";
* import { AllTools, ToolBase, type ToolCategory, type OperationType } from "mongodb-mcp-server/tools";
* class CustomTool extends ToolBase {
* name = "custom_tool";
* // ...
* override name = "custom_tool";
* override category: ToolCategory = "mongodb";
* static operationType: OperationType = "read";
* protected description = "Custom tool description";
* protected argsShape = {};
* protected async execute() {
* return { content: [{ type: "text", text: "Result" }] };
* }
* protected resolveTelemetryMetadata() {
* return {};
* }
* }
* const server = new Server({
* session: mySession,
Expand All @@ -47,11 +56,11 @@ export interface ServerOptions {
* telemetry: myTelemetry,
* elicitation: myElicitation,
* connectionErrorHandler: myConnectionErrorHandler,
* tools: [...AllTools, CustomTool],
* tools: [...Object.values(AllTools), CustomTool],
* });
* ```
*/
tools?: (new (params: ToolConstructorParams) => ToolBase)[];
tools?: ToolClass[];
}

export class Server {
Expand All @@ -60,7 +69,7 @@ export class Server {
private readonly telemetry: Telemetry;
public readonly userConfig: UserConfig;
public readonly elicitation: Elicitation;
private readonly toolConstructors: (new (params: ToolConstructorParams) => ToolBase)[];
private readonly toolConstructors: ToolClass[];
public readonly tools: ToolBase[] = [];
public readonly connectionErrorHandler: ConnectionErrorHandler;

Expand Down Expand Up @@ -89,7 +98,7 @@ export class Server {
this.userConfig = userConfig;
this.elicitation = elicitation;
this.connectionErrorHandler = connectionErrorHandler;
this.toolConstructors = tools ?? AllTools;
this.toolConstructors = tools ?? Object.values(AllTools);
}

async connect(transport: Transport): Promise<void> {
Expand Down Expand Up @@ -242,6 +251,7 @@ export class Server {
private registerTools(): void {
for (const toolConstructor of this.toolConstructors) {
const tool = new toolConstructor({
operationType: toolConstructor.operationType,
session: this.session,
config: this.userConfig,
telemetry: this.telemetry,
Expand Down
2 changes: 1 addition & 1 deletion src/tools/atlas/connect/connectCluster.ts
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ export const ConnectClusterArgs = {
export class ConnectClusterTool extends AtlasToolBase {
public name = "atlas-connect-cluster";
protected description = "Connect to MongoDB Atlas cluster";
public operationType: OperationType = "connect";
static operationType: OperationType = "connect";
protected argsShape = {
...ConnectClusterArgs,
};
Expand Down
2 changes: 1 addition & 1 deletion src/tools/atlas/create/createAccessList.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ export const CreateAccessListArgs = {
export class CreateAccessListTool extends AtlasToolBase {
public name = "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
2 changes: 1 addition & 1 deletion src/tools/atlas/create/createDBUser.ts
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ export const CreateDBUserArgs = {
export class CreateDBUserTool extends AtlasToolBase {
public name = "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
2 changes: 1 addition & 1 deletion src/tools/atlas/create/createFreeCluster.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ import { AtlasArgs } from "../../args.js";
export class CreateFreeClusterTool extends AtlasToolBase {
public name = "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
2 changes: 1 addition & 1 deletion src/tools/atlas/create/createProject.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ import { AtlasArgs } from "../../args.js";
export class CreateProjectTool extends AtlasToolBase {
public name = "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
2 changes: 1 addition & 1 deletion src/tools/atlas/read/getPerformanceAdvisor.ts
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ const PerformanceAdvisorOperationType = z.enum([
export class GetPerformanceAdvisorTool extends AtlasToolBase {
public name = "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
2 changes: 1 addition & 1 deletion src/tools/atlas/read/inspectAccessList.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ export const InspectAccessListArgs = {
export class InspectAccessListTool extends AtlasToolBase {
public name = "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
2 changes: 1 addition & 1 deletion src/tools/atlas/read/inspectCluster.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ export const InspectClusterArgs = {
export class InspectClusterTool extends AtlasToolBase {
public name = "atlas-inspect-cluster";
protected description = "Inspect MongoDB Atlas cluster";
public operationType: OperationType = "read";
static operationType: OperationType = "read";
protected argsShape = {
...InspectClusterArgs,
};
Expand Down
2 changes: 1 addition & 1 deletion src/tools/atlas/read/listAlerts.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ export const ListAlertsArgs = {
export class ListAlertsTool extends AtlasToolBase {
public name = "atlas-list-alerts";
protected description = "List MongoDB Atlas alerts";
public operationType: OperationType = "read";
static operationType: OperationType = "read";
protected argsShape = {
...ListAlertsArgs,
};
Expand Down
2 changes: 1 addition & 1 deletion src/tools/atlas/read/listClusters.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ export const ListClustersArgs = {
export class ListClustersTool extends AtlasToolBase {
public name = "atlas-list-clusters";
protected description = "List MongoDB Atlas clusters";
public operationType: OperationType = "read";
static operationType: OperationType = "read";
protected argsShape = {
...ListClustersArgs,
};
Expand Down
2 changes: 1 addition & 1 deletion src/tools/atlas/read/listDBUsers.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ export const ListDBUsersArgs = {
export class ListDBUsersTool extends AtlasToolBase {
public name = "atlas-list-db-users";
protected description = "List MongoDB Atlas database users";
public operationType: OperationType = "read";
static operationType: OperationType = "read";
protected argsShape = {
...ListDBUsersArgs,
};
Expand Down
2 changes: 1 addition & 1 deletion src/tools/atlas/read/listOrgs.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ import { formatUntrustedData } from "../../tool.js";
export class ListOrganizationsTool extends AtlasToolBase {
public name = "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
2 changes: 1 addition & 1 deletion src/tools/atlas/read/listProjects.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ import { AtlasArgs } from "../../args.js";
export class ListProjectsTool extends AtlasToolBase {
public name = "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
42 changes: 13 additions & 29 deletions src/tools/atlas/tools.ts
Original file line number Diff line number Diff line change
@@ -1,29 +1,13 @@
import { ListClustersTool } from "./read/listClusters.js";
import { ListProjectsTool } from "./read/listProjects.js";
import { InspectClusterTool } from "./read/inspectCluster.js";
import { CreateFreeClusterTool } from "./create/createFreeCluster.js";
import { CreateAccessListTool } from "./create/createAccessList.js";
import { InspectAccessListTool } from "./read/inspectAccessList.js";
import { ListDBUsersTool } from "./read/listDBUsers.js";
import { CreateDBUserTool } from "./create/createDBUser.js";
import { CreateProjectTool } from "./create/createProject.js";
import { ListOrganizationsTool } from "./read/listOrgs.js";
import { ConnectClusterTool } from "./connect/connectCluster.js";
import { ListAlertsTool } from "./read/listAlerts.js";
import { GetPerformanceAdvisorTool } from "./read/getPerformanceAdvisor.js";

export const AtlasTools = [
ListClustersTool,
ListProjectsTool,
InspectClusterTool,
CreateFreeClusterTool,
CreateAccessListTool,
InspectAccessListTool,
ListDBUsersTool,
CreateDBUserTool,
CreateProjectTool,
ListOrganizationsTool,
ConnectClusterTool,
ListAlertsTool,
GetPerformanceAdvisorTool,
];
export { ListClustersTool } from "./read/listClusters.js";
export { ListProjectsTool } from "./read/listProjects.js";
export { InspectClusterTool } from "./read/inspectCluster.js";
export { CreateFreeClusterTool } from "./create/createFreeCluster.js";
export { CreateAccessListTool } from "./create/createAccessList.js";
export { InspectAccessListTool } from "./read/inspectAccessList.js";
export { ListDBUsersTool } from "./read/listDBUsers.js";
export { CreateDBUserTool } from "./create/createDBUser.js";
export { CreateProjectTool } from "./create/createProject.js";
export { ListOrganizationsTool } from "./read/listOrgs.js";
export { ConnectClusterTool } from "./connect/connectCluster.js";
export { ListAlertsTool } from "./read/listAlerts.js";
export { GetPerformanceAdvisorTool } from "./read/getPerformanceAdvisor.js";
2 changes: 1 addition & 1 deletion src/tools/atlasLocal/connect/connectDeployment.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ import type { ConnectionMetadata } from "../../../telemetry/types.js";
export class ConnectDeploymentTool extends AtlasLocalToolBase {
public name = "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
2 changes: 1 addition & 1 deletion src/tools/atlasLocal/create/createDeployment.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ import { CommonArgs } from "../../args.js";
export class CreateDeploymentTool extends AtlasLocalToolBase {
public name = "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
2 changes: 1 addition & 1 deletion src/tools/atlasLocal/delete/deleteDeployment.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ import { CommonArgs } from "../../args.js";
export class DeleteDeploymentTool extends AtlasLocalToolBase {
public name = "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
2 changes: 1 addition & 1 deletion src/tools/atlasLocal/read/listDeployments.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ import type { Client } from "@mongodb-js/atlas-local";
export class ListDeploymentsTool extends AtlasLocalToolBase {
public name = "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
10 changes: 4 additions & 6 deletions src/tools/atlasLocal/tools.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,4 @@
import { DeleteDeploymentTool } from "./delete/deleteDeployment.js";
import { ListDeploymentsTool } from "./read/listDeployments.js";
import { CreateDeploymentTool } from "./create/createDeployment.js";
import { ConnectDeploymentTool } from "./connect/connectDeployment.js";

export const AtlasLocalTools = [ListDeploymentsTool, DeleteDeploymentTool, CreateDeploymentTool, ConnectDeploymentTool];
export { DeleteDeploymentTool } from "./delete/deleteDeployment.js";
export { ListDeploymentsTool } from "./read/listDeployments.js";
export { CreateDeploymentTool } from "./create/createDeployment.js";
export { ConnectDeploymentTool } from "./connect/connectDeployment.js";
15 changes: 11 additions & 4 deletions src/tools/index.ts
Original file line number Diff line number Diff line change
@@ -1,13 +1,20 @@
import { AtlasTools } from "./atlas/tools.js";
import { AtlasLocalTools } from "./atlasLocal/tools.js";
import { MongoDbTools } from "./mongodb/tools.js";
import * as AtlasTools from "./atlas/tools.js";
import * as AtlasLocalTools from "./atlasLocal/tools.js";
import * as MongoDbTools from "./mongodb/tools.js";

const AllTools = [...MongoDbTools, ...AtlasTools, ...AtlasLocalTools];
const AllTools = {
...MongoDbTools,
...AtlasTools,
...AtlasLocalTools,
} as const;
Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Please note that we're now exporting record like exports instead of array like exports. The changes allows nitpicking of individual tools.

Copy link
Collaborator

Choose a reason for hiding this comment

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

I think we should export this as an array and also do:

export * from from "./mongodb/tools.js";
...

This way the IDE can suggest these imports and no real nitpicking is required

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

But it is the same?

export * from "../mongodb/tools.js"

needs to be imported as:
import { ConnectTool } from "mongodb-mcp-server/tools"

and currently you would do the same either by:

import { AllTools } from "mongodb-mcp-server/tools"
AllTools.ConnectTool

or

import { MongoDbTools } from "mongodb-mcp-server/tools"
MongoDbTools.ConnectTool

Actually with the current change, we do get per category division already which we would miss if we do:
export * from "../mongodb/tools.js"

unless we do
export * as MongoDbTools from "../mongodb/tools.js"

which then essentially is the same.

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Based on our discussion, I have updated the export to have individual exports alongside the list of all the tools for convenience.


// Export all the different categories of tools
export { AllTools, MongoDbTools, AtlasTools, AtlasLocalTools };

// Export the base tool class and supporting types.
export {
ToolBase,
type ToolClass,
type ToolConstructorParams,
type ToolCategory,
type OperationType,
Expand Down
10 changes: 5 additions & 5 deletions src/tools/mongodb/connect/connect.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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
10 changes: 5 additions & 5 deletions src/tools/mongodb/connect/switchConnection.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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();
});
}
Expand Down
Loading