From 0833ff38da261cad446680aeaa5cacb34620c7a9 Mon Sep 17 00:00:00 2001 From: Filipe Constantinov Menezes Date: Fri, 4 Jul 2025 17:38:12 +0100 Subject: [PATCH 1/5] chore: avoid log files on disk and move them to console for containers --- src/common/container.ts | 24 ++++++++++++++++++++++++ src/logger.ts | 9 +++++++-- src/server.ts | 11 +++++++++-- src/telemetry/telemetry.ts | 27 ++------------------------- 4 files changed, 42 insertions(+), 29 deletions(-) create mode 100644 src/common/container.ts diff --git a/src/common/container.ts b/src/common/container.ts new file mode 100644 index 00000000..11bb588c --- /dev/null +++ b/src/common/container.ts @@ -0,0 +1,24 @@ +import fs from "fs/promises"; + +export async function detectContainerEnv(): Promise { + if (process.platform !== "linux") { + return false; // we only support linux containers for now + } + + if (process.env.container) { + return true; + } + + const exists = await Promise.all( + ["/.dockerenv", "/run/.containerenv", "/var/run/.containerenv"].map(async (file) => { + try { + await fs.access(file); + return true; + } catch { + return false; + } + }) + ); + + return exists.includes(true); +} \ No newline at end of file diff --git a/src/logger.ts b/src/logger.ts index 8157324b..8a49d568 100644 --- a/src/logger.ts +++ b/src/logger.ts @@ -180,11 +180,16 @@ class CompositeLogger extends LoggerBase { const logger = new CompositeLogger(); export default logger; -export async function initializeLogger(server: McpServer, logPath: string): Promise { +export async function setStdioPreset(server: McpServer, logPath: string): Promise { const diskLogger = await DiskLogger.fromPath(logPath); const mcpLogger = new McpLogger(server); logger.setLoggers(mcpLogger, diskLogger); +} + +export function setDockerPreset(server: McpServer): void { + const mcpLogger = new McpLogger(server); + const consoleLogger = new ConsoleLogger(); - return logger; + logger.setLoggers(mcpLogger, consoleLogger); } diff --git a/src/server.ts b/src/server.ts index b0e8e19c..ddc51cb3 100644 --- a/src/server.ts +++ b/src/server.ts @@ -3,7 +3,7 @@ import { Session } from "./session.js"; import { Transport } from "@modelcontextprotocol/sdk/shared/transport.js"; import { AtlasTools } from "./tools/atlas/tools.js"; import { MongoDbTools } from "./tools/mongodb/tools.js"; -import logger, { initializeLogger, LogId } from "./logger.js"; +import logger, { setStdioPreset, setDockerPreset, LogId } from "./logger.js"; import { ObjectId } from "mongodb"; import { Telemetry } from "./telemetry/telemetry.js"; import { UserConfig } from "./config.js"; @@ -11,6 +11,7 @@ import { type ServerEvent } from "./telemetry/types.js"; import { type ServerCommand } from "./telemetry/types.js"; import { CallToolRequestSchema, CallToolResult } from "@modelcontextprotocol/sdk/types.js"; import assert from "assert"; +import { detectContainerEnv } from "./common/container.js"; export interface ServerOptions { session: Session; @@ -63,7 +64,13 @@ export class Server { return existingHandler(request, extra); }); - await initializeLogger(this.mcpServer, this.userConfig.logPath); + const containerEnv = await detectContainerEnv(); + + if (containerEnv) { + setDockerPreset(this.mcpServer); + } else { + await setStdioPreset(this.mcpServer, this.userConfig.logPath); + } await this.mcpServer.connect(transport); diff --git a/src/telemetry/telemetry.ts b/src/telemetry/telemetry.ts index 5d0ad827..f1e24e20 100644 --- a/src/telemetry/telemetry.ts +++ b/src/telemetry/telemetry.ts @@ -7,7 +7,7 @@ import { MACHINE_METADATA } from "./constants.js"; import { EventCache } from "./eventCache.js"; import nodeMachineId from "node-machine-id"; import { getDeviceId } from "@mongodb-js/device-id"; -import fs from "fs/promises"; +import { detectContainerEnv } from "../common/container.js"; type EventResult = { success: boolean; @@ -53,29 +53,6 @@ export class Telemetry { return instance; } - private async isContainerEnv(): Promise { - if (process.platform !== "linux") { - return false; // we only support linux containers for now - } - - if (process.env.container) { - return true; - } - - const exists = await Promise.all( - ["/.dockerenv", "/run/.containerenv", "/var/run/.containerenv"].map(async (file) => { - try { - await fs.access(file); - return true; - } catch { - return false; - } - }) - ); - - return exists.includes(true); - } - private async setup(): Promise { if (!this.isTelemetryEnabled()) { return; @@ -98,7 +75,7 @@ export class Telemetry { }, abortSignal: this.deviceIdAbortController.signal, }), - this.isContainerEnv(), + detectContainerEnv(), ]); const [deviceId, containerEnv] = await this.setupPromise; From e13a3d21095dc9986cab940097ab1e66d493d6da Mon Sep 17 00:00:00 2001 From: Filipe Constantinov Menezes Date: Fri, 4 Jul 2025 17:42:17 +0100 Subject: [PATCH 2/5] fix: format --- src/common/container.ts | 2 +- src/logger.ts | 2 +- src/server.ts | 4 ++-- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/common/container.ts b/src/common/container.ts index 11bb588c..e76290a0 100644 --- a/src/common/container.ts +++ b/src/common/container.ts @@ -21,4 +21,4 @@ export async function detectContainerEnv(): Promise { ); return exists.includes(true); -} \ No newline at end of file +} diff --git a/src/logger.ts b/src/logger.ts index 8a49d568..354d8956 100644 --- a/src/logger.ts +++ b/src/logger.ts @@ -187,7 +187,7 @@ export async function setStdioPreset(server: McpServer, logPath: string): Promis logger.setLoggers(mcpLogger, diskLogger); } -export function setDockerPreset(server: McpServer): void { +export function setContainerPreset(server: McpServer): void { const mcpLogger = new McpLogger(server); const consoleLogger = new ConsoleLogger(); diff --git a/src/server.ts b/src/server.ts index ddc51cb3..31a99ded 100644 --- a/src/server.ts +++ b/src/server.ts @@ -3,7 +3,7 @@ import { Session } from "./session.js"; import { Transport } from "@modelcontextprotocol/sdk/shared/transport.js"; import { AtlasTools } from "./tools/atlas/tools.js"; import { MongoDbTools } from "./tools/mongodb/tools.js"; -import logger, { setStdioPreset, setDockerPreset, LogId } from "./logger.js"; +import logger, { setStdioPreset, setContainerPreset, LogId } from "./logger.js"; import { ObjectId } from "mongodb"; import { Telemetry } from "./telemetry/telemetry.js"; import { UserConfig } from "./config.js"; @@ -67,7 +67,7 @@ export class Server { const containerEnv = await detectContainerEnv(); if (containerEnv) { - setDockerPreset(this.mcpServer); + setContainerPreset(this.mcpServer); } else { await setStdioPreset(this.mcpServer, this.userConfig.logPath); } From 328e8c14b097ea68f0bd0de0520240d21da0add6 Mon Sep 17 00:00:00 2001 From: Filipe Constantinov Menezes Date: Fri, 4 Jul 2025 17:46:05 +0100 Subject: [PATCH 3/5] fix: cache --- src/common/container.ts | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/src/common/container.ts b/src/common/container.ts index e76290a0..86495964 100644 --- a/src/common/container.ts +++ b/src/common/container.ts @@ -1,6 +1,8 @@ import fs from "fs/promises"; -export async function detectContainerEnv(): Promise { +let containerEnv: boolean | undefined; + +async function internalDetectContainerEnv(): Promise { if (process.platform !== "linux") { return false; // we only support linux containers for now } @@ -22,3 +24,12 @@ export async function detectContainerEnv(): Promise { return exists.includes(true); } + +export async function detectContainerEnv(): Promise { + if (containerEnv !== undefined) { + return containerEnv; + } + + containerEnv = await internalDetectContainerEnv(); + return containerEnv; +} From 22aac5afb6637593865847f74a472564967d586a Mon Sep 17 00:00:00 2001 From: Filipe Constantinov Menezes Date: Tue, 8 Jul 2025 07:24:12 +0100 Subject: [PATCH 4/5] fix: comments --- src/common/container.ts | 46 ++++++++++++++++++++--------------------- 1 file changed, 23 insertions(+), 23 deletions(-) diff --git a/src/common/container.ts b/src/common/container.ts index 86495964..696835ae 100644 --- a/src/common/container.ts +++ b/src/common/container.ts @@ -2,34 +2,34 @@ import fs from "fs/promises"; let containerEnv: boolean | undefined; -async function internalDetectContainerEnv(): Promise { - if (process.platform !== "linux") { - return false; // we only support linux containers for now +export async function detectContainerEnv(): Promise { + if (containerEnv !== undefined) { + return containerEnv; } - if (process.env.container) { - return true; - } + const detect = async function (): Promise { + if (process.platform !== "linux") { + return false; // we only support linux containers for now + } - const exists = await Promise.all( - ["/.dockerenv", "/run/.containerenv", "/var/run/.containerenv"].map(async (file) => { - try { - await fs.access(file); - return true; - } catch { - return false; - } - }) - ); + if (process.env.container) { + return true; + } - return exists.includes(true); -} + const exists = await Promise.all( + ["/.dockerenv", "/run/.containerenv", "/var/run/.containerenv"].map(async (file) => { + try { + await fs.access(file); + return true; + } catch { + return false; + } + }) + ); -export async function detectContainerEnv(): Promise { - if (containerEnv !== undefined) { - return containerEnv; - } + return exists.includes(true); + }; - containerEnv = await internalDetectContainerEnv(); + containerEnv = await detect(); return containerEnv; } From 76e006853b2ff9bef2664c44a12940abd9678ad5 Mon Sep 17 00:00:00 2001 From: Filipe Constantinov Menezes Date: Tue, 8 Jul 2025 07:34:38 +0100 Subject: [PATCH 5/5] fix: tests --- tests/integration/tools/atlas/clusters.test.ts | 17 ++++++++++++++--- 1 file changed, 14 insertions(+), 3 deletions(-) diff --git a/tests/integration/tools/atlas/clusters.test.ts b/tests/integration/tools/atlas/clusters.test.ts index 166ee637..8bb19bda 100644 --- a/tests/integration/tools/atlas/clusters.test.ts +++ b/tests/integration/tools/atlas/clusters.test.ts @@ -1,6 +1,7 @@ import { Session } from "../../../../src/session.js"; import { expectDefined } from "../../helpers.js"; import { describeWithAtlas, withProject, randomId } from "./atlasHelpers.js"; +import { ClusterDescription20240805 } from "../../../../src/common/atlas/openapi.js"; import { CallToolResult } from "@modelcontextprotocol/sdk/types.js"; function sleep(ms: number) { @@ -33,7 +34,12 @@ async function deleteAndWaitCluster(session: Session, projectId: string, cluster } } -async function waitClusterState(session: Session, projectId: string, clusterName: string, state: string) { +async function waitCluster( + session: Session, + projectId: string, + clusterName: string, + check: (cluster: ClusterDescription20240805) => boolean | Promise +) { while (true) { const cluster = await session.apiClient.getCluster({ params: { @@ -43,7 +49,7 @@ async function waitClusterState(session: Session, projectId: string, clusterName }, }, }); - if (cluster?.stateName === state) { + if (await check(cluster)) { return; } await sleep(1000); @@ -142,7 +148,12 @@ describeWithAtlas("clusters", (integration) => { describe("atlas-connect-cluster", () => { beforeAll(async () => { const projectId = getProjectId(); - await waitClusterState(integration.mcpServer().session, projectId, clusterName, "IDLE"); + await waitCluster(integration.mcpServer().session, projectId, clusterName, (cluster) => { + return ( + cluster.stateName === "IDLE" && + (cluster.connectionStrings?.standardSrv || cluster.connectionStrings?.standard) !== undefined + ); + }); await integration.mcpServer().session.apiClient.createProjectIpAccessList({ params: { path: {