Skip to content

Commit cd5cf37

Browse files
nirinchevkmruiz
andauthored
fix: handle setLogLevel requests (#486)
Co-authored-by: Kevin Mas Ruiz <[email protected]>
1 parent be27061 commit cd5cf37

File tree

7 files changed

+82
-20
lines changed

7 files changed

+82
-20
lines changed

.github/workflows/check.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -55,4 +55,4 @@ jobs:
5555
rm -rf node_modules
5656
npm pkg set scripts.prepare="exit 0"
5757
npm install --omit=dev
58-
- run: npx -y @modelcontextprotocol/[email protected].2 --cli --method tools/list -- node dist/esm/index.js
58+
- run: npx -y @modelcontextprotocol/[email protected].5 --cli --method tools/list -- node dist/esm/index.js

package-lock.json

Lines changed: 1 addition & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -62,7 +62,7 @@
6262
"@ai-sdk/google": "^1.2.22",
6363
"@ai-sdk/openai": "^1.3.23",
6464
"@eslint/js": "^9.30.1",
65-
"@modelcontextprotocol/inspector": "^0.16.0",
65+
"@modelcontextprotocol/inspector": "^0.16.5",
6666
"@mongodb-js/oidc-mock-provider": "^0.11.3",
6767
"@redocly/cli": "^2.0.7",
6868
"@types/express": "^5.0.1",

src/common/logger.ts

Lines changed: 22 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2,9 +2,9 @@ import fs from "fs/promises";
22
import type { MongoLogId, MongoLogWriter } from "mongodb-log-writer";
33
import { mongoLogId, MongoLogManager } from "mongodb-log-writer";
44
import redact from "mongodb-redact";
5-
import type { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
65
import type { LoggingMessageNotification } from "@modelcontextprotocol/sdk/types.js";
76
import { EventEmitter } from "events";
7+
import type { Server } from "../lib.js";
88

99
export type LogLevel = LoggingMessageNotification["params"]["level"];
1010

@@ -250,19 +250,37 @@ export class DiskLogger extends LoggerBase<{ initialized: [] }> {
250250
}
251251

252252
export class McpLogger extends LoggerBase {
253-
public constructor(private readonly server: McpServer) {
253+
private static readonly LOG_LEVELS: LogLevel[] = [
254+
"debug",
255+
"info",
256+
"notice",
257+
"warning",
258+
"error",
259+
"critical",
260+
"alert",
261+
"emergency",
262+
];
263+
264+
public constructor(private readonly server: Server) {
254265
super();
255266
}
256267

257268
protected readonly type: LoggerType = "mcp";
258269

259270
protected logCore(level: LogLevel, payload: LogPayload): void {
260271
// Only log if the server is connected
261-
if (!this.server?.isConnected()) {
272+
if (!this.server.mcpServer.isConnected()) {
262273
return;
263274
}
264275

265-
void this.server.server.sendLoggingMessage({
276+
const minimumLevel = McpLogger.LOG_LEVELS.indexOf(this.server.mcpLogLevel);
277+
const currentLevel = McpLogger.LOG_LEVELS.indexOf(level);
278+
if (minimumLevel > currentLevel) {
279+
// Don't log if the requested level is lower than the minimum level
280+
return;
281+
}
282+
283+
void this.server.mcpServer.server.sendLoggingMessage({
266284
level,
267285
data: `[${payload.context}]: ${payload.message}`,
268286
});

src/server.ts

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import type { Transport } from "@modelcontextprotocol/sdk/shared/transport.js";
44
import { AtlasTools } from "./tools/atlas/tools.js";
55
import { MongoDbTools } from "./tools/mongodb/tools.js";
66
import { Resources } from "./resources/resources.js";
7+
import type { LogLevel } from "./common/logger.js";
78
import { LogId } from "./common/logger.js";
89
import type { Telemetry } from "./telemetry/telemetry.js";
910
import type { UserConfig } from "./common/config.js";
@@ -12,6 +13,7 @@ import { type ServerCommand } from "./telemetry/types.js";
1213
import type { CallToolResult } from "@modelcontextprotocol/sdk/types.js";
1314
import {
1415
CallToolRequestSchema,
16+
SetLevelRequestSchema,
1517
SubscribeRequestSchema,
1618
UnsubscribeRequestSchema,
1719
} from "@modelcontextprotocol/sdk/types.js";
@@ -32,6 +34,13 @@ export class Server {
3234
private readonly telemetry: Telemetry;
3335
public readonly userConfig: UserConfig;
3436
public readonly tools: ToolBase[] = [];
37+
38+
private _mcpLogLevel: LogLevel = "debug";
39+
40+
public get mcpLogLevel(): LogLevel {
41+
return this._mcpLogLevel;
42+
}
43+
3544
private readonly startTime: number;
3645
private readonly subscriptions = new Set<string>();
3746

@@ -97,6 +106,11 @@ export class Server {
97106
return {};
98107
});
99108

109+
this.mcpServer.server.setRequestHandler(SetLevelRequestSchema, ({ params }) => {
110+
this._mcpLogLevel = params.level;
111+
return {};
112+
});
113+
100114
this.mcpServer.server.oninitialized = (): void => {
101115
this.session.setMcpClient(this.mcpServer.server.getClientVersion());
102116
// Placed here to start the connection to the config connection string as soon as the server is initialized.

src/transports/base.ts

Lines changed: 10 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -44,12 +44,7 @@ export abstract class TransportRunnerBase {
4444
version: packageInfo.version,
4545
});
4646

47-
const loggers = [this.logger];
48-
if (this.userConfig.loggers.includes("mcp")) {
49-
loggers.push(new McpLogger(mcpServer));
50-
}
51-
52-
const logger = new CompositeLogger(...loggers);
47+
const logger = new CompositeLogger(this.logger);
5348
const exportsManager = ExportsManager.init(this.userConfig, logger);
5449
const connectionManager = new ConnectionManager(this.userConfig, this.driverOptions, logger, this.deviceId);
5550

@@ -64,12 +59,20 @@ export abstract class TransportRunnerBase {
6459

6560
const telemetry = Telemetry.create(session, this.userConfig, this.deviceId);
6661

67-
return new Server({
62+
const result = new Server({
6863
mcpServer,
6964
session,
7065
telemetry,
7166
userConfig: this.userConfig,
7267
});
68+
69+
// We need to create the MCP logger after the server is constructed
70+
// because it needs the server instance
71+
if (this.userConfig.loggers.includes("mcp")) {
72+
logger.addLogger(new McpLogger(result));
73+
}
74+
75+
return result;
7376
}
7477

7578
abstract start(): Promise<void>;

tests/unit/logger.test.ts

Lines changed: 33 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,19 +1,20 @@
11
import type { MockInstance } from "vitest";
22
import { describe, beforeEach, afterEach, vi, it, expect } from "vitest";
3-
import type { LoggerType } from "../../src/common/logger.js";
3+
import type { LoggerType, LogLevel } from "../../src/common/logger.js";
44
import { CompositeLogger, ConsoleLogger, DiskLogger, LogId, McpLogger } from "../../src/common/logger.js";
5-
import type { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
65
import os from "os";
76
import * as path from "path";
87
import * as fs from "fs/promises";
98
import { once } from "events";
9+
import type { Server } from "../../src/server.js";
1010

1111
describe("Logger", () => {
1212
let consoleErrorSpy: MockInstance<typeof console.error>;
1313
let consoleLogger: ConsoleLogger;
1414

1515
let mcpLoggerSpy: MockInstance;
1616
let mcpLogger: McpLogger;
17+
let minimumMcpLogLevel: LogLevel;
1718

1819
beforeEach(() => {
1920
// Mock console.error before creating the ConsoleLogger
@@ -22,12 +23,18 @@ describe("Logger", () => {
2223
consoleLogger = new ConsoleLogger();
2324

2425
mcpLoggerSpy = vi.fn();
26+
minimumMcpLogLevel = "debug";
2527
mcpLogger = new McpLogger({
26-
server: {
27-
sendLoggingMessage: mcpLoggerSpy,
28+
mcpServer: {
29+
server: {
30+
sendLoggingMessage: mcpLoggerSpy,
31+
},
32+
isConnected: () => true,
2833
},
29-
isConnected: () => true,
30-
} as unknown as McpServer);
34+
get mcpLogLevel() {
35+
return minimumMcpLogLevel;
36+
},
37+
} as unknown as Server);
3138
});
3239

3340
afterEach(() => {
@@ -306,4 +313,24 @@ describe("Logger", () => {
306313
});
307314
});
308315
});
316+
317+
describe("mcp logger", () => {
318+
it("filters out messages below the minimum log level", () => {
319+
minimumMcpLogLevel = "debug";
320+
mcpLogger.log("debug", { id: LogId.serverInitialized, context: "test", message: "Debug message" });
321+
322+
expect(mcpLoggerSpy).toHaveBeenCalledOnce();
323+
expect(getLastMcpLogMessage()).toContain("Debug message");
324+
325+
minimumMcpLogLevel = "info";
326+
mcpLogger.log("debug", { id: LogId.serverInitialized, context: "test", message: "Debug message 2" });
327+
328+
expect(mcpLoggerSpy).toHaveBeenCalledTimes(1);
329+
330+
mcpLogger.log("alert", { id: LogId.serverInitialized, context: "test", message: "Alert message" });
331+
332+
expect(mcpLoggerSpy).toHaveBeenCalledTimes(2);
333+
expect(getLastMcpLogMessage()).toContain("Alert message");
334+
});
335+
});
309336
});

0 commit comments

Comments
 (0)