From 3a56da3e0cfa575597afc6e847876f6d79128747 Mon Sep 17 00:00:00 2001 From: Filipe C Menezes Date: Fri, 11 Apr 2025 12:24:42 +0100 Subject: [PATCH 1/8] chore: remove configFile and state.json --- README.md | 29 ++++++++---------------- src/config.ts | 33 +++++++--------------------- src/logger.ts | 5 +---- src/server.ts | 24 +++++--------------- src/state.ts | 28 +---------------------- src/tools/atlas/atlasTool.ts | 2 +- src/tools/atlas/createAccessList.ts | 4 ++-- src/tools/atlas/createDBUser.ts | 2 +- src/tools/atlas/createFreeCluster.ts | 2 +- src/tools/atlas/inspectAccessList.ts | 2 +- src/tools/atlas/inspectCluster.ts | 2 +- src/tools/atlas/listClusters.ts | 6 ++--- src/tools/atlas/listDBUsers.ts | 2 +- src/tools/atlas/listProjects.ts | 2 +- src/tools/mongodb/connect.ts | 2 +- src/tools/mongodb/mongodbTool.ts | 3 +-- 16 files changed, 39 insertions(+), 109 deletions(-) diff --git a/README.md b/README.md index b8d4eedd..b887870a 100644 --- a/README.md +++ b/README.md @@ -157,8 +157,6 @@ The MongoDB MCP Server can be configured using multiple methods, with the follow 1. Command-line arguments 2. Environment variables -3. Configuration file -4. Default values ### Configuration Options @@ -167,6 +165,12 @@ The MongoDB MCP Server can be configured using multiple methods, with the follow | `apiClientId` | Atlas API client ID for authentication | | `apiClientSecret` | Atlas API client secret for authentication | | `connectionString` | MongoDB connection string for direct database connections (optional users may choose to inform it on every tool call) | +| `logPath` | Folder to store logs | + +**Default Log Path:** + +- Windows: `%LOCALAPPDATA%\mongodb\mongodb-mcp\.app-logs` +- macOS/Linux: `~/.mongodb/mongodb-mcp/.app-logs` ### Atlas API Access @@ -195,23 +199,6 @@ To use the Atlas API tools, you'll need to create a service account in MongoDB A ### Configuration Methods -#### Configuration File - -Create a JSON configuration file at one of these locations: - -- Linux/macOS: `/etc/mongodb-mcp.conf` -- Windows: `%LOCALAPPDATA%\mongodb\mongodb-mcp\mongodb-mcp.conf` - -Example configuration file: - -```json -{ - "apiClientId": "your-atlas-client-id", - "apiClientSecret": "your-atlas-client-secret", - "connectionString": "mongodb+srv://username:password@cluster.mongodb.net/myDatabase" -} -``` - #### Environment Variables Set environment variables with the prefix `MDB_MCP_` followed by the option name in uppercase with underscores: @@ -223,6 +210,8 @@ export MDB_MCP_API_CLIENT_SECRET="your-atlas-client-secret" # Set a custom MongoDB connection string export MDB_MCP_CONNECTION_STRING="mongodb+srv://username:password@cluster.mongodb.net/myDatabase" + +export MDB_MCP_LOG_PATH="/path/to/logs" ``` #### Command-Line Arguments @@ -230,7 +219,7 @@ export MDB_MCP_CONNECTION_STRING="mongodb+srv://username:password@cluster.mongod Pass configuration options as command-line arguments when starting the server: ```shell -node dist/index.js --apiClientId="your-atlas-client-id" --apiClientSecret="your-atlas-client-secret" --connectionString="mongodb+srv://username:password@cluster.mongodb.net/myDatabase" +node dist/index.js --apiClientId="your-atlas-client-id" --apiClientSecret="your-atlas-client-secret" --connectionString="mongodb+srv://username:password@cluster.mongodb.net/myDatabase" --logPath=/path/to/logs ``` ## 🤝 Contributing diff --git a/src/config.ts b/src/config.ts index acadcf9e..4a13118c 100644 --- a/src/config.ts +++ b/src/config.ts @@ -5,7 +5,7 @@ import argv from "yargs-parser"; import packageJson from "../package.json" with { type: "json" }; import fs from "fs"; import { ReadConcernLevel, ReadPreferenceMode, W } from "mongodb"; -const { localDataPath, configPath } = getLocalDataPath(); +import { log } from "console"; // If we decide to support non-string config options, we'll need to extend the mechanism for parsing // env variables. @@ -13,7 +13,7 @@ interface UserConfig { apiBaseUrl: string; apiClientId?: string; apiClientSecret?: string; - stateFile: string; + logPath: string; connectionString?: string; connectOptions: { readConcern: ReadConcernLevel; @@ -25,7 +25,7 @@ interface UserConfig { const defaults: UserConfig = { apiBaseUrl: "https://cloud.mongodb.com/", - stateFile: path.join(localDataPath, "state.json"), + logPath: getLogPath(), connectOptions: { readConcern: "local", readPreference: "secondaryPreferred", @@ -36,7 +36,6 @@ const defaults: UserConfig = { const mergedUserConfig = { ...defaults, - ...getFileConfig(), ...getEnvConfig(), ...getCliConfig(), }; @@ -46,33 +45,28 @@ const config = { atlasApiVersion: `2025-03-12`, version: packageJson.version, userAgent: `AtlasMCP/${packageJson.version} (${process.platform}; ${process.arch}; ${process.env.HOSTNAME || "unknown"})`, - localDataPath, }; export default config; -function getLocalDataPath(): { localDataPath: string; configPath: string } { +function getLogPath(): string { let localDataPath: string | undefined; - let configPath: string | undefined; if (process.platform === "win32") { const appData = process.env.APPDATA; const localAppData = process.env.LOCALAPPDATA ?? process.env.APPDATA; if (localAppData && appData) { localDataPath = path.join(localAppData, "mongodb", "mongodb-mcp"); - configPath = path.join(localDataPath, "mongodb-mcp.conf"); } } localDataPath ??= path.join(os.homedir(), ".mongodb", "mongodb-mcp"); - configPath ??= "/etc/mongodb-mcp.conf"; - fs.mkdirSync(localDataPath, { recursive: true }); + const logPath = path.join(localDataPath, ".app-logs"); - return { - localDataPath, - configPath, - }; + fs.mkdirSync(logPath, { recursive: true }); + + return logPath; } // Gets the config supplied by the user as environment variables. The variable names @@ -125,17 +119,6 @@ function SNAKE_CASE_toCamelCase(str: string): string { return str.toLowerCase().replace(/([-_][a-z])/g, (group) => group.toUpperCase().replace("_", "")); } -// Gets the config supplied by the user as a JSON file. The file is expected to be located in the local data path -// and named `config.json`. -function getFileConfig(): Partial { - try { - const config = fs.readFileSync(configPath, "utf8"); - return JSON.parse(config); - } catch { - return {}; - } -} - // Reads the cli args and parses them into a UserConfig object. function getCliConfig() { return argv(process.argv.slice(2)) as unknown as Partial; diff --git a/src/logger.ts b/src/logger.ts index d5415a74..487d446b 100644 --- a/src/logger.ts +++ b/src/logger.ts @@ -100,11 +100,8 @@ const logger = new ProxyingLogger(); export default logger; export async function initializeLogger(server: McpServer): Promise { - const logDir = path.join(config.localDataPath, ".app-logs"); - await fs.mkdir(logDir, { recursive: true }); - const manager = new MongoLogManager({ - directory: path.join(config.localDataPath, ".app-logs"), + directory: config.logPath, retentionDays: 30, onwarn: console.warn, onerror: console.error, diff --git a/src/server.ts b/src/server.ts index 1df18e66..0b48b956 100644 --- a/src/server.ts +++ b/src/server.ts @@ -11,14 +11,14 @@ import { mongoLogId } from "mongodb-log-writer"; export class Server { state: State = defaultState; apiClient?: ApiClient; - initialized: boolean = false; - private async init() { - if (this.initialized) { - return; - } + private createMcpServer(): McpServer { + const server = new McpServer({ + name: "MongoDB Atlas", + version: config.version, + }); - await this.state.loadCredentials(); + server.server.registerCapabilities({ logging: {} }); if (config.apiClientId && config.apiClientSecret) { this.apiClient = new ApiClient({ @@ -29,17 +29,6 @@ export class Server { }); } - this.initialized = true; - } - - private createMcpServer(): McpServer { - const server = new McpServer({ - name: "MongoDB Atlas", - version: config.version, - }); - - server.server.registerCapabilities({ logging: {} }); - registerAtlasTools(server, this.state, this.apiClient); registerMongoDBTools(server, this.state); @@ -47,7 +36,6 @@ export class Server { } async connect(transport: Transport) { - await this.init(); const server = this.createMcpServer(); await server.connect(transport); await initializeLogger(server); diff --git a/src/state.ts b/src/state.ts index f99f3cb6..672f6dd8 100644 --- a/src/state.ts +++ b/src/state.ts @@ -1,34 +1,8 @@ import { NodeDriverServiceProvider } from "@mongosh/service-provider-node-driver"; -import { AsyncEntry } from "@napi-rs/keyring"; -import logger from "./logger.js"; -import { mongoLogId } from "mongodb-log-writer"; - -interface Credentials { - connectionString?: string; -} export class State { - private entry = new AsyncEntry("mongodb-mcp", "credentials"); - credentials: Credentials = {}; serviceProvider?: NodeDriverServiceProvider; - - public async persistCredentials(): Promise { - await this.entry.setPassword(JSON.stringify(this.credentials)); - } - - public async loadCredentials(): Promise { - try { - const data = await this.entry.getPassword(); - if (data) { - this.credentials = JSON.parse(data); - } - - return true; - } catch (err: unknown) { - logger.error(mongoLogId(1_000_007), "state", `Failed to load state: ${err}`); - return false; - } - } + connectionString?: string; } const defaultState = new State(); diff --git a/src/tools/atlas/atlasTool.ts b/src/tools/atlas/atlasTool.ts index 0b40d088..2cd433ed 100644 --- a/src/tools/atlas/atlasTool.ts +++ b/src/tools/atlas/atlasTool.ts @@ -10,7 +10,7 @@ export abstract class AtlasToolBase extends ToolBase { super(state); } - protected ensureAuthenticated(): void { + protected ensureAuthenticated(): asserts this is { apiClient: ApiClient } { if (!this.apiClient) { throw new Error( "Not authenticated make sure to configure MCP server with MDB_MCP_API_CLIENT_ID and MDB_MCP_API_CLIENT_SECRET environment variables." diff --git a/src/tools/atlas/createAccessList.ts b/src/tools/atlas/createAccessList.ts index f91509a4..8b95da67 100644 --- a/src/tools/atlas/createAccessList.ts +++ b/src/tools/atlas/createAccessList.ts @@ -39,7 +39,7 @@ export class CreateAccessListTool extends AtlasToolBase { })); if (currentIpAddress) { - const currentIp = await this.apiClient!.getIpInfo(); + const currentIp = await this.apiClient.getIpInfo(); const input = { groupId: projectId, ipAddress: currentIp.currentIpv4Address, @@ -56,7 +56,7 @@ export class CreateAccessListTool extends AtlasToolBase { const inputs = [...ipInputs, ...cidrInputs]; - await this.apiClient!.createProjectIpAccessList({ + await this.apiClient.createProjectIpAccessList({ params: { path: { groupId: projectId, diff --git a/src/tools/atlas/createDBUser.ts b/src/tools/atlas/createDBUser.ts index fe605a0f..ccb83f28 100644 --- a/src/tools/atlas/createDBUser.ts +++ b/src/tools/atlas/createDBUser.ts @@ -53,7 +53,7 @@ export class CreateDBUserTool extends AtlasToolBase { : undefined, } as CloudDatabaseUser; - await this.apiClient!.createDatabaseUser({ + await this.apiClient.createDatabaseUser({ params: { path: { groupId: projectId, diff --git a/src/tools/atlas/createFreeCluster.ts b/src/tools/atlas/createFreeCluster.ts index 5339c5d2..513a07f7 100644 --- a/src/tools/atlas/createFreeCluster.ts +++ b/src/tools/atlas/createFreeCluster.ts @@ -38,7 +38,7 @@ export class CreateFreeClusterTool extends AtlasToolBase { terminationProtectionEnabled: false, } as unknown as ClusterDescription20240805; - await this.apiClient!.createCluster({ + await this.apiClient.createCluster({ params: { path: { groupId: projectId, diff --git a/src/tools/atlas/inspectAccessList.ts b/src/tools/atlas/inspectAccessList.ts index d1497ee6..6be93723 100644 --- a/src/tools/atlas/inspectAccessList.ts +++ b/src/tools/atlas/inspectAccessList.ts @@ -13,7 +13,7 @@ export class InspectAccessListTool extends AtlasToolBase { protected async execute({ projectId }: ToolArgs): Promise { this.ensureAuthenticated(); - const accessList = await this.apiClient!.listProjectIpAccessLists({ + const accessList = await this.apiClient.listProjectIpAccessLists({ params: { path: { groupId: projectId, diff --git a/src/tools/atlas/inspectCluster.ts b/src/tools/atlas/inspectCluster.ts index f0bda92d..8a18084d 100644 --- a/src/tools/atlas/inspectCluster.ts +++ b/src/tools/atlas/inspectCluster.ts @@ -15,7 +15,7 @@ export class InspectClusterTool extends AtlasToolBase { protected async execute({ projectId, clusterName }: ToolArgs): Promise { this.ensureAuthenticated(); - const cluster = await this.apiClient!.getCluster({ + const cluster = await this.apiClient.getCluster({ params: { path: { groupId: projectId, diff --git a/src/tools/atlas/listClusters.ts b/src/tools/atlas/listClusters.ts index 31dea9e5..93da22f0 100644 --- a/src/tools/atlas/listClusters.ts +++ b/src/tools/atlas/listClusters.ts @@ -15,11 +15,11 @@ export class ListClustersTool extends AtlasToolBase { this.ensureAuthenticated(); if (!projectId) { - const data = await this.apiClient!.listClustersForAllProjects(); + const data = await this.apiClient.listClustersForAllProjects(); return this.formatAllClustersTable(data); } else { - const project = await this.apiClient!.getProject({ + const project = await this.apiClient.getProject({ params: { path: { groupId: projectId, @@ -31,7 +31,7 @@ export class ListClustersTool extends AtlasToolBase { throw new Error(`Project with ID "${projectId}" not found.`); } - const data = await this.apiClient!.listClusters({ + const data = await this.apiClient.listClusters({ params: { path: { groupId: project.id || "", diff --git a/src/tools/atlas/listDBUsers.ts b/src/tools/atlas/listDBUsers.ts index 46404123..c2983716 100644 --- a/src/tools/atlas/listDBUsers.ts +++ b/src/tools/atlas/listDBUsers.ts @@ -14,7 +14,7 @@ export class ListDBUsersTool extends AtlasToolBase { protected async execute({ projectId }: ToolArgs): Promise { this.ensureAuthenticated(); - const data = await this.apiClient!.listDatabaseUsers({ + const data = await this.apiClient.listDatabaseUsers({ params: { path: { groupId: projectId, diff --git a/src/tools/atlas/listProjects.ts b/src/tools/atlas/listProjects.ts index d6b16f89..e3e1e878 100644 --- a/src/tools/atlas/listProjects.ts +++ b/src/tools/atlas/listProjects.ts @@ -9,7 +9,7 @@ export class ListProjectsTool extends AtlasToolBase { protected async execute(): Promise { this.ensureAuthenticated(); - const data = await this.apiClient!.listProjects(); + const data = await this.apiClient.listProjects(); if (!data?.results?.length) { throw new Error("No projects found in your MongoDB Atlas account."); diff --git a/src/tools/mongodb/connect.ts b/src/tools/mongodb/connect.ts index 3ed18fcd..ac93c359 100644 --- a/src/tools/mongodb/connect.ts +++ b/src/tools/mongodb/connect.ts @@ -20,7 +20,7 @@ export class ConnectTool extends MongoDBToolBase { protected async execute({ connectionStringOrClusterName, }: ToolArgs): Promise { - connectionStringOrClusterName ??= config.connectionString || this.state.credentials.connectionString; + connectionStringOrClusterName ??= config.connectionString || this.state.connectionString; if (!connectionStringOrClusterName) { return { content: [ diff --git a/src/tools/mongodb/mongodbTool.ts b/src/tools/mongodb/mongodbTool.ts index cc376aea..1a372222 100644 --- a/src/tools/mongodb/mongodbTool.ts +++ b/src/tools/mongodb/mongodbTool.ts @@ -66,7 +66,6 @@ export abstract class MongoDBToolBase extends ToolBase { }); state.serviceProvider = provider; - state.credentials.connectionString = connectionString; - await state.persistCredentials(); + state.connectionString = connectionString; } } From 15e9e5bc9df1966f00206a7714494bd9a3c851f1 Mon Sep 17 00:00:00 2001 From: Filipe C Menezes Date: Fri, 11 Apr 2025 12:32:23 +0100 Subject: [PATCH 2/8] fix: styles --- src/config.ts | 1 - src/logger.ts | 2 -- 2 files changed, 3 deletions(-) diff --git a/src/config.ts b/src/config.ts index 4a13118c..48595d91 100644 --- a/src/config.ts +++ b/src/config.ts @@ -5,7 +5,6 @@ import argv from "yargs-parser"; import packageJson from "../package.json" with { type: "json" }; import fs from "fs"; import { ReadConcernLevel, ReadPreferenceMode, W } from "mongodb"; -import { log } from "console"; // If we decide to support non-string config options, we'll need to extend the mechanism for parsing // env variables. diff --git a/src/logger.ts b/src/logger.ts index 487d446b..726085dd 100644 --- a/src/logger.ts +++ b/src/logger.ts @@ -1,8 +1,6 @@ import { MongoLogId, MongoLogManager, MongoLogWriter } from "mongodb-log-writer"; -import path from "path"; import config from "./config.js"; import redact from "mongodb-redact"; -import fs from "fs/promises"; import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js"; import { LoggingMessageNotification } from "@modelcontextprotocol/sdk/types.js"; From 7d6892903c802f4db7ba96e7d1b45b04ea547378 Mon Sep 17 00:00:00 2001 From: Filipe C Menezes Date: Fri, 11 Apr 2025 12:48:01 +0100 Subject: [PATCH 3/8] fix: address comments --- src/config.ts | 18 ++++-------------- src/logger.ts | 15 +++++++++++++++ src/state.ts | 1 - src/tools/mongodb/connect.ts | 2 +- src/tools/mongodb/mongodbTool.ts | 1 - 5 files changed, 20 insertions(+), 17 deletions(-) diff --git a/src/config.ts b/src/config.ts index 48595d91..d821715b 100644 --- a/src/config.ts +++ b/src/config.ts @@ -49,21 +49,11 @@ const config = { export default config; function getLogPath(): string { - let localDataPath: string | undefined; + const localDataPath = (process.platform === "win32") ? + path.join(process.env.LOCALAPPDATA || process.env.APPDATA || os.homedir(), "mongodb") + : path.join(os.homedir(), ".mongodb"); - if (process.platform === "win32") { - const appData = process.env.APPDATA; - const localAppData = process.env.LOCALAPPDATA ?? process.env.APPDATA; - if (localAppData && appData) { - localDataPath = path.join(localAppData, "mongodb", "mongodb-mcp"); - } - } - - localDataPath ??= path.join(os.homedir(), ".mongodb", "mongodb-mcp"); - - const logPath = path.join(localDataPath, ".app-logs"); - - fs.mkdirSync(logPath, { recursive: true }); + const logPath = path.join(localDataPath, "mongodb-mcp", ".app-logs"); return logPath; } diff --git a/src/logger.ts b/src/logger.ts index 726085dd..532ff506 100644 --- a/src/logger.ts +++ b/src/logger.ts @@ -1,3 +1,4 @@ +import fs from "fs"; import { MongoLogId, MongoLogManager, MongoLogWriter } from "mongodb-log-writer"; import config from "./config.js"; import redact from "mongodb-redact"; @@ -97,7 +98,21 @@ class ProxyingLogger extends LoggerBase { const logger = new ProxyingLogger(); export default logger; +async function mkdirPromise(path: fs.PathLike, options?: fs.Mode | fs.MakeDirectoryOptions) { + return new Promise((resolve, reject) => { + fs.mkdir(path, options, (err, resultPath) => { + if (err) { + reject(err); + } else { + resolve(resultPath); + } + }); + }); +} + export async function initializeLogger(server: McpServer): Promise { + await mkdirPromise(config.logPath, { recursive: true }); + const manager = new MongoLogManager({ directory: config.logPath, retentionDays: 30, diff --git a/src/state.ts b/src/state.ts index 672f6dd8..c0b0c951 100644 --- a/src/state.ts +++ b/src/state.ts @@ -2,7 +2,6 @@ import { NodeDriverServiceProvider } from "@mongosh/service-provider-node-driver export class State { serviceProvider?: NodeDriverServiceProvider; - connectionString?: string; } const defaultState = new State(); diff --git a/src/tools/mongodb/connect.ts b/src/tools/mongodb/connect.ts index ac93c359..dfba9926 100644 --- a/src/tools/mongodb/connect.ts +++ b/src/tools/mongodb/connect.ts @@ -20,7 +20,7 @@ export class ConnectTool extends MongoDBToolBase { protected async execute({ connectionStringOrClusterName, }: ToolArgs): Promise { - connectionStringOrClusterName ??= config.connectionString || this.state.connectionString; + connectionStringOrClusterName ??= config.connectionString; if (!connectionStringOrClusterName) { return { content: [ diff --git a/src/tools/mongodb/mongodbTool.ts b/src/tools/mongodb/mongodbTool.ts index 1a372222..5c9594e5 100644 --- a/src/tools/mongodb/mongodbTool.ts +++ b/src/tools/mongodb/mongodbTool.ts @@ -66,6 +66,5 @@ export abstract class MongoDBToolBase extends ToolBase { }); state.serviceProvider = provider; - state.connectionString = connectionString; } } From 616a32eca81acf124e64b9df0b7037af450459ee Mon Sep 17 00:00:00 2001 From: Filipe C Menezes Date: Fri, 11 Apr 2025 13:27:23 +0100 Subject: [PATCH 4/8] refactor: move apiClient into state --- src/common/atlas/apiClient.ts | 75 ++++++++++++++++++---------- src/config.ts | 13 ++--- src/server.ts | 6 +-- src/state.ts | 21 ++++++++ src/tools/atlas/atlasTool.ts | 14 +----- src/tools/atlas/createAccessList.ts | 6 +-- src/tools/atlas/createDBUser.ts | 4 +- src/tools/atlas/createFreeCluster.ts | 4 +- src/tools/atlas/inspectAccessList.ts | 4 +- src/tools/atlas/inspectCluster.ts | 4 +- src/tools/atlas/listClusters.ts | 8 +-- src/tools/atlas/listDBUsers.ts | 4 +- src/tools/atlas/listProjects.ts | 4 +- src/tools/atlas/tools.ts | 19 ++++--- 14 files changed, 107 insertions(+), 79 deletions(-) diff --git a/src/common/atlas/apiClient.ts b/src/common/atlas/apiClient.ts index 7384bec9..a3523506 100644 --- a/src/common/atlas/apiClient.ts +++ b/src/common/atlas/apiClient.ts @@ -1,9 +1,11 @@ import config from "../../config.js"; -import createClient, { FetchOptions, Middleware } from "openapi-fetch"; +import createClient, { Client, FetchOptions, Middleware } from "openapi-fetch"; import { AccessToken, ClientCredentials } from "simple-oauth2"; import { paths, operations } from "./openapi.js"; +const ATLAS_API_VERSION = "2025-03-12"; + export class ApiClientError extends Error { response?: Response; @@ -25,38 +27,32 @@ export class ApiClientError extends Error { } export interface ApiClientOptions { - credentials: { + credentials?: { clientId: string; clientSecret: string; }; baseUrl?: string; + userAgent?: string; } export class ApiClient { - private client = createClient({ - baseUrl: config.apiBaseUrl, - headers: { - "User-Agent": config.userAgent, - Accept: `application/vnd.atlas.${config.atlasApiVersion}+json`, - }, - }); - private oauth2Client = new ClientCredentials({ - client: { - id: this.options.credentials.clientId, - secret: this.options.credentials.clientSecret, - }, - auth: { - tokenHost: this.options.baseUrl || config.apiBaseUrl, - tokenPath: "/api/oauth/token", - }, - }); + private options: { + baseUrl: string; + userAgent: string; + credentials?: { + clientId: string; + clientSecret: string; + }; + }; + private client: Client; + private oauth2Client?: ClientCredentials; private accessToken?: AccessToken; private getAccessToken = async () => { - if (!this.accessToken || this.accessToken.expired()) { + if (this.oauth2Client && (!this.accessToken || this.accessToken.expired())) { this.accessToken = await this.oauth2Client.getToken({}); } - return this.accessToken.token.access_token; + return this.accessToken?.token.access_token as string | undefined; }; private authMiddleware = (apiClient: ApiClient): Middleware => ({ @@ -82,8 +78,37 @@ export class ApiClient { }, }); - constructor(private options: ApiClientOptions) { - this.client.use(this.authMiddleware(this)); + constructor(options?: ApiClientOptions) { + const defaultOptions = { + baseUrl: "https://cloud.mongodb.com/", + userAgent: `AtlasMCP/${config.version} (${process.platform}; ${process.arch}; ${process.env.HOSTNAME || "unknown"})`, + }; + + this.options = { + ...defaultOptions, + ...options, + }; + + this.client = createClient({ + baseUrl: this.options.baseUrl, + headers: { + "User-Agent": this.options.userAgent, + Accept: `application/vnd.atlas.${ATLAS_API_VERSION}+json`, + }, + }); + if (this.options.credentials?.clientId && this.options.credentials?.clientSecret) { + this.oauth2Client = new ClientCredentials({ + client: { + id: this.options.credentials.clientId, + secret: this.options.credentials.clientSecret, + }, + auth: { + tokenHost: this.options.baseUrl, + tokenPath: "/api/oauth/token", + }, + }); + this.client.use(this.authMiddleware(this)); + } this.client.use(this.errorMiddleware()); } @@ -91,13 +116,13 @@ export class ApiClient { const accessToken = await this.getAccessToken(); const endpoint = "api/private/ipinfo"; - const url = new URL(endpoint, config.apiBaseUrl); + const url = new URL(endpoint, this.options.baseUrl || config.apiBaseUrl); const response = await fetch(url, { method: "GET", headers: { Accept: "application/json", Authorization: `Bearer ${accessToken}`, - "User-Agent": config.userAgent, + "User-Agent": this.options.userAgent, }, }); diff --git a/src/config.ts b/src/config.ts index d821715b..ecdf32ad 100644 --- a/src/config.ts +++ b/src/config.ts @@ -3,13 +3,12 @@ import os from "os"; import argv from "yargs-parser"; import packageJson from "../package.json" with { type: "json" }; -import fs from "fs"; import { ReadConcernLevel, ReadPreferenceMode, W } from "mongodb"; // If we decide to support non-string config options, we'll need to extend the mechanism for parsing // env variables. interface UserConfig { - apiBaseUrl: string; + apiBaseUrl?: string; apiClientId?: string; apiClientSecret?: string; logPath: string; @@ -23,7 +22,6 @@ interface UserConfig { } const defaults: UserConfig = { - apiBaseUrl: "https://cloud.mongodb.com/", logPath: getLogPath(), connectOptions: { readConcern: "local", @@ -41,17 +39,16 @@ const mergedUserConfig = { const config = { ...mergedUserConfig, - atlasApiVersion: `2025-03-12`, version: packageJson.version, - userAgent: `AtlasMCP/${packageJson.version} (${process.platform}; ${process.arch}; ${process.env.HOSTNAME || "unknown"})`, }; export default config; function getLogPath(): string { - const localDataPath = (process.platform === "win32") ? - path.join(process.env.LOCALAPPDATA || process.env.APPDATA || os.homedir(), "mongodb") - : path.join(os.homedir(), ".mongodb"); + const localDataPath = + process.platform === "win32" + ? path.join(process.env.LOCALAPPDATA || process.env.APPDATA || os.homedir(), "mongodb") + : path.join(os.homedir(), ".mongodb"); const logPath = path.join(localDataPath, "mongodb-mcp", ".app-logs"); diff --git a/src/server.ts b/src/server.ts index b1d95a17..85d97c1b 100644 --- a/src/server.ts +++ b/src/server.ts @@ -1,5 +1,4 @@ import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js"; -import { ApiClient } from "./common/atlas/apiClient.js"; import defaultState, { State } from "./state.js"; import { Transport } from "@modelcontextprotocol/sdk/shared/transport.js"; import { registerAtlasTools } from "./tools/atlas/tools.js"; @@ -10,7 +9,6 @@ import { mongoLogId } from "mongodb-log-writer"; export class Server { state: State = defaultState; - apiClient?: ApiClient; private server?: McpServer; async connect(transport: Transport) { @@ -21,12 +19,12 @@ export class Server { this.server.server.registerCapabilities({ logging: {} }); - registerAtlasTools(this.server, this.state, this.apiClient); + registerAtlasTools(this.server, this.state); registerMongoDBTools(this.server, this.state); await this.server.connect(transport); await initializeLogger(this.server); - + logger.info(mongoLogId(1_000_004), "server", `Server started with transport ${transport.constructor.name}`); } diff --git a/src/state.ts b/src/state.ts index c0b0c951..f4e694bd 100644 --- a/src/state.ts +++ b/src/state.ts @@ -1,7 +1,28 @@ import { NodeDriverServiceProvider } from "@mongosh/service-provider-node-driver"; +import { ApiClient } from "./common/atlas/apiClient.js"; +import config from "./config.js"; export class State { serviceProvider?: NodeDriverServiceProvider; + apiClient?: ApiClient; + + ensureApiClient(): asserts this is { apiClient: ApiClient } { + if (!this.apiClient) { + if (!config.apiClientId || !config.apiClientSecret) { + throw new Error( + "Not authenticated make sure to configure MCP server with MDB_MCP_API_CLIENT_ID and MDB_MCP_API_CLIENT_SECRET environment variables." + ); + } + + this.apiClient = new ApiClient({ + baseUrl: config.apiBaseUrl, + credentials: { + clientId: config.apiClientId, + clientSecret: config.apiClientSecret, + }, + }); + } + } } const defaultState = new State(); diff --git a/src/tools/atlas/atlasTool.ts b/src/tools/atlas/atlasTool.ts index 2cd433ed..4aef681c 100644 --- a/src/tools/atlas/atlasTool.ts +++ b/src/tools/atlas/atlasTool.ts @@ -1,20 +1,8 @@ import { ToolBase } from "../tool.js"; -import { ApiClient } from "../../common/atlas/apiClient.js"; import { State } from "../../state.js"; export abstract class AtlasToolBase extends ToolBase { - constructor( - state: State, - protected apiClient?: ApiClient - ) { + constructor(state: State) { super(state); } - - protected ensureAuthenticated(): asserts this is { apiClient: ApiClient } { - if (!this.apiClient) { - throw new Error( - "Not authenticated make sure to configure MCP server with MDB_MCP_API_CLIENT_ID and MDB_MCP_API_CLIENT_SECRET environment variables." - ); - } - } } diff --git a/src/tools/atlas/createAccessList.ts b/src/tools/atlas/createAccessList.ts index 8b95da67..09a991c8 100644 --- a/src/tools/atlas/createAccessList.ts +++ b/src/tools/atlas/createAccessList.ts @@ -26,7 +26,7 @@ export class CreateAccessListTool extends AtlasToolBase { comment, currentIpAddress, }: ToolArgs): Promise { - this.ensureAuthenticated(); + this.state.ensureApiClient(); if (!ipAddresses?.length && !cidrBlocks?.length && !currentIpAddress) { throw new Error("One of ipAddresses, cidrBlocks, currentIpAddress must be provided."); @@ -39,7 +39,7 @@ export class CreateAccessListTool extends AtlasToolBase { })); if (currentIpAddress) { - const currentIp = await this.apiClient.getIpInfo(); + const currentIp = await this.state.apiClient.getIpInfo(); const input = { groupId: projectId, ipAddress: currentIp.currentIpv4Address, @@ -56,7 +56,7 @@ export class CreateAccessListTool extends AtlasToolBase { const inputs = [...ipInputs, ...cidrInputs]; - await this.apiClient.createProjectIpAccessList({ + await this.state.apiClient.createProjectIpAccessList({ params: { path: { groupId: projectId, diff --git a/src/tools/atlas/createDBUser.ts b/src/tools/atlas/createDBUser.ts index ccb83f28..2698f0d8 100644 --- a/src/tools/atlas/createDBUser.ts +++ b/src/tools/atlas/createDBUser.ts @@ -33,7 +33,7 @@ export class CreateDBUserTool extends AtlasToolBase { roles, clusters, }: ToolArgs): Promise { - this.ensureAuthenticated(); + this.state.ensureApiClient(); const input = { groupId: projectId, @@ -53,7 +53,7 @@ export class CreateDBUserTool extends AtlasToolBase { : undefined, } as CloudDatabaseUser; - await this.apiClient.createDatabaseUser({ + await this.state.apiClient.createDatabaseUser({ params: { path: { groupId: projectId, diff --git a/src/tools/atlas/createFreeCluster.ts b/src/tools/atlas/createFreeCluster.ts index 513a07f7..8179883f 100644 --- a/src/tools/atlas/createFreeCluster.ts +++ b/src/tools/atlas/createFreeCluster.ts @@ -14,7 +14,7 @@ export class CreateFreeClusterTool extends AtlasToolBase { }; protected async execute({ projectId, name, region }: ToolArgs): Promise { - this.ensureAuthenticated(); + this.state.ensureApiClient(); const input = { groupId: projectId, @@ -38,7 +38,7 @@ export class CreateFreeClusterTool extends AtlasToolBase { terminationProtectionEnabled: false, } as unknown as ClusterDescription20240805; - await this.apiClient.createCluster({ + await this.state.apiClient.createCluster({ params: { path: { groupId: projectId, diff --git a/src/tools/atlas/inspectAccessList.ts b/src/tools/atlas/inspectAccessList.ts index 6be93723..c66cf5dc 100644 --- a/src/tools/atlas/inspectAccessList.ts +++ b/src/tools/atlas/inspectAccessList.ts @@ -11,9 +11,9 @@ export class InspectAccessListTool extends AtlasToolBase { }; protected async execute({ projectId }: ToolArgs): Promise { - this.ensureAuthenticated(); + this.state.ensureApiClient(); - const accessList = await this.apiClient.listProjectIpAccessLists({ + const accessList = await this.state.apiClient.listProjectIpAccessLists({ params: { path: { groupId: projectId, diff --git a/src/tools/atlas/inspectCluster.ts b/src/tools/atlas/inspectCluster.ts index 8a18084d..9ad35a46 100644 --- a/src/tools/atlas/inspectCluster.ts +++ b/src/tools/atlas/inspectCluster.ts @@ -13,9 +13,9 @@ export class InspectClusterTool extends AtlasToolBase { }; protected async execute({ projectId, clusterName }: ToolArgs): Promise { - this.ensureAuthenticated(); + this.state.ensureApiClient(); - const cluster = await this.apiClient.getCluster({ + const cluster = await this.state.apiClient.getCluster({ params: { path: { groupId: projectId, diff --git a/src/tools/atlas/listClusters.ts b/src/tools/atlas/listClusters.ts index 93da22f0..eb38f74e 100644 --- a/src/tools/atlas/listClusters.ts +++ b/src/tools/atlas/listClusters.ts @@ -12,14 +12,14 @@ export class ListClustersTool extends AtlasToolBase { }; protected async execute({ projectId }: ToolArgs): Promise { - this.ensureAuthenticated(); + this.state.ensureApiClient(); if (!projectId) { - const data = await this.apiClient.listClustersForAllProjects(); + const data = await this.state.apiClient.listClustersForAllProjects(); return this.formatAllClustersTable(data); } else { - const project = await this.apiClient.getProject({ + const project = await this.state.apiClient.getProject({ params: { path: { groupId: projectId, @@ -31,7 +31,7 @@ export class ListClustersTool extends AtlasToolBase { throw new Error(`Project with ID "${projectId}" not found.`); } - const data = await this.apiClient.listClusters({ + const data = await this.state.apiClient.listClusters({ params: { path: { groupId: project.id || "", diff --git a/src/tools/atlas/listDBUsers.ts b/src/tools/atlas/listDBUsers.ts index c2983716..d9712b1e 100644 --- a/src/tools/atlas/listDBUsers.ts +++ b/src/tools/atlas/listDBUsers.ts @@ -12,9 +12,9 @@ export class ListDBUsersTool extends AtlasToolBase { }; protected async execute({ projectId }: ToolArgs): Promise { - this.ensureAuthenticated(); + this.state.ensureApiClient(); - const data = await this.apiClient.listDatabaseUsers({ + const data = await this.state.apiClient.listDatabaseUsers({ params: { path: { groupId: projectId, diff --git a/src/tools/atlas/listProjects.ts b/src/tools/atlas/listProjects.ts index e3e1e878..17fa6668 100644 --- a/src/tools/atlas/listProjects.ts +++ b/src/tools/atlas/listProjects.ts @@ -7,9 +7,9 @@ export class ListProjectsTool extends AtlasToolBase { protected argsShape = {}; protected async execute(): Promise { - this.ensureAuthenticated(); + this.state.ensureApiClient(); - const data = await this.apiClient.listProjects(); + const data = await this.state.apiClient.listProjects(); if (!data?.results?.length) { throw new Error("No projects found in your MongoDB Atlas account."); diff --git a/src/tools/atlas/tools.ts b/src/tools/atlas/tools.ts index 41845730..5e717306 100644 --- a/src/tools/atlas/tools.ts +++ b/src/tools/atlas/tools.ts @@ -1,5 +1,4 @@ import { ToolBase } from "../tool.js"; -import { ApiClient } from "../../common/atlas/apiClient.js"; import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js"; import { State } from "../../state.js"; import { ListClustersTool } from "./listClusters.js"; @@ -11,16 +10,16 @@ import { InspectAccessListTool } from "./inspectAccessList.js"; import { ListDBUsersTool } from "./listDBUsers.js"; import { CreateDBUserTool } from "./createDBUser.js"; -export function registerAtlasTools(server: McpServer, state: State, apiClient?: ApiClient) { +export function registerAtlasTools(server: McpServer, state: State) { const tools: ToolBase[] = [ - new ListClustersTool(state, apiClient), - new ListProjectsTool(state, apiClient), - new InspectClusterTool(state, apiClient), - new CreateFreeClusterTool(state, apiClient), - new CreateAccessListTool(state, apiClient), - new InspectAccessListTool(state, apiClient), - new ListDBUsersTool(state, apiClient), - new CreateDBUserTool(state, apiClient), + new ListClustersTool(state), + new ListProjectsTool(state), + new InspectClusterTool(state), + new CreateFreeClusterTool(state), + new CreateAccessListTool(state), + new InspectAccessListTool(state), + new ListDBUsersTool(state), + new CreateDBUserTool(state), ]; for (const tool of tools) { From 3b3fe7944fc388e36a3d6d82e7c6e2b42ebf533d Mon Sep 17 00:00:00 2001 From: Filipe C Menezes Date: Fri, 11 Apr 2025 13:28:01 +0100 Subject: [PATCH 5/8] fix: logs before server --- src/server.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/server.ts b/src/server.ts index 85d97c1b..5530658c 100644 --- a/src/server.ts +++ b/src/server.ts @@ -22,8 +22,8 @@ export class Server { registerAtlasTools(this.server, this.state); registerMongoDBTools(this.server, this.state); - await this.server.connect(transport); await initializeLogger(this.server); + await this.server.connect(transport); logger.info(mongoLogId(1_000_004), "server", `Server started with transport ${transport.constructor.name}`); } From 2eae0de531af7cb6deb735edf5e92ef016a98cbf Mon Sep 17 00:00:00 2001 From: Filipe C Menezes Date: Fri, 11 Apr 2025 13:30:38 +0100 Subject: [PATCH 6/8] fix: remove unneeded config --- src/common/atlas/apiClient.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/common/atlas/apiClient.ts b/src/common/atlas/apiClient.ts index a3523506..3198ea70 100644 --- a/src/common/atlas/apiClient.ts +++ b/src/common/atlas/apiClient.ts @@ -116,7 +116,7 @@ export class ApiClient { const accessToken = await this.getAccessToken(); const endpoint = "api/private/ipinfo"; - const url = new URL(endpoint, this.options.baseUrl || config.apiBaseUrl); + const url = new URL(endpoint, this.options.baseUrl); const response = await fetch(url, { method: "GET", headers: { From 594c644c5228f9a30d6819f8646584a95f8ec3a3 Mon Sep 17 00:00:00 2001 From: Filipe C Menezes Date: Fri, 11 Apr 2025 13:32:31 +0100 Subject: [PATCH 7/8] dep: remove @napi-rs/keyring --- package-lock.json | 216 ---------------------------------------------- package.json | 1 - 2 files changed, 217 deletions(-) diff --git a/package-lock.json b/package-lock.json index 27ab6a0f..0ec360e2 100644 --- a/package-lock.json +++ b/package-lock.json @@ -11,7 +11,6 @@ "dependencies": { "@mongodb-js/devtools-connect": "^3.7.2", "@mongosh/service-provider-node-driver": "^3.6.0", - "@napi-rs/keyring": "^1.1.6", "@types/express": "^5.0.1", "bson": "^6.10.3", "mongodb": "^6.15.0", @@ -3643,221 +3642,6 @@ "node": ">=14.15.1" } }, - "node_modules/@napi-rs/keyring": { - "version": "1.1.6", - "resolved": "https://registry.npmjs.org/@napi-rs/keyring/-/keyring-1.1.6.tgz", - "integrity": "sha512-e6xoYELSMyaxcXv4MmEHhf0oOGsMnfWMmeu84CD91ICMgMH1I1vrLSMFpiPEQz03xD+pNQgAkQ7DwwBDozCuvw==", - "license": "MIT", - "engines": { - "node": ">= 10" - }, - "optionalDependencies": { - "@napi-rs/keyring-darwin-arm64": "1.1.6", - "@napi-rs/keyring-darwin-x64": "1.1.6", - "@napi-rs/keyring-freebsd-x64": "1.1.6", - "@napi-rs/keyring-linux-arm-gnueabihf": "1.1.6", - "@napi-rs/keyring-linux-arm64-gnu": "1.1.6", - "@napi-rs/keyring-linux-arm64-musl": "1.1.6", - "@napi-rs/keyring-linux-riscv64-gnu": "1.1.6", - "@napi-rs/keyring-linux-x64-gnu": "1.1.6", - "@napi-rs/keyring-linux-x64-musl": "1.1.6", - "@napi-rs/keyring-win32-arm64-msvc": "1.1.6", - "@napi-rs/keyring-win32-ia32-msvc": "1.1.6", - "@napi-rs/keyring-win32-x64-msvc": "1.1.6" - } - }, - "node_modules/@napi-rs/keyring-darwin-arm64": { - "version": "1.1.6", - "resolved": "https://registry.npmjs.org/@napi-rs/keyring-darwin-arm64/-/keyring-darwin-arm64-1.1.6.tgz", - "integrity": "sha512-8N+qvM+O6OSU59BTgDP/PvqYhoqfOcD2HGy1NgRFo1B0DRmkTp4U/DGZrV4Pk/nOP6Uf0PLqznfx3a/M8O5sjQ==", - "cpu": [ - "arm64" - ], - "license": "MIT", - "optional": true, - "os": [ - "darwin" - ], - "engines": { - "node": ">= 10" - } - }, - "node_modules/@napi-rs/keyring-darwin-x64": { - "version": "1.1.6", - "resolved": "https://registry.npmjs.org/@napi-rs/keyring-darwin-x64/-/keyring-darwin-x64-1.1.6.tgz", - "integrity": "sha512-r3Jgc5/ubfaao6Lmk/USA13IwU/GEVLP8NDfg5gYXjPVllU6bWnAaEDHVg7q4vl51kViwj9ELo6XTmOeJFut6A==", - "cpu": [ - "x64" - ], - "license": "MIT", - "optional": true, - "os": [ - "darwin" - ], - "engines": { - "node": ">= 10" - } - }, - "node_modules/@napi-rs/keyring-freebsd-x64": { - "version": "1.1.6", - "resolved": "https://registry.npmjs.org/@napi-rs/keyring-freebsd-x64/-/keyring-freebsd-x64-1.1.6.tgz", - "integrity": "sha512-ayG396jZAt7j820gsEyW/LJKn+rf9KtgSPq1NKpvu84Y5GXopoFLyjMIP7wYZ1RLBL6SGKy27/f8S4f6YZ4DuA==", - "cpu": [ - "x64" - ], - "license": "MIT", - "optional": true, - "os": [ - "freebsd" - ], - "engines": { - "node": ">= 10" - } - }, - "node_modules/@napi-rs/keyring-linux-arm-gnueabihf": { - "version": "1.1.6", - "resolved": "https://registry.npmjs.org/@napi-rs/keyring-linux-arm-gnueabihf/-/keyring-linux-arm-gnueabihf-1.1.6.tgz", - "integrity": "sha512-8nXavgxcaUTUxyFHR+PEQF7eC8rITlYZNUmlf5amTb36y5bkNKrc3QLvCxjtbFSR/+KYzMi3vydoqNmFpF616w==", - "cpu": [ - "arm" - ], - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">= 10" - } - }, - "node_modules/@napi-rs/keyring-linux-arm64-gnu": { - "version": "1.1.6", - "resolved": "https://registry.npmjs.org/@napi-rs/keyring-linux-arm64-gnu/-/keyring-linux-arm64-gnu-1.1.6.tgz", - "integrity": "sha512-qsI2NTAxGD3mBhZvdyYGL+N0n1D/NAjV0zCpTsFKKSzdpIrQJ0nM5Y0HxlLi6TsHm61dMyXHkdHb0ut8AzTcGA==", - "cpu": [ - "arm64" - ], - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">= 10" - } - }, - "node_modules/@napi-rs/keyring-linux-arm64-musl": { - "version": "1.1.6", - "resolved": "https://registry.npmjs.org/@napi-rs/keyring-linux-arm64-musl/-/keyring-linux-arm64-musl-1.1.6.tgz", - "integrity": "sha512-SB/2A4LtL+SrS2aZXl3rWBtyCVB2aG2zAU56kOGFDGwRZM2tqaITuQoM1QLOAMwu0eksN/Xedy95Yn2rkRH0nQ==", - "cpu": [ - "arm64" - ], - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">= 10" - } - }, - "node_modules/@napi-rs/keyring-linux-riscv64-gnu": { - "version": "1.1.6", - "resolved": "https://registry.npmjs.org/@napi-rs/keyring-linux-riscv64-gnu/-/keyring-linux-riscv64-gnu-1.1.6.tgz", - "integrity": "sha512-BcjXf33T2CoVgS87SvZ62Y6xxkbenNIeldy0r8O5nz6zFgN+wYB0scz5ulvowEYBQnhi4fmbxfneeqM/0HUOeA==", - "cpu": [ - "riscv64" - ], - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">= 10" - } - }, - "node_modules/@napi-rs/keyring-linux-x64-gnu": { - "version": "1.1.6", - "resolved": "https://registry.npmjs.org/@napi-rs/keyring-linux-x64-gnu/-/keyring-linux-x64-gnu-1.1.6.tgz", - "integrity": "sha512-eK0OxCBI6Wl8rFHYynrtEID6pxOwhPfnpIIpul7UPeqCCMJSyZpFN4lFP3oZ4vqX/6FnWjwMrR7IGbPgivdMjA==", - "cpu": [ - "x64" - ], - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">= 10" - } - }, - "node_modules/@napi-rs/keyring-linux-x64-musl": { - "version": "1.1.6", - "resolved": "https://registry.npmjs.org/@napi-rs/keyring-linux-x64-musl/-/keyring-linux-x64-musl-1.1.6.tgz", - "integrity": "sha512-Qb3NP98KFq4jXmk9PUQlcYrHjbzsBTtG+OOxX4YxUNKTGuUaIOGP79lB0w7jhns2oHdq8DwkW2ugzlmGSUaRSw==", - "cpu": [ - "x64" - ], - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">= 10" - } - }, - "node_modules/@napi-rs/keyring-win32-arm64-msvc": { - "version": "1.1.6", - "resolved": "https://registry.npmjs.org/@napi-rs/keyring-win32-arm64-msvc/-/keyring-win32-arm64-msvc-1.1.6.tgz", - "integrity": "sha512-e794gO2CLD0P7JN2DVPT5CC60k3WmNWTWU5BVoQM8Hj0NYebx7j6LyxMIpdb2cztOHHiv7iltEHekgutf0TMlA==", - "cpu": [ - "arm64" - ], - "license": "MIT", - "optional": true, - "os": [ - "win32" - ], - "engines": { - "node": ">= 10" - } - }, - "node_modules/@napi-rs/keyring-win32-ia32-msvc": { - "version": "1.1.6", - "resolved": "https://registry.npmjs.org/@napi-rs/keyring-win32-ia32-msvc/-/keyring-win32-ia32-msvc-1.1.6.tgz", - "integrity": "sha512-SUPafl6vKRMQBKZoSwIeBFZ+c7AGEKUy6mpAD9fVHDKHOBWP3VpHKda4YIlgGtQd3SxH0bjfqJ078Z5SYsDYZQ==", - "cpu": [ - "ia32" - ], - "license": "MIT", - "optional": true, - "os": [ - "win32" - ], - "engines": { - "node": ">= 10" - } - }, - "node_modules/@napi-rs/keyring-win32-x64-msvc": { - "version": "1.1.6", - "resolved": "https://registry.npmjs.org/@napi-rs/keyring-win32-x64-msvc/-/keyring-win32-x64-msvc-1.1.6.tgz", - "integrity": "sha512-FkNhM1x5ijFzGSrRcshRxUxQSrrjxl4wCmvRcXnimWreOHyzNotT+/1EZtSfM/k8yhdK0HEkkVIMQl0UqfioRw==", - "cpu": [ - "x64" - ], - "license": "MIT", - "optional": true, - "os": [ - "win32" - ], - "engines": { - "node": ">= 10" - } - }, "node_modules/@nodelib/fs.scandir": { "version": "2.1.5", "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz", diff --git a/package.json b/package.json index b91e20f6..80c2b88d 100644 --- a/package.json +++ b/package.json @@ -60,7 +60,6 @@ "dependencies": { "@mongodb-js/devtools-connect": "^3.7.2", "@mongosh/service-provider-node-driver": "^3.6.0", - "@napi-rs/keyring": "^1.1.6", "@types/express": "^5.0.1", "bson": "^6.10.3", "mongodb": "^6.15.0", From 5b899e37f922aa182d57576c4825a5c9e40dd69a Mon Sep 17 00:00:00 2001 From: Filipe C Menezes Date: Fri, 11 Apr 2025 13:35:47 +0100 Subject: [PATCH 8/8] fix: tests --- tests/integration/helpers.ts | 16 +--------------- 1 file changed, 1 insertion(+), 15 deletions(-) diff --git a/tests/integration/helpers.ts b/tests/integration/helpers.ts index d20da21a..a08b3eea 100644 --- a/tests/integration/helpers.ts +++ b/tests/integration/helpers.ts @@ -4,23 +4,12 @@ import { Server } from "../../src/server.js"; import runner, { MongoCluster } from "mongodb-runner"; import path from "path"; import fs from "fs/promises"; -import defaultState from "../../src/state.js"; -export async function setupIntegrationTest({ mockStateStore = true }: { mockStateStore?: boolean } = {}): Promise<{ +export async function setupIntegrationTest(): Promise<{ client: Client; server: Server; teardown: () => Promise; }> { - let loadCredentialsMock: jest.SpyInstance | undefined; - let saveCredentialsMock: jest.SpyInstance | undefined; - if (mockStateStore) { - // Mock the load/persist credentials method to avoid state loading/restore messing up with the tests - loadCredentialsMock = jest.spyOn(defaultState, "loadCredentials").mockImplementation(() => Promise.resolve()); - saveCredentialsMock = jest - .spyOn(defaultState, "persistCredentials") - .mockImplementation(() => Promise.resolve()); - } - const clientTransport = new InMemoryTransport(); const serverTransport = new InMemoryTransport(); @@ -50,9 +39,6 @@ export async function setupIntegrationTest({ mockStateStore = true }: { mockStat teardown: async () => { await client.close(); await server.close(); - - loadCredentialsMock?.mockRestore(); - saveCredentialsMock?.mockRestore(); }, }; }