diff --git a/src/common/logger.ts b/src/common/logger.ts index 1fe3cc73a..07b126aa4 100644 --- a/src/common/logger.ts +++ b/src/common/logger.ts @@ -48,6 +48,7 @@ export const LogId = { toolUpdateFailure: mongoLogId(1_005_001), resourceUpdateFailure: mongoLogId(1_005_002), + updateToolMetadata: mongoLogId(1_005_003), streamableHttpTransportStarted: mongoLogId(1_006_001), streamableHttpTransportSessionCloseFailure: mongoLogId(1_006_002), diff --git a/src/server.ts b/src/server.ts index 7d4a10b16..458bcd28b 100644 --- a/src/server.ts +++ b/src/server.ts @@ -62,12 +62,15 @@ export class Server { } async connect(transport: Transport): Promise { - // Resources are now reactive, so we register them ASAP so they can listen to events like + await this.validateConfig(); + // Register resources after the server is initialized so they can listen to events like // connection events. this.registerResources(); - await this.validateConfig(); - - this.mcpServer.server.registerCapabilities({ logging: {}, resources: { listChanged: true, subscribe: true } }); + this.mcpServer.server.registerCapabilities({ + logging: {}, + resources: { listChanged: true, subscribe: true }, + instructions: this.getInstructions(), + }); // TODO: Eventually we might want to make tools reactive too instead of relying on custom logic. this.registerTools(); @@ -134,17 +137,17 @@ export class Server { message: `Server with version ${packageInfo.version} started with transport ${transport.constructor.name} and agent runner ${JSON.stringify(this.session.mcpClient)}`, }); - this.emitServerEvent("start", Date.now() - this.startTime); + this.emitServerTelemetryEvent("start", Date.now() - this.startTime); }; this.mcpServer.server.onclose = (): void => { const closeTime = Date.now(); - this.emitServerEvent("stop", Date.now() - closeTime); + this.emitServerTelemetryEvent("stop", Date.now() - closeTime); }; this.mcpServer.server.onerror = (error: Error): void => { const closeTime = Date.now(); - this.emitServerEvent("stop", Date.now() - closeTime, error); + this.emitServerTelemetryEvent("stop", Date.now() - closeTime, error); }; await this.mcpServer.connect(transport); @@ -161,17 +164,18 @@ export class Server { } public sendResourceUpdated(uri: string): void { + this.session.logger.info({ + id: LogId.resourceUpdateFailure, + context: "resources", + message: `Resource updated: ${uri}`, + }); + if (this.subscriptions.has(uri)) { void this.mcpServer.server.sendResourceUpdated({ uri }); } } - /** - * Emits a server event - * @param command - The server command (e.g., "start", "stop", "register", "deregister") - * @param additionalProperties - Additional properties specific to the event - */ - private emitServerEvent(command: ServerCommand, commandDuration: number, error?: Error): void { + private emitServerTelemetryEvent(command: ServerCommand, commandDuration: number, error?: Error): void { const event: ServerEvent = { timestamp: new Date().toISOString(), source: "mdbmcp", @@ -262,6 +266,24 @@ export class Server { } } + private getInstructions(): string { + let instructions = ` + This is the MongoDB MCP server. + `; + if (this.userConfig.connectionString) { + instructions += ` + This MCP server was configured with a MongoDB connection string, and you can assume that you are connected to a MongoDB cluster. + `; + } + + if (this.userConfig.apiClientId && this.userConfig.apiClientSecret) { + instructions += ` + This MCP server was configured with MongoDB Atlas API credentials.`; + } + + return instructions; + } + private async connectToConfigConnectionString(): Promise { if (this.userConfig.connectionString) { try { diff --git a/src/tools/mongodb/connect/connect.ts b/src/tools/mongodb/connect/connect.ts index d7ed16d26..601fcb368 100644 --- a/src/tools/mongodb/connect/connect.ts +++ b/src/tools/mongodb/connect/connect.ts @@ -4,6 +4,7 @@ import { MongoDBToolBase } from "../mongodbTool.js"; import type { ToolArgs, OperationType, ToolConstructorParams } from "../../tool.js"; import assert from "assert"; import type { Server } from "../../../server.js"; +import { LogId } from "../../../common/logger.js"; const disconnectedSchema = z .object({ @@ -27,7 +28,8 @@ const disconnectedName = "connect" as const; const connectedDescription = "Switch to a different MongoDB connection. If the user has configured a connection string or has previously called the connect tool, a connection is already established and there's no need to call this tool unless the user has explicitly requested to switch to a new instance."; -const disconnectedDescription = "Connect to a MongoDB instance"; +const disconnectedDescription = + "Connect to a MongoDB instance. The config resource captures if the server is already connected to a MongoDB cluster. If the user has configured a connection string or has previously called the connect tool, a connection is already established and there's no need to call this tool unless the user has explicitly requested to switch to a new MongoDB cluster."; export class ConnectTool extends MongoDBToolBase { public name: typeof connectedName | typeof disconnectedName = disconnectedName; @@ -84,18 +86,30 @@ export class ConnectTool extends MongoDBToolBase { } private updateMetadata(): void { + let name: string; + let description: string; + let inputSchema: z.ZodObject; + if (this.session.isConnectedToMongoDB) { - this.update?.({ - name: connectedName, - description: connectedDescription, - inputSchema: connectedSchema, - }); + name = connectedName; + description = connectedDescription; + inputSchema = connectedSchema; } else { - this.update?.({ - name: disconnectedName, - description: disconnectedDescription, - inputSchema: disconnectedSchema, - }); + name = disconnectedName; + description = disconnectedDescription; + inputSchema = disconnectedSchema; } + + this.session.logger.info({ + id: LogId.updateToolMetadata, + context: "tool", + message: `Updating tool metadata to ${name}`, + }); + + this.update?.({ + name, + description, + inputSchema, + }); } } diff --git a/tests/integration/tools/mongodb/connect/connect.test.ts b/tests/integration/tools/mongodb/connect/connect.test.ts index 9d60447f6..46526fe5b 100644 --- a/tests/integration/tools/mongodb/connect/connect.test.ts +++ b/tests/integration/tools/mongodb/connect/connect.test.ts @@ -91,14 +91,19 @@ describeWithMongoDB( describeWithMongoDB( "Connect tool", (integration) => { - validateToolMetadata(integration, "connect", "Connect to a MongoDB instance", [ - { - name: "connectionString", - description: "MongoDB connection string (in the mongodb:// or mongodb+srv:// format)", - type: "string", - required: true, - }, - ]); + validateToolMetadata( + integration, + "connect", + "Connect to a MongoDB instance. The config resource captures if the server is already connected to a MongoDB cluster. If the user has configured a connection string or has previously called the connect tool, a connection is already established and there's no need to call this tool unless the user has explicitly requested to switch to a new MongoDB cluster.", + [ + { + name: "connectionString", + description: "MongoDB connection string (in the mongodb:// or mongodb+srv:// format)", + type: "string", + required: true, + }, + ] + ); validateThrowsForInvalidArguments(integration, "connect", [{}, { connectionString: 123 }]);