diff --git a/src/server.ts b/src/server.ts index 14bea760..1bec50b0 100644 --- a/src/server.ts +++ b/src/server.ts @@ -8,6 +8,8 @@ import { mongoLogId } from "mongodb-log-writer"; import { ObjectId } from "mongodb"; import { Telemetry } from "./telemetry/telemetry.js"; import { UserConfig } from "./config.js"; +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"; @@ -22,8 +24,10 @@ export class Server { private readonly mcpServer: McpServer; private readonly telemetry: Telemetry; private readonly userConfig: UserConfig; + private readonly startTime: number; constructor({ session, mcpServer, userConfig }: ServerOptions) { + this.startTime = Date.now(); this.session = session; this.telemetry = new Telemetry(session); this.mcpServer = mcpServer; @@ -71,6 +75,18 @@ export class Server { "server", `Server started with transport ${transport.constructor.name} and agent runner ${this.session.agentRunner?.name}` ); + + this.emitServerEvent("start", Date.now() - this.startTime); + }; + + this.mcpServer.server.onclose = () => { + const closeTime = Date.now(); + this.emitServerEvent("stop", Date.now() - closeTime); + }; + + this.mcpServer.server.onerror = (error: Error) => { + const closeTime = Date.now(); + this.emitServerEvent("stop", Date.now() - closeTime, error); }; } @@ -79,6 +95,39 @@ export class Server { await this.mcpServer.close(); } + /** + * Emits a server event + * @param command - The server command (e.g., "start", "stop", "register", "deregister") + * @param additionalProperties - Additional properties specific to the event + */ + emitServerEvent(command: ServerCommand, commandDuration: number, error?: Error) { + const event: ServerEvent = { + timestamp: new Date().toISOString(), + source: "mdbmcp", + properties: { + ...this.telemetry.getCommonProperties(), + result: "success", + duration_ms: commandDuration, + component: "server", + category: "other", + command: command, + }, + }; + + if (command === "start") { + event.properties.startup_time_ms = commandDuration; + } + if (command === "stop") { + event.properties.runtime_duration_ms = Date.now() - this.startTime; + if (error) { + event.properties.result = "failure"; + event.properties.reason = error.message; + } + } + + this.telemetry.emitEvents([event]).catch(() => {}); + } + private registerTools() { for (const tool of [...AtlasTools, ...MongoDbTools]) { new tool(this.session, this.userConfig, this.telemetry).register(this.mcpServer); diff --git a/src/telemetry/telemetry.ts b/src/telemetry/telemetry.ts index a43b11c9..2b5ed7df 100644 --- a/src/telemetry/telemetry.ts +++ b/src/telemetry/telemetry.ts @@ -69,7 +69,6 @@ export class Telemetry { public async emitEvents(events: BaseEvent[]): Promise { try { if (!Telemetry.isTelemetryEnabled()) { - logger.debug(mongoLogId(1_000_000), "telemetry", "Telemetry is disabled, skipping events."); return; } diff --git a/src/telemetry/types.ts b/src/telemetry/types.ts index 4f24e545..c44127d6 100644 --- a/src/telemetry/types.ts +++ b/src/telemetry/types.ts @@ -2,6 +2,7 @@ * Result type constants for telemetry events */ export type TelemetryResult = "success" | "failure"; +export type ServerCommand = "start" | "stop"; /** * Base interface for all events @@ -45,3 +46,15 @@ export interface ToolEvent extends BaseEvent { is_atlas?: boolean; } & BaseEvent["properties"]; } + +/** + * Interface for server events + */ +export interface ServerEvent extends BaseEvent { + properties: { + command: ServerCommand; + reason?: string; + startup_time_ms?: number; + runtime_duration_ms?: number; + } & BaseEvent["properties"]; +}