From 938342d211b26ebb321e1e525bde26c8780a5d2a Mon Sep 17 00:00:00 2001 From: Bianca Lisle Date: Tue, 29 Apr 2025 11:01:55 +0100 Subject: [PATCH 01/18] chore: minor improvements --- src/session.ts | 1 + src/telemetry/telemetry.ts | 53 +++++++++++++++++++------------------- src/tools/tool.ts | 5 +++- 3 files changed, 32 insertions(+), 27 deletions(-) diff --git a/src/session.ts b/src/session.ts index 57053688..431ce196 100644 --- a/src/session.ts +++ b/src/session.ts @@ -26,6 +26,7 @@ export class Session extends EventEmitter<{ username: string; projectId: string; clusterName: string; + organizationId?: string; expiryDate: Date; }; diff --git a/src/telemetry/telemetry.ts b/src/telemetry/telemetry.ts index 53431232..01c0c059 100644 --- a/src/telemetry/telemetry.ts +++ b/src/telemetry/telemetry.ts @@ -11,41 +11,41 @@ type EventResult = { error?: Error; }; -export class Telemetry { - private readonly commonProperties: CommonProperties; - - constructor( - private readonly session: Session, - private readonly eventCache: EventCache = EventCache.getInstance() - ) { - this.commonProperties = { - ...MACHINE_METADATA, - }; - } - - /** +/** * Checks if telemetry is currently enabled * This is a method rather than a constant to capture runtime config changes * * Follows the Console Do Not Track standard (https://consoledonottrack.com/) * by respecting the DO_NOT_TRACK environment variable */ - private static isTelemetryEnabled(): boolean { - // Check if telemetry is explicitly disabled in config - if (config.telemetry === "disabled") { +export function isTelemetryEnabled(): boolean { + // Check if telemetry is explicitly disabled in config + if (config.telemetry === "disabled") { + return false; + } + + const doNotTrack = process.env.DO_NOT_TRACK; + if (doNotTrack) { + const value = doNotTrack.toLowerCase(); + // Telemetry should be disabled if DO_NOT_TRACK is "1", "true", or "yes" + if (value === "1" || value === "true" || value === "yes") { return false; } + } - const doNotTrack = process.env.DO_NOT_TRACK; - if (doNotTrack) { - const value = doNotTrack.toLowerCase(); - // Telemetry should be disabled if DO_NOT_TRACK is "1", "true", or "yes" - if (value === "1" || value === "true" || value === "yes") { - return false; - } - } + return true; +} + +export class Telemetry { + private readonly commonProperties: CommonProperties; - return true; + constructor( + private readonly session: Session, + private readonly eventCache: EventCache = EventCache.getInstance() + ) { + this.commonProperties = { + ...MACHINE_METADATA, + }; } /** @@ -54,7 +54,8 @@ export class Telemetry { */ public async emitEvents(events: BaseEvent[]): Promise { try { - if (!Telemetry.isTelemetryEnabled()) { + if (!isTelemetryEnabled()) { + logger.info(LogId.telemetryEmitFailure, "telemetry", `Telemetry is disabled.`); return; } diff --git a/src/tools/tool.ts b/src/tools/tool.ts index d7ea909e..a8e9380b 100644 --- a/src/tools/tool.ts +++ b/src/tools/tool.ts @@ -3,7 +3,7 @@ import type { McpServer, RegisteredTool, ToolCallback } from "@modelcontextproto import type { CallToolResult } from "@modelcontextprotocol/sdk/types.js"; import { Session } from "../session.js"; import logger, { LogId } from "../logger.js"; -import { Telemetry } from "../telemetry/telemetry.js"; +import { Telemetry, isTelemetryEnabled } from "../telemetry/telemetry.js"; import { type ToolEvent } from "../telemetry/types.js"; import { UserConfig } from "../config.js"; @@ -38,6 +38,9 @@ export abstract class ToolBase { * @param error - Optional error if the command failed */ private async emitToolEvent(startTime: number, result: CallToolResult): Promise { + if (!isTelemetryEnabled()) { + return; + } const duration = Date.now() - startTime; const event: ToolEvent = { timestamp: new Date().toISOString(), From ab608c85adade81252ac2cb86241c8d6ff2d234e Mon Sep 17 00:00:00 2001 From: Bianca Lisle Date: Tue, 29 Apr 2025 12:44:06 +0100 Subject: [PATCH 02/18] wip --- src/logger.ts | 1 + src/telemetry/telemetry.ts | 6 +- src/tools/mongodb/mongodbTool.ts | 16 ++++- src/tools/tool.ts | 103 ++++++++++++++++++++++--------- 4 files changed, 95 insertions(+), 31 deletions(-) diff --git a/src/logger.ts b/src/logger.ts index 534bfb80..8746090f 100644 --- a/src/logger.ts +++ b/src/logger.ts @@ -17,6 +17,7 @@ export const LogId = { telemetryEmitFailure: mongoLogId(1_002_002), telemetryEmitStart: mongoLogId(1_002_003), telemetryEmitSuccess: mongoLogId(1_002_004), + telmetryMetadataError: mongoLogId(1_002_005), toolExecute: mongoLogId(1_003_001), toolExecuteFailure: mongoLogId(1_003_002), diff --git a/src/telemetry/telemetry.ts b/src/telemetry/telemetry.ts index 01c0c059..857c677a 100644 --- a/src/telemetry/telemetry.ts +++ b/src/telemetry/telemetry.ts @@ -97,7 +97,11 @@ export class Telemetry { const result = await this.sendEvents(this.session.apiClient, allEvents); if (result.success) { this.eventCache.clearEvents(); - logger.debug(LogId.telemetryEmitSuccess, "telemetry", `Sent ${allEvents.length} events successfully`); + logger.debug( + LogId.telemetryEmitSuccess, + "telemetry", + `Sent ${allEvents.length} events successfully: ${JSON.stringify(allEvents, null, 2)}` + ); return; } diff --git a/src/tools/mongodb/mongodbTool.ts b/src/tools/mongodb/mongodbTool.ts index d0e59b8b..63d337fa 100644 --- a/src/tools/mongodb/mongodbTool.ts +++ b/src/tools/mongodb/mongodbTool.ts @@ -1,9 +1,10 @@ import { z } from "zod"; -import { ToolArgs, ToolBase, ToolCategory } from "../tool.js"; +import { ToolArgs, ToolBase, ToolCategory, ToolMetadata } from "../tool.js"; import { NodeDriverServiceProvider } from "@mongosh/service-provider-node-driver"; -import { CallToolResult } from "@modelcontextprotocol/sdk/types.js"; +import { CallToolResult, ServerNotification, ServerRequest } from "@modelcontextprotocol/sdk/types.js"; import { ErrorCodes, MongoDBError } from "../../errors.js"; import logger, { LogId } from "../../logger.js"; +import { RequestHandlerExtra } from "@modelcontextprotocol/sdk/shared/protocol.js"; export const DbOperationArgs = { database: z.string().describe("Database name"), @@ -73,4 +74,15 @@ export abstract class MongoDBToolBase extends ToolBase { protected connectToMongoDB(connectionString: string): Promise { return this.session.connectToMongoDB(connectionString, this.config.connectOptions); } + + protected resolveToolMetadata(args: { [x: string]: any; }, extra: RequestHandlerExtra): ToolMetadata { + const metadata = super.resolveToolMetadata(args, extra); + + // Add projectId to the metadata if running a MongoDB operation to an Atlas cluster + if (this.session.connectedAtlasCluster?.projectId) { + metadata.projectId = this.session.connectedAtlasCluster.projectId; + } + + return metadata; + } } diff --git a/src/tools/tool.ts b/src/tools/tool.ts index a8e9380b..807245be 100644 --- a/src/tools/tool.ts +++ b/src/tools/tool.ts @@ -4,13 +4,17 @@ import type { CallToolResult } from "@modelcontextprotocol/sdk/types.js"; import { Session } from "../session.js"; import logger, { LogId } from "../logger.js"; import { Telemetry, isTelemetryEnabled } from "../telemetry/telemetry.js"; -import { type ToolEvent } from "../telemetry/types.js"; +import { type ToolEvent} from "../telemetry/types.js"; import { UserConfig } from "../config.js"; export type ToolArgs = z.objectOutputType; export type OperationType = "metadata" | "read" | "create" | "delete" | "update"; export type ToolCategory = "mongodb" | "atlas"; +export type ToolMetadata = { + projectId?: string; + orgId?: string; +} export abstract class ToolBase { protected abstract name: string; @@ -23,6 +27,7 @@ export abstract class ToolBase { protected abstract argsShape: ZodRawShape; + protected abstract execute(...args: Parameters>): Promise; constructor( @@ -31,31 +36,6 @@ export abstract class ToolBase { protected readonly telemetry: Telemetry ) {} - /** - * Creates and emits a tool telemetry event - * @param startTime - Start time in milliseconds - * @param result - Whether the command succeeded or failed - * @param error - Optional error if the command failed - */ - private async emitToolEvent(startTime: number, result: CallToolResult): Promise { - if (!isTelemetryEnabled()) { - return; - } - const duration = Date.now() - startTime; - const event: ToolEvent = { - timestamp: new Date().toISOString(), - source: "mdbmcp", - properties: { - command: this.name, - category: this.category, - component: "tool", - duration_ms: duration, - result: result.isError ? "failure" : "success", - }, - }; - await this.telemetry.emitEvents([event]); - } - public register(server: McpServer): void { if (!this.verifyAllowed()) { return; @@ -67,12 +47,12 @@ export abstract class ToolBase { logger.debug(LogId.toolExecute, "tool", `Executing ${this.name} with args: ${JSON.stringify(args)}`); const result = await this.execute(...args); - await this.emitToolEvent(startTime, result); + await this.emitToolEvent(startTime, result, ...args); return result; } catch (error: unknown) { logger.error(LogId.toolExecuteFailure, "tool", `Error executing ${this.name}: ${error as string}`); const toolResult = await this.handleError(error, args[0] as ToolArgs); - await this.emitToolEvent(startTime, toolResult).catch(() => {}); + await this.emitToolEvent(startTime, toolResult, ...args).catch(() => {}); return toolResult; } }; @@ -152,4 +132,71 @@ export abstract class ToolBase { ], }; } + + + /** + * + * Resolves the tool metadata from the arguments passed to the tool + * + * @param args - The arguments passed to the tool + * @returns The tool metadata + */ + protected resolveToolMetadata( + ...args: Parameters> + ): ToolMetadata { + const toolMetadata: ToolMetadata = {}; + try { + // Parse the arguments to extract project_id and org_id + const argsShape = z.object(this.argsShape); + const parsedArgs = argsShape.safeParse(args[0]); + if (parsedArgs.success && parsedArgs.data?.projectId) { + toolMetadata.projectId = parsedArgs.data?.projectId; + } + + if (parsedArgs.success && parsedArgs.data?.orgId) { + toolMetadata.orgId = parsedArgs.data?.orgId; + } + } + catch (error) { + logger.info(LogId.telmetryMetadataError, "tool", `Error resolving tool metadata: ${error as string}`); + } + return toolMetadata; + } + + + /** + * Creates and emits a tool telemetry event + * @param startTime - Start time in milliseconds + * @param result - Whether the command succeeded or failed + * @param args - The arguments passed to the tool + */ + private async emitToolEvent(startTime: number, result: CallToolResult, ...args: Parameters>): Promise { + if (!isTelemetryEnabled()) { + return; + } + const duration = Date.now() - startTime; + const metadata = this.resolveToolMetadata(...args); + const event: ToolEvent = { + timestamp: new Date().toISOString(), + source: "mdbmcp", + properties: { + ...this.telemetry.getCommonProperties(), + command: this.name, + category: this.category, + component: "tool", + duration_ms: duration, + result: result.isError ? "failure" : "success", + }, + }; + + if (metadata?.orgId) { + event.properties.org_id = metadata.orgId; + } + + if (metadata?.projectId) { + event.properties.project_id = metadata.projectId; + } + + await this.telemetry.emitEvents([event]); + } } From 39a7def6da4f8f17af1e39ef700712247461fa50 Mon Sep 17 00:00:00 2001 From: Bianca Lisle Date: Tue, 29 Apr 2025 13:12:38 +0100 Subject: [PATCH 03/18] reformat --- src/telemetry/telemetry.ts | 12 ++++++------ src/tools/mongodb/mongodbTool.ts | 5 ++++- src/tools/tool.ts | 26 ++++++++++++-------------- 3 files changed, 22 insertions(+), 21 deletions(-) diff --git a/src/telemetry/telemetry.ts b/src/telemetry/telemetry.ts index 857c677a..1dd54ae1 100644 --- a/src/telemetry/telemetry.ts +++ b/src/telemetry/telemetry.ts @@ -12,12 +12,12 @@ type EventResult = { }; /** - * Checks if telemetry is currently enabled - * This is a method rather than a constant to capture runtime config changes - * - * Follows the Console Do Not Track standard (https://consoledonottrack.com/) - * by respecting the DO_NOT_TRACK environment variable - */ + * Checks if telemetry is currently enabled + * This is a method rather than a constant to capture runtime config changes + * + * Follows the Console Do Not Track standard (https://consoledonottrack.com/) + * by respecting the DO_NOT_TRACK environment variable + */ export function isTelemetryEnabled(): boolean { // Check if telemetry is explicitly disabled in config if (config.telemetry === "disabled") { diff --git a/src/tools/mongodb/mongodbTool.ts b/src/tools/mongodb/mongodbTool.ts index 63d337fa..e82eaa0a 100644 --- a/src/tools/mongodb/mongodbTool.ts +++ b/src/tools/mongodb/mongodbTool.ts @@ -75,7 +75,10 @@ export abstract class MongoDBToolBase extends ToolBase { return this.session.connectToMongoDB(connectionString, this.config.connectOptions); } - protected resolveToolMetadata(args: { [x: string]: any; }, extra: RequestHandlerExtra): ToolMetadata { + protected resolveToolMetadata( + args: { [x: string]: any }, + extra: RequestHandlerExtra + ): ToolMetadata { const metadata = super.resolveToolMetadata(args, extra); // Add projectId to the metadata if running a MongoDB operation to an Atlas cluster diff --git a/src/tools/tool.ts b/src/tools/tool.ts index 807245be..8d0390db 100644 --- a/src/tools/tool.ts +++ b/src/tools/tool.ts @@ -3,8 +3,8 @@ import type { McpServer, RegisteredTool, ToolCallback } from "@modelcontextproto import type { CallToolResult } from "@modelcontextprotocol/sdk/types.js"; import { Session } from "../session.js"; import logger, { LogId } from "../logger.js"; -import { Telemetry, isTelemetryEnabled } from "../telemetry/telemetry.js"; -import { type ToolEvent} from "../telemetry/types.js"; +import { Telemetry, isTelemetryEnabled } from "../telemetry/telemetry.js"; +import { type ToolEvent } from "../telemetry/types.js"; import { UserConfig } from "../config.js"; export type ToolArgs = z.objectOutputType; @@ -14,7 +14,7 @@ export type ToolCategory = "mongodb" | "atlas"; export type ToolMetadata = { projectId?: string; orgId?: string; -} +}; export abstract class ToolBase { protected abstract name: string; @@ -27,7 +27,6 @@ export abstract class ToolBase { protected abstract argsShape: ZodRawShape; - protected abstract execute(...args: Parameters>): Promise; constructor( @@ -133,17 +132,14 @@ export abstract class ToolBase { }; } - /** - * + * * Resolves the tool metadata from the arguments passed to the tool - * + * * @param args - The arguments passed to the tool * @returns The tool metadata */ - protected resolveToolMetadata( - ...args: Parameters> - ): ToolMetadata { + protected resolveToolMetadata(...args: Parameters>): ToolMetadata { const toolMetadata: ToolMetadata = {}; try { // Parse the arguments to extract project_id and org_id @@ -156,21 +152,23 @@ export abstract class ToolBase { if (parsedArgs.success && parsedArgs.data?.orgId) { toolMetadata.orgId = parsedArgs.data?.orgId; } - } - catch (error) { + } catch (error) { logger.info(LogId.telmetryMetadataError, "tool", `Error resolving tool metadata: ${error as string}`); } return toolMetadata; } - /** * Creates and emits a tool telemetry event * @param startTime - Start time in milliseconds * @param result - Whether the command succeeded or failed * @param args - The arguments passed to the tool */ - private async emitToolEvent(startTime: number, result: CallToolResult, ...args: Parameters>): Promise { + private async emitToolEvent( + startTime: number, + result: CallToolResult, + ...args: Parameters> + ): Promise { if (!isTelemetryEnabled()) { return; } From ee23bed41b5dc92849c855172c045d79ab0602ba Mon Sep 17 00:00:00 2001 From: Bianca Lisle Date: Tue, 29 Apr 2025 13:32:44 +0100 Subject: [PATCH 04/18] update --- src/tools/mongodb/mongodbTool.ts | 2 +- src/tools/tool.ts | 33 +++++++++++++++++++++++++------- 2 files changed, 27 insertions(+), 8 deletions(-) diff --git a/src/tools/mongodb/mongodbTool.ts b/src/tools/mongodb/mongodbTool.ts index e82eaa0a..70f05d15 100644 --- a/src/tools/mongodb/mongodbTool.ts +++ b/src/tools/mongodb/mongodbTool.ts @@ -76,7 +76,7 @@ export abstract class MongoDBToolBase extends ToolBase { } protected resolveToolMetadata( - args: { [x: string]: any }, + args: ToolArgs, extra: RequestHandlerExtra ): ToolMetadata { const metadata = super.resolveToolMetadata(args, extra); diff --git a/src/tools/tool.ts b/src/tools/tool.ts index 8d0390db..a4efb1c1 100644 --- a/src/tools/tool.ts +++ b/src/tools/tool.ts @@ -142,18 +142,37 @@ export abstract class ToolBase { protected resolveToolMetadata(...args: Parameters>): ToolMetadata { const toolMetadata: ToolMetadata = {}; try { - // Parse the arguments to extract project_id and org_id + if (!args[0] || typeof args[0] !== "object") { + return toolMetadata; + } + + // Create a typed parser for the exact shape we expect const argsShape = z.object(this.argsShape); - const parsedArgs = argsShape.safeParse(args[0]); - if (parsedArgs.success && parsedArgs.data?.projectId) { - toolMetadata.projectId = parsedArgs.data?.projectId; + const parsedResult = argsShape.safeParse(args[0]); + + if (!parsedResult.success) { + logger.debug( + LogId.telmetryMetadataError, + "tool", + `Error parsing tool arguments: ${parsedResult.error.message}` + ); + return toolMetadata; + } + + const data = parsedResult.data; + + // Extract projectId using type guard + if ("projectId" in data && typeof data.projectId === "string" && data.projectId.trim() !== "") { + toolMetadata.projectId = data.projectId; } - if (parsedArgs.success && parsedArgs.data?.orgId) { - toolMetadata.orgId = parsedArgs.data?.orgId; + // Extract orgId using type guard + if ("orgId" in data && typeof data.orgId === "string" && data.orgId.trim() !== "") { + toolMetadata.orgId = data.orgId; } } catch (error) { - logger.info(LogId.telmetryMetadataError, "tool", `Error resolving tool metadata: ${error as string}`); + const errorMessage = error instanceof Error ? error.message : String(error); + logger.debug(LogId.telmetryMetadataError, "tool", `Error resolving tool metadata: ${errorMessage}`); } return toolMetadata; } From 9b675d4bb67ce9b9c7a5686da8943ef11d896be4 Mon Sep 17 00:00:00 2001 From: Bianca Lisle Date: Tue, 29 Apr 2025 13:42:13 +0100 Subject: [PATCH 05/18] address comments --- src/tools/mongodb/mongodbTool.ts | 4 ++-- src/tools/tool.ts | 6 +++--- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/src/tools/mongodb/mongodbTool.ts b/src/tools/mongodb/mongodbTool.ts index 70f05d15..f8c76d83 100644 --- a/src/tools/mongodb/mongodbTool.ts +++ b/src/tools/mongodb/mongodbTool.ts @@ -75,11 +75,11 @@ export abstract class MongoDBToolBase extends ToolBase { return this.session.connectToMongoDB(connectionString, this.config.connectOptions); } - protected resolveToolMetadata( + protected resolveTelemetryMetadata( args: ToolArgs, extra: RequestHandlerExtra ): ToolMetadata { - const metadata = super.resolveToolMetadata(args, extra); + const metadata = super.resolveTelemetryMetadata(args, extra); // Add projectId to the metadata if running a MongoDB operation to an Atlas cluster if (this.session.connectedAtlasCluster?.projectId) { diff --git a/src/tools/tool.ts b/src/tools/tool.ts index a4efb1c1..b3da7a1e 100644 --- a/src/tools/tool.ts +++ b/src/tools/tool.ts @@ -139,10 +139,10 @@ export abstract class ToolBase { * @param args - The arguments passed to the tool * @returns The tool metadata */ - protected resolveToolMetadata(...args: Parameters>): ToolMetadata { + protected resolveTelemetryMetadata(...args: Parameters>): ToolMetadata { const toolMetadata: ToolMetadata = {}; try { - if (!args[0] || typeof args[0] !== "object") { + if (!args.length) { return toolMetadata; } @@ -192,7 +192,7 @@ export abstract class ToolBase { return; } const duration = Date.now() - startTime; - const metadata = this.resolveToolMetadata(...args); + const metadata = this.resolveTelemetryMetadata(...args); const event: ToolEvent = { timestamp: new Date().toISOString(), source: "mdbmcp", From 4a764febda1aec87238960576ec0b46cc146938b Mon Sep 17 00:00:00 2001 From: Bianca Lisle Date: Tue, 29 Apr 2025 14:12:55 +0100 Subject: [PATCH 06/18] address comment: remove unused value --- src/session.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/src/session.ts b/src/session.ts index 431ce196..57053688 100644 --- a/src/session.ts +++ b/src/session.ts @@ -26,7 +26,6 @@ export class Session extends EventEmitter<{ username: string; projectId: string; clusterName: string; - organizationId?: string; expiryDate: Date; }; From bfd6d3b547272df2f7c883858fa3a55378de7b9e Mon Sep 17 00:00:00 2001 From: Bianca Lisle Date: Tue, 29 Apr 2025 14:15:28 +0100 Subject: [PATCH 07/18] address comment: simplify do not track --- src/telemetry/telemetry.ts | 12 ++---------- 1 file changed, 2 insertions(+), 10 deletions(-) diff --git a/src/telemetry/telemetry.ts b/src/telemetry/telemetry.ts index 1dd54ae1..46019c42 100644 --- a/src/telemetry/telemetry.ts +++ b/src/telemetry/telemetry.ts @@ -24,16 +24,8 @@ export function isTelemetryEnabled(): boolean { return false; } - const doNotTrack = process.env.DO_NOT_TRACK; - if (doNotTrack) { - const value = doNotTrack.toLowerCase(); - // Telemetry should be disabled if DO_NOT_TRACK is "1", "true", or "yes" - if (value === "1" || value === "true" || value === "yes") { - return false; - } - } - - return true; + const doNotTrack = "DO_NOT_TRACK" in process.env; + return !doNotTrack; } export class Telemetry { From dc0e4d39c7d759d56ae7c545e652d0e5a9e4cdc7 Mon Sep 17 00:00:00 2001 From: Bianca Lisle Date: Tue, 29 Apr 2025 14:16:42 +0100 Subject: [PATCH 08/18] address comment: rename metadata --- src/tools/mongodb/mongodbTool.ts | 4 ++-- src/tools/tool.ts | 8 ++++---- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/src/tools/mongodb/mongodbTool.ts b/src/tools/mongodb/mongodbTool.ts index f8c76d83..33308845 100644 --- a/src/tools/mongodb/mongodbTool.ts +++ b/src/tools/mongodb/mongodbTool.ts @@ -1,5 +1,5 @@ import { z } from "zod"; -import { ToolArgs, ToolBase, ToolCategory, ToolMetadata } from "../tool.js"; +import { ToolArgs, ToolBase, ToolCategory, TelemetryToolMetadata } from "../tool.js"; import { NodeDriverServiceProvider } from "@mongosh/service-provider-node-driver"; import { CallToolResult, ServerNotification, ServerRequest } from "@modelcontextprotocol/sdk/types.js"; import { ErrorCodes, MongoDBError } from "../../errors.js"; @@ -78,7 +78,7 @@ export abstract class MongoDBToolBase extends ToolBase { protected resolveTelemetryMetadata( args: ToolArgs, extra: RequestHandlerExtra - ): ToolMetadata { + ): TelemetryToolMetadata { const metadata = super.resolveTelemetryMetadata(args, extra); // Add projectId to the metadata if running a MongoDB operation to an Atlas cluster diff --git a/src/tools/tool.ts b/src/tools/tool.ts index b3da7a1e..e688f7ca 100644 --- a/src/tools/tool.ts +++ b/src/tools/tool.ts @@ -11,7 +11,7 @@ export type ToolArgs = z.objectOutputType>): ToolMetadata { - const toolMetadata: ToolMetadata = {}; + protected resolveTelemetryMetadata(...args: Parameters>): TelemetryToolMetadata { + const toolMetadata: TelemetryToolMetadata = {}; try { if (!args.length) { return toolMetadata; From 596c2330a0903ee2b76c184c36093218a480f689 Mon Sep 17 00:00:00 2001 From: Bianca Lisle Date: Tue, 29 Apr 2025 14:20:50 +0100 Subject: [PATCH 09/18] reformat --- src/tools/tool.ts | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/tools/tool.ts b/src/tools/tool.ts index e688f7ca..b3837872 100644 --- a/src/tools/tool.ts +++ b/src/tools/tool.ts @@ -105,7 +105,7 @@ export abstract class ToolBase { if (errorClarification) { logger.debug( LogId.toolDisabled, - "tool", + "tool", `Prevented registration of ${this.name} because ${errorClarification} is disabled in the config` ); @@ -139,7 +139,9 @@ export abstract class ToolBase { * @param args - The arguments passed to the tool * @returns The tool metadata */ - protected resolveTelemetryMetadata(...args: Parameters>): TelemetryToolMetadata { + protected resolveTelemetryMetadata( + ...args: Parameters> + ): TelemetryToolMetadata { const toolMetadata: TelemetryToolMetadata = {}; try { if (!args.length) { From 72539ab25c437c09dccc12504c11a8875cded049 Mon Sep 17 00:00:00 2001 From: Bianca Lisle Date: Tue, 29 Apr 2025 14:55:03 +0100 Subject: [PATCH 10/18] address comment: remove call --- src/tools/tool.ts | 54 +++++++++++++++++++++-------------------------- 1 file changed, 24 insertions(+), 30 deletions(-) diff --git a/src/tools/tool.ts b/src/tools/tool.ts index b3837872..f6a37de1 100644 --- a/src/tools/tool.ts +++ b/src/tools/tool.ts @@ -46,7 +46,7 @@ export abstract class ToolBase { logger.debug(LogId.toolExecute, "tool", `Executing ${this.name} with args: ${JSON.stringify(args)}`); const result = await this.execute(...args); - await this.emitToolEvent(startTime, result, ...args); + await this.emitToolEvent(startTime, result, ...args).catch(() => {}); return result; } catch (error: unknown) { logger.error(LogId.toolExecuteFailure, "tool", `Error executing ${this.name}: ${error as string}`); @@ -143,38 +143,33 @@ export abstract class ToolBase { ...args: Parameters> ): TelemetryToolMetadata { const toolMetadata: TelemetryToolMetadata = {}; - try { - if (!args.length) { - return toolMetadata; - } + if (!args.length) { + return toolMetadata; + } - // Create a typed parser for the exact shape we expect - const argsShape = z.object(this.argsShape); - const parsedResult = argsShape.safeParse(args[0]); - - if (!parsedResult.success) { - logger.debug( - LogId.telmetryMetadataError, - "tool", - `Error parsing tool arguments: ${parsedResult.error.message}` - ); - return toolMetadata; - } + // Create a typed parser for the exact shape we expect + const argsShape = z.object(this.argsShape); + const parsedResult = argsShape.safeParse(args[0]); - const data = parsedResult.data; + if (!parsedResult.success) { + logger.debug( + LogId.telmetryMetadataError, + "tool", + `Error parsing tool arguments: ${parsedResult.error.message}` + ); + return toolMetadata; + } - // Extract projectId using type guard - if ("projectId" in data && typeof data.projectId === "string" && data.projectId.trim() !== "") { - toolMetadata.projectId = data.projectId; - } + const data = parsedResult.data; - // Extract orgId using type guard - if ("orgId" in data && typeof data.orgId === "string" && data.orgId.trim() !== "") { - toolMetadata.orgId = data.orgId; - } - } catch (error) { - const errorMessage = error instanceof Error ? error.message : String(error); - logger.debug(LogId.telmetryMetadataError, "tool", `Error resolving tool metadata: ${errorMessage}`); + // Extract projectId using type guard + if ("projectId" in data && typeof data.projectId === "string" && data.projectId.trim() !== "") { + toolMetadata.projectId = data.projectId; + } + + // Extract orgId using type guard + if ("orgId" in data && typeof data.orgId === "string" && data.orgId.trim() !== "") { + toolMetadata.orgId = data.orgId; } return toolMetadata; } @@ -199,7 +194,6 @@ export abstract class ToolBase { timestamp: new Date().toISOString(), source: "mdbmcp", properties: { - ...this.telemetry.getCommonProperties(), command: this.name, category: this.category, component: "tool", From 43e5c75c507cf9031ffca238ab8563b89f80ed41 Mon Sep 17 00:00:00 2001 From: Bianca Lisle Date: Tue, 29 Apr 2025 15:13:25 +0100 Subject: [PATCH 11/18] address comment: telemetry enabled check --- src/telemetry/telemetry.ts | 36 ++++++++++++++++++------------------ src/tools/tool.ts | 4 ++-- 2 files changed, 20 insertions(+), 20 deletions(-) diff --git a/src/telemetry/telemetry.ts b/src/telemetry/telemetry.ts index 46019c42..51eeb2aa 100644 --- a/src/telemetry/telemetry.ts +++ b/src/telemetry/telemetry.ts @@ -11,23 +11,6 @@ type EventResult = { error?: Error; }; -/** - * Checks if telemetry is currently enabled - * This is a method rather than a constant to capture runtime config changes - * - * Follows the Console Do Not Track standard (https://consoledonottrack.com/) - * by respecting the DO_NOT_TRACK environment variable - */ -export function isTelemetryEnabled(): boolean { - // Check if telemetry is explicitly disabled in config - if (config.telemetry === "disabled") { - return false; - } - - const doNotTrack = "DO_NOT_TRACK" in process.env; - return !doNotTrack; -} - export class Telemetry { private readonly commonProperties: CommonProperties; @@ -46,7 +29,7 @@ export class Telemetry { */ public async emitEvents(events: BaseEvent[]): Promise { try { - if (!isTelemetryEnabled()) { + if (!this.isTelemetryEnabled()) { logger.info(LogId.telemetryEmitFailure, "telemetry", `Telemetry is disabled.`); return; } @@ -72,6 +55,23 @@ export class Telemetry { }; } + /** + * Checks if telemetry is currently enabled + * This is a method rather than a constant to capture runtime config changes + * + * Follows the Console Do Not Track standard (https://consoledonottrack.com/) + * by respecting the DO_NOT_TRACK environment variable + */ + public isTelemetryEnabled(): boolean { + // Check if telemetry is explicitly disabled in config + if (config.telemetry === "disabled") { + return false; + } + + const doNotTrack = "DO_NOT_TRACK" in process.env; + return !doNotTrack; + } + /** * Attempts to emit events through authenticated and unauthenticated clients * Falls back to caching if both attempts fail diff --git a/src/tools/tool.ts b/src/tools/tool.ts index f6a37de1..c605a82c 100644 --- a/src/tools/tool.ts +++ b/src/tools/tool.ts @@ -3,7 +3,7 @@ import type { McpServer, RegisteredTool, ToolCallback } from "@modelcontextproto import type { CallToolResult } from "@modelcontextprotocol/sdk/types.js"; import { Session } from "../session.js"; import logger, { LogId } from "../logger.js"; -import { Telemetry, isTelemetryEnabled } from "../telemetry/telemetry.js"; +import { Telemetry } from "../telemetry/telemetry.js"; import { type ToolEvent } from "../telemetry/types.js"; import { UserConfig } from "../config.js"; @@ -185,7 +185,7 @@ export abstract class ToolBase { result: CallToolResult, ...args: Parameters> ): Promise { - if (!isTelemetryEnabled()) { + if (!this.telemetry.isTelemetryEnabled()) { return; } const duration = Date.now() - startTime; From 8721c390600e9bd600be15833e8fec9be3745936 Mon Sep 17 00:00:00 2001 From: Bianca Lisle Date: Tue, 29 Apr 2025 15:21:06 +0100 Subject: [PATCH 12/18] update test telemetry --- tests/integration/tools/mongodb/mongodbHelpers.ts | 2 ++ 1 file changed, 2 insertions(+) diff --git a/tests/integration/tools/mongodb/mongodbHelpers.ts b/tests/integration/tools/mongodb/mongodbHelpers.ts index 2b4ea6a0..41b8f8d0 100644 --- a/tests/integration/tools/mongodb/mongodbHelpers.ts +++ b/tests/integration/tools/mongodb/mongodbHelpers.ts @@ -22,10 +22,12 @@ export function describeWithMongoDB( const integration = setupIntegrationTest(() => ({ ...getUserConfig(mdbIntegration), connectionString: mdbIntegration.connectionString(), + telemetry: "disabled", // Explicitly disable telemetry })); beforeEach(() => { integration.mcpServer().userConfig.connectionString = mdbIntegration.connectionString(); + integration.mcpServer().userConfig.telemetry = "disabled"; // Ensure telemetry stays disabled }); fn({ From b4ac4db46584f9e748ad6f9403d3d650e32c715b Mon Sep 17 00:00:00 2001 From: Bianca Lisle <40155621+blva@users.noreply.github.com> Date: Tue, 29 Apr 2025 15:22:09 +0100 Subject: [PATCH 13/18] Update src/logger.ts Co-authored-by: Gagik Amaryan --- src/logger.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/logger.ts b/src/logger.ts index 8746090f..19a311c2 100644 --- a/src/logger.ts +++ b/src/logger.ts @@ -17,7 +17,7 @@ export const LogId = { telemetryEmitFailure: mongoLogId(1_002_002), telemetryEmitStart: mongoLogId(1_002_003), telemetryEmitSuccess: mongoLogId(1_002_004), - telmetryMetadataError: mongoLogId(1_002_005), + telemetryMetadataError: mongoLogId(1_002_005), toolExecute: mongoLogId(1_003_001), toolExecuteFailure: mongoLogId(1_003_002), From bbc49be1973d9bd373ee407b9ba349a84b5caa98 Mon Sep 17 00:00:00 2001 From: Bianca Lisle Date: Tue, 29 Apr 2025 15:22:58 +0100 Subject: [PATCH 14/18] update --- src/tools/tool.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/tools/tool.ts b/src/tools/tool.ts index c605a82c..eed9a916 100644 --- a/src/tools/tool.ts +++ b/src/tools/tool.ts @@ -153,7 +153,7 @@ export abstract class ToolBase { if (!parsedResult.success) { logger.debug( - LogId.telmetryMetadataError, + LogId.telemetryMetadataError, "tool", `Error parsing tool arguments: ${parsedResult.error.message}` ); From a06c460b97220b62e9d3133679c2eb2f56cad2a9 Mon Sep 17 00:00:00 2001 From: Bianca Lisle Date: Tue, 29 Apr 2025 15:52:17 +0100 Subject: [PATCH 15/18] address comments: update tools --- src/tools/atlas/atlasTool.ts | 47 +++++++++++++++++++++++++++++++- src/tools/mongodb/mongodbTool.ts | 9 +++--- src/tools/tool.ts | 42 ++-------------------------- 3 files changed, 52 insertions(+), 46 deletions(-) diff --git a/src/tools/atlas/atlasTool.ts b/src/tools/atlas/atlasTool.ts index 6ca5282d..6c74bb88 100644 --- a/src/tools/atlas/atlasTool.ts +++ b/src/tools/atlas/atlasTool.ts @@ -1,4 +1,7 @@ -import { ToolBase, ToolCategory } from "../tool.js"; +import { ToolBase, ToolCategory, TelemetryToolMetadata } from "../tool.js"; +import { ToolCallback } from "@modelcontextprotocol/sdk/server/mcp.js"; +import logger, { LogId } from "../../logger.js"; +import { z } from "zod"; export abstract class AtlasToolBase extends ToolBase { protected category: ToolCategory = "atlas"; @@ -9,4 +12,46 @@ export abstract class AtlasToolBase extends ToolBase { } return super.verifyAllowed(); } + + /** + * + * Resolves the tool metadata from the arguments passed to the tool + * + * @param args - The arguments passed to the tool + * @returns The tool metadata + */ + protected resolveTelemetryMetadata( + ...args: Parameters> + ): TelemetryToolMetadata { + const toolMetadata: TelemetryToolMetadata = {}; + if (!args.length) { + return toolMetadata; + } + + // Create a typed parser for the exact shape we expect + const argsShape = z.object(this.argsShape); + const parsedResult = argsShape.safeParse(args[0]); + + if (!parsedResult.success) { + logger.debug( + LogId.telemetryMetadataError, + "tool", + `Error parsing tool arguments: ${parsedResult.error.message}` + ); + return toolMetadata; + } + + const data = parsedResult.data; + + // Extract projectId using type guard + if ("projectId" in data && typeof data.projectId === "string" && data.projectId.trim() !== "") { + toolMetadata.projectId = data.projectId; + } + + // Extract orgId using type guard + if ("orgId" in data && typeof data.orgId === "string" && data.orgId.trim() !== "") { + toolMetadata.orgId = data.orgId; + } + return toolMetadata; + } } diff --git a/src/tools/mongodb/mongodbTool.ts b/src/tools/mongodb/mongodbTool.ts index 33308845..2ef1aee0 100644 --- a/src/tools/mongodb/mongodbTool.ts +++ b/src/tools/mongodb/mongodbTool.ts @@ -1,10 +1,9 @@ import { z } from "zod"; import { ToolArgs, ToolBase, ToolCategory, TelemetryToolMetadata } from "../tool.js"; import { NodeDriverServiceProvider } from "@mongosh/service-provider-node-driver"; -import { CallToolResult, ServerNotification, ServerRequest } from "@modelcontextprotocol/sdk/types.js"; +import { CallToolResult } from "@modelcontextprotocol/sdk/types.js"; import { ErrorCodes, MongoDBError } from "../../errors.js"; import logger, { LogId } from "../../logger.js"; -import { RequestHandlerExtra } from "@modelcontextprotocol/sdk/shared/protocol.js"; export const DbOperationArgs = { database: z.string().describe("Database name"), @@ -76,10 +75,10 @@ export abstract class MongoDBToolBase extends ToolBase { } protected resolveTelemetryMetadata( - args: ToolArgs, - extra: RequestHandlerExtra + // eslint-disable-next-line @typescript-eslint/no-unused-vars + args: ToolArgs ): TelemetryToolMetadata { - const metadata = super.resolveTelemetryMetadata(args, extra); + const metadata: TelemetryToolMetadata = {}; // Add projectId to the metadata if running a MongoDB operation to an Atlas cluster if (this.session.connectedAtlasCluster?.projectId) { diff --git a/src/tools/tool.ts b/src/tools/tool.ts index eed9a916..b6b00eda 100644 --- a/src/tools/tool.ts +++ b/src/tools/tool.ts @@ -132,47 +132,9 @@ export abstract class ToolBase { }; } - /** - * - * Resolves the tool metadata from the arguments passed to the tool - * - * @param args - The arguments passed to the tool - * @returns The tool metadata - */ - protected resolveTelemetryMetadata( + protected abstract resolveTelemetryMetadata( ...args: Parameters> - ): TelemetryToolMetadata { - const toolMetadata: TelemetryToolMetadata = {}; - if (!args.length) { - return toolMetadata; - } - - // Create a typed parser for the exact shape we expect - const argsShape = z.object(this.argsShape); - const parsedResult = argsShape.safeParse(args[0]); - - if (!parsedResult.success) { - logger.debug( - LogId.telemetryMetadataError, - "tool", - `Error parsing tool arguments: ${parsedResult.error.message}` - ); - return toolMetadata; - } - - const data = parsedResult.data; - - // Extract projectId using type guard - if ("projectId" in data && typeof data.projectId === "string" && data.projectId.trim() !== "") { - toolMetadata.projectId = data.projectId; - } - - // Extract orgId using type guard - if ("orgId" in data && typeof data.orgId === "string" && data.orgId.trim() !== "") { - toolMetadata.orgId = data.orgId; - } - return toolMetadata; - } + ): TelemetryToolMetadata; /** * Creates and emits a tool telemetry event From a95af7b1960c6ff5b4d803348bfb7327a5cfeb7f Mon Sep 17 00:00:00 2001 From: Bianca Lisle Date: Tue, 29 Apr 2025 16:00:47 +0100 Subject: [PATCH 16/18] disable telemetry for all tests --- tests/integration/helpers.ts | 20 +++++++++++-------- .../tools/mongodb/mongodbHelpers.ts | 2 -- 2 files changed, 12 insertions(+), 10 deletions(-) diff --git a/tests/integration/helpers.ts b/tests/integration/helpers.ts index c57deda8..ead9be8d 100644 --- a/tests/integration/helpers.ts +++ b/tests/integration/helpers.ts @@ -23,8 +23,12 @@ export interface IntegrationTest { export function setupIntegrationTest(getUserConfig: () => UserConfig): IntegrationTest { let mcpClient: Client | undefined; let mcpServer: Server | undefined; + let oldDoNotTrackValue: string | undefined; beforeAll(async () => { + // GET DO_NOT_TRACK value + oldDoNotTrackValue = process.env.DO_NOT_TRACK; + process.env.DO_NOT_TRACK = "1"; const userConfig = getUserConfig(); const clientTransport = new InMemoryTransport(); const serverTransport = new InMemoryTransport(); @@ -51,25 +55,18 @@ export function setupIntegrationTest(getUserConfig: () => UserConfig): Integrati apiClientSecret: userConfig.apiClientSecret, }); - userConfig.telemetry = "disabled"; mcpServer = new Server({ session, userConfig, mcpServer: new McpServer({ name: "test-server", - version: "1.2.3", + version: "5.2.3", }), }); await mcpServer.connect(serverTransport); await mcpClient.connect(clientTransport); }); - beforeEach(() => { - if (mcpServer) { - mcpServer.userConfig.telemetry = "disabled"; - } - }); - afterEach(async () => { if (mcpServer) { await mcpServer.session.close(); @@ -82,6 +79,13 @@ export function setupIntegrationTest(getUserConfig: () => UserConfig): Integrati await mcpServer?.close(); mcpServer = undefined; + + // Reset DO_NOT_TRACK value + if (oldDoNotTrackValue !== undefined) { + process.env.DO_NOT_TRACK = oldDoNotTrackValue; + } else { + delete process.env.DO_NOT_TRACK; + } }); const getMcpClient = () => { diff --git a/tests/integration/tools/mongodb/mongodbHelpers.ts b/tests/integration/tools/mongodb/mongodbHelpers.ts index 41b8f8d0..2b4ea6a0 100644 --- a/tests/integration/tools/mongodb/mongodbHelpers.ts +++ b/tests/integration/tools/mongodb/mongodbHelpers.ts @@ -22,12 +22,10 @@ export function describeWithMongoDB( const integration = setupIntegrationTest(() => ({ ...getUserConfig(mdbIntegration), connectionString: mdbIntegration.connectionString(), - telemetry: "disabled", // Explicitly disable telemetry })); beforeEach(() => { integration.mcpServer().userConfig.connectionString = mdbIntegration.connectionString(); - integration.mcpServer().userConfig.telemetry = "disabled"; // Ensure telemetry stays disabled }); fn({ From 3eee38c5e4617feba2d05a70060fc77e51e6d743 Mon Sep 17 00:00:00 2001 From: Bianca Lisle Date: Tue, 29 Apr 2025 16:25:02 +0100 Subject: [PATCH 17/18] address comments: create default test config --- tests/integration/helpers.ts | 16 +++++----------- tests/integration/server.test.ts | 9 ++++----- tests/integration/tools/atlas/atlasHelpers.ts | 5 ++--- .../integration/tools/mongodb/mongodbHelpers.ts | 6 +++--- 4 files changed, 14 insertions(+), 22 deletions(-) diff --git a/tests/integration/helpers.ts b/tests/integration/helpers.ts index ead9be8d..bacc89b9 100644 --- a/tests/integration/helpers.ts +++ b/tests/integration/helpers.ts @@ -5,6 +5,7 @@ import { UserConfig } from "../../src/config.js"; import { McpError } from "@modelcontextprotocol/sdk/types.js"; import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js"; import { Session } from "../../src/session.js"; +import { config } from "../../src/config.js"; interface ParameterInfo { name: string; @@ -19,16 +20,16 @@ export interface IntegrationTest { mcpClient: () => Client; mcpServer: () => Server; } +export const defaultTestConfig: UserConfig = { + ...config, + telemetry: "disabled", +}; export function setupIntegrationTest(getUserConfig: () => UserConfig): IntegrationTest { let mcpClient: Client | undefined; let mcpServer: Server | undefined; - let oldDoNotTrackValue: string | undefined; beforeAll(async () => { - // GET DO_NOT_TRACK value - oldDoNotTrackValue = process.env.DO_NOT_TRACK; - process.env.DO_NOT_TRACK = "1"; const userConfig = getUserConfig(); const clientTransport = new InMemoryTransport(); const serverTransport = new InMemoryTransport(); @@ -79,13 +80,6 @@ export function setupIntegrationTest(getUserConfig: () => UserConfig): Integrati await mcpServer?.close(); mcpServer = undefined; - - // Reset DO_NOT_TRACK value - if (oldDoNotTrackValue !== undefined) { - process.env.DO_NOT_TRACK = oldDoNotTrackValue; - } else { - delete process.env.DO_NOT_TRACK; - } }); const getMcpClient = () => { diff --git a/tests/integration/server.test.ts b/tests/integration/server.test.ts index aec15add..3b4c1858 100644 --- a/tests/integration/server.test.ts +++ b/tests/integration/server.test.ts @@ -1,5 +1,4 @@ -import { expectDefined, setupIntegrationTest } from "./helpers.js"; -import { config } from "../../src/config.js"; +import { defaultTestConfig, expectDefined, setupIntegrationTest } from "./helpers.js"; import { describeWithMongoDB } from "./tools/mongodb/mongodbHelpers.js"; describe("Server integration test", () => { @@ -16,7 +15,7 @@ describe("Server integration test", () => { }); }, () => ({ - ...config, + ...defaultTestConfig, apiClientId: undefined, apiClientSecret: undefined, }) @@ -24,7 +23,7 @@ describe("Server integration test", () => { describe("with atlas", () => { const integration = setupIntegrationTest(() => ({ - ...config, + ...defaultTestConfig, apiClientId: "test", apiClientSecret: "test", })); @@ -59,7 +58,7 @@ describe("Server integration test", () => { describe("with read-only mode", () => { const integration = setupIntegrationTest(() => ({ - ...config, + ...defaultTestConfig, readOnly: true, apiClientId: "test", apiClientSecret: "test", diff --git a/tests/integration/tools/atlas/atlasHelpers.ts b/tests/integration/tools/atlas/atlasHelpers.ts index d66a4041..aecf0479 100644 --- a/tests/integration/tools/atlas/atlasHelpers.ts +++ b/tests/integration/tools/atlas/atlasHelpers.ts @@ -1,15 +1,14 @@ import { ObjectId } from "mongodb"; import { Group } from "../../../../src/common/atlas/openapi.js"; import { ApiClient } from "../../../../src/common/atlas/apiClient.js"; -import { setupIntegrationTest, IntegrationTest } from "../../helpers.js"; -import { config } from "../../../../src/config.js"; +import { setupIntegrationTest, IntegrationTest, defaultTestConfig } from "../../helpers.js"; export type IntegrationTestFunction = (integration: IntegrationTest) => void; export function describeWithAtlas(name: string, fn: IntegrationTestFunction) { const testDefinition = () => { const integration = setupIntegrationTest(() => ({ - ...config, + ...defaultTestConfig, apiClientId: process.env.MDB_MCP_API_CLIENT_ID, apiClientSecret: process.env.MDB_MCP_API_CLIENT_SECRET, })); diff --git a/tests/integration/tools/mongodb/mongodbHelpers.ts b/tests/integration/tools/mongodb/mongodbHelpers.ts index 2b4ea6a0..087d675c 100644 --- a/tests/integration/tools/mongodb/mongodbHelpers.ts +++ b/tests/integration/tools/mongodb/mongodbHelpers.ts @@ -2,8 +2,8 @@ import { MongoCluster } from "mongodb-runner"; import path from "path"; import fs from "fs/promises"; import { MongoClient, ObjectId } from "mongodb"; -import { getResponseContent, IntegrationTest, setupIntegrationTest } from "../../helpers.js"; -import { config, UserConfig } from "../../../../src/config.js"; +import { getResponseContent, IntegrationTest, setupIntegrationTest, defaultTestConfig } from "../../helpers.js"; +import { UserConfig } from "../../../../src/config.js"; interface MongoDBIntegrationTest { mongoClient: () => MongoClient; @@ -14,7 +14,7 @@ interface MongoDBIntegrationTest { export function describeWithMongoDB( name: string, fn: (integration: IntegrationTest & MongoDBIntegrationTest & { connectMcpClient: () => Promise }) => void, - getUserConfig: (mdbIntegration: MongoDBIntegrationTest) => UserConfig = () => config, + getUserConfig: (mdbIntegration: MongoDBIntegrationTest) => UserConfig = () => defaultTestConfig, describeFn = describe ) { describeFn(name, () => { From f0142ec5437bb6e9474e1e2e85c72735ab1e7166 Mon Sep 17 00:00:00 2001 From: Bianca Lisle Date: Tue, 29 Apr 2025 16:38:31 +0100 Subject: [PATCH 18/18] address comments: inject userconfig in telemetry --- src/server.ts | 2 +- src/telemetry/telemetry.ts | 7 ++++--- tests/unit/telemetry.test.ts | 3 +-- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/src/server.ts b/src/server.ts index b11ba31d..effdee63 100644 --- a/src/server.ts +++ b/src/server.ts @@ -28,7 +28,7 @@ export class Server { constructor({ session, mcpServer, userConfig }: ServerOptions) { this.startTime = Date.now(); this.session = session; - this.telemetry = new Telemetry(session); + this.telemetry = new Telemetry(session, userConfig); this.mcpServer = mcpServer; this.userConfig = userConfig; } diff --git a/src/telemetry/telemetry.ts b/src/telemetry/telemetry.ts index 51eeb2aa..31760ff4 100644 --- a/src/telemetry/telemetry.ts +++ b/src/telemetry/telemetry.ts @@ -1,6 +1,6 @@ import { Session } from "../session.js"; import { BaseEvent, CommonProperties } from "./types.js"; -import { config } from "../config.js"; +import { UserConfig } from "../config.js"; import logger, { LogId } from "../logger.js"; import { ApiClient } from "../common/atlas/apiClient.js"; import { MACHINE_METADATA } from "./constants.js"; @@ -16,6 +16,7 @@ export class Telemetry { constructor( private readonly session: Session, + private readonly userConfig: UserConfig, private readonly eventCache: EventCache = EventCache.getInstance() ) { this.commonProperties = { @@ -51,7 +52,7 @@ export class Telemetry { mcp_client_name: this.session.agentRunner?.name, session_id: this.session.sessionId, config_atlas_auth: this.session.apiClient.hasCredentials() ? "true" : "false", - config_connection_string: config.connectionString ? "true" : "false", + config_connection_string: this.userConfig.connectionString ? "true" : "false", }; } @@ -64,7 +65,7 @@ export class Telemetry { */ public isTelemetryEnabled(): boolean { // Check if telemetry is explicitly disabled in config - if (config.telemetry === "disabled") { + if (this.userConfig.telemetry === "disabled") { return false; } diff --git a/tests/unit/telemetry.test.ts b/tests/unit/telemetry.test.ts index 5b37da8e..4165b60c 100644 --- a/tests/unit/telemetry.test.ts +++ b/tests/unit/telemetry.test.ts @@ -113,8 +113,7 @@ describe("Telemetry", () => { } as unknown as Session; // Create the telemetry instance with mocked dependencies - telemetry = new Telemetry(session, mockEventCache); - + telemetry = new Telemetry(session, config, mockEventCache); config.telemetry = "enabled"; });