Skip to content

Commit e322daf

Browse files
authored
Merge pull request #2669 from cliffhall/auto-log-level-support-in-everything-server
Everything server - fix regression + auto log level handling support
2 parents 3631d22 + 97c6408 commit e322daf

File tree

7 files changed

+107
-107
lines changed

7 files changed

+107
-107
lines changed

.gitignore

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -122,6 +122,9 @@ dist
122122
# Stores VSCode versions used for testing VSCode extensions
123123
.vscode-test
124124

125+
# Jetbrains IDEs
126+
.idea/
127+
125128
# yarn v2
126129
.yarn/cache
127130
.yarn/unplugged

package-lock.json

Lines changed: 4 additions & 4 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

src/everything/everything.ts

Lines changed: 46 additions & 90 deletions
Original file line numberDiff line numberDiff line change
@@ -14,12 +14,11 @@ import {
1414
ReadResourceRequestSchema,
1515
Resource,
1616
RootsListChangedNotificationSchema,
17-
SetLevelRequestSchema,
1817
SubscribeRequestSchema,
1918
Tool,
2019
ToolSchema,
2120
UnsubscribeRequestSchema,
22-
type Root,
21+
type Root
2322
} from "@modelcontextprotocol/sdk/types.js";
2423
import { z } from "zod";
2524
import { zodToJsonSchema } from "zod-to-json-schema";
@@ -174,58 +173,50 @@ export const createServer = () => {
174173
let subsUpdateInterval: NodeJS.Timeout | undefined;
175174
let stdErrUpdateInterval: NodeJS.Timeout | undefined;
176175

177-
let logLevel: LoggingLevel = "debug";
178176
let logsUpdateInterval: NodeJS.Timeout | undefined;
179177
// Store client capabilities
180178
let clientCapabilities: ClientCapabilities | undefined;
181179

182180
// Roots state management
183181
let currentRoots: Root[] = [];
184182
let clientSupportsRoots = false;
185-
const messages = [
186-
{ level: "debug", data: "Debug-level message" },
187-
{ level: "info", data: "Info-level message" },
188-
{ level: "notice", data: "Notice-level message" },
189-
{ level: "warning", data: "Warning-level message" },
190-
{ level: "error", data: "Error-level message" },
191-
{ level: "critical", data: "Critical-level message" },
192-
{ level: "alert", data: "Alert level-message" },
193-
{ level: "emergency", data: "Emergency-level message" },
194-
];
195-
196-
const isMessageIgnored = (level: LoggingLevel): boolean => {
197-
const currentLevel = messages.findIndex((msg) => logLevel === msg.level);
198-
const messageLevel = messages.findIndex((msg) => level === msg.level);
199-
return messageLevel < currentLevel;
200-
};
201-
202-
// Function to start notification intervals when a client connects
203-
const startNotificationIntervals = () => {
204-
if (!subsUpdateInterval) {
205-
subsUpdateInterval = setInterval(() => {
206-
for (const uri of subscriptions) {
207-
server.notification({
208-
method: "notifications/resources/updated",
209-
params: { uri },
210-
});
211-
}
212-
}, 10000);
213-
}
183+
let sessionId: string | undefined;
184+
185+
// Function to start notification intervals when a client connects
186+
const startNotificationIntervals = (sid?: string|undefined) => {
187+
sessionId = sid;
188+
if (!subsUpdateInterval) {
189+
subsUpdateInterval = setInterval(() => {
190+
for (const uri of subscriptions) {
191+
server.notification({
192+
method: "notifications/resources/updated",
193+
params: { uri },
194+
});
195+
}
196+
}, 10000);
197+
}
214198

215-
if (!logsUpdateInterval) {
216-
logsUpdateInterval = setInterval(() => {
217-
let message = {
218-
method: "notifications/message",
219-
params: messages[Math.floor(Math.random() * messages.length)],
220-
};
221-
if (!isMessageIgnored(message.params.level as LoggingLevel))
222-
server.notification(message);
223-
}, 20000);
199+
console.log(sessionId)
200+
const maybeAppendSessionId = sessionId ? ` - SessionId ${sessionId}`: "";
201+
const messages: { level: LoggingLevel; data: string }[] = [
202+
{ level: "debug", data: `Debug-level message${maybeAppendSessionId}` },
203+
{ level: "info", data: `Info-level message${maybeAppendSessionId}` },
204+
{ level: "notice", data: `Notice-level message${maybeAppendSessionId}` },
205+
{ level: "warning", data: `Warning-level message${maybeAppendSessionId}` },
206+
{ level: "error", data: `Error-level message${maybeAppendSessionId}` },
207+
{ level: "critical", data: `Critical-level message${maybeAppendSessionId}` },
208+
{ level: "alert", data: `Alert level-message${maybeAppendSessionId}` },
209+
{ level: "emergency", data: `Emergency-level message${maybeAppendSessionId}` },
210+
];
211+
212+
if (!logsUpdateInterval) {
213+
console.error("Starting logs update interval");
214+
logsUpdateInterval = setInterval(async () => {
215+
await server.sendLoggingMessage( messages[Math.floor(Math.random() * messages.length)], sessionId);
216+
}, 15000);
224217
}
225218
};
226219

227-
228-
229220
// Helper method to request sampling from client
230221
const requestSampling = async (
231222
context: string,
@@ -918,23 +909,6 @@ export const createServer = () => {
918909
throw new Error(`Unknown reference type`);
919910
});
920911

921-
server.setRequestHandler(SetLevelRequestSchema, async (request) => {
922-
const { level } = request.params;
923-
logLevel = level;
924-
925-
// Demonstrate different log levels
926-
await server.notification({
927-
method: "notifications/message",
928-
params: {
929-
level: "debug",
930-
logger: "test-server",
931-
data: `Logging level set to: ${logLevel}`,
932-
},
933-
});
934-
935-
return {};
936-
});
937-
938912
// Roots protocol handlers
939913
server.setNotificationHandler(RootsListChangedNotificationSchema, async () => {
940914
try {
@@ -944,24 +918,18 @@ export const createServer = () => {
944918
currentRoots = response.roots;
945919

946920
// Log the roots update for demonstration
947-
await server.notification({
948-
method: "notifications/message",
949-
params: {
921+
await server.sendLoggingMessage({
950922
level: "info",
951923
logger: "everything-server",
952924
data: `Roots updated: ${currentRoots.length} root(s) received from client`,
953-
},
954-
});
925+
}, sessionId);
955926
}
956927
} catch (error) {
957-
await server.notification({
958-
method: "notifications/message",
959-
params: {
928+
await server.sendLoggingMessage({
960929
level: "error",
961930
logger: "everything-server",
962931
data: `Failed to request roots from client: ${error instanceof Error ? error.message : String(error)}`,
963-
},
964-
});
932+
}, sessionId);
965933
}
966934
});
967935

@@ -976,43 +944,31 @@ export const createServer = () => {
976944
if (response && 'roots' in response) {
977945
currentRoots = response.roots;
978946

979-
await server.notification({
980-
method: "notifications/message",
981-
params: {
947+
await server.sendLoggingMessage({
982948
level: "info",
983949
logger: "everything-server",
984950
data: `Initial roots received: ${currentRoots.length} root(s) from client`,
985-
},
986-
});
951+
}, sessionId);
987952
} else {
988-
await server.notification({
989-
method: "notifications/message",
990-
params: {
953+
await server.sendLoggingMessage({
991954
level: "warning",
992955
logger: "everything-server",
993956
data: "Client returned no roots set",
994-
},
995-
});
957+
}, sessionId);
996958
}
997959
} catch (error) {
998-
await server.notification({
999-
method: "notifications/message",
1000-
params: {
960+
await server.sendLoggingMessage({
1001961
level: "error",
1002962
logger: "everything-server",
1003963
data: `Failed to request initial roots from client: ${error instanceof Error ? error.message : String(error)}`,
1004-
},
1005-
});
964+
}, sessionId);
1006965
}
1007966
} else {
1008-
await server.notification({
1009-
method: "notifications/message",
1010-
params: {
967+
await server.sendLoggingMessage({
1011968
level: "info",
1012969
logger: "everything-server",
1013970
data: "Client does not support MCP roots protocol",
1014-
},
1015-
});
971+
}, sessionId);
1016972
}
1017973
};
1018974

src/everything/package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@
2222
"start:streamableHttp": "node dist/streamableHttp.js"
2323
},
2424
"dependencies": {
25-
"@modelcontextprotocol/sdk": "^1.17.4",
25+
"@modelcontextprotocol/sdk": "^1.17.5",
2626
"express": "^4.21.1",
2727
"zod": "^3.23.8",
2828
"zod-to-json-schema": "^3.23.5"

src/everything/sse.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@ app.get("/sse", async (req, res) => {
2626
console.error("Client Connected: ", transport.sessionId);
2727

2828
// Start notification intervals after client connects
29-
startNotificationIntervals();
29+
startNotificationIntervals(transport.sessionId);
3030

3131
// Handle close of connection
3232
server.onclose = async () => {

src/everything/stdio.ts

Lines changed: 46 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -2,21 +2,58 @@
22

33
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
44
import { createServer } from "./everything.js";
5+
import {
6+
LoggingLevel,
7+
LoggingLevelSchema,
8+
LoggingMessageNotification,
9+
SetLevelRequestSchema
10+
} from "@modelcontextprotocol/sdk/types.js";
511

612
console.error('Starting default (STDIO) server...');
713

814
async function main() {
9-
const transport = new StdioServerTransport();
10-
const {server, cleanup} = createServer();
15+
const transport = new StdioServerTransport();
16+
const {server, cleanup, startNotificationIntervals } = createServer();
1117

12-
await server.connect(transport);
18+
// Currently, for STDIO servers, automatic log-level support is not available, as levels are tracked by sessionId.
19+
// The listener will be set, so if the STDIO server advertises support for logging, and the client sends a setLevel
20+
// request, it will be handled and thus not throw a "Method not found" error. However, the STDIO server will need to
21+
// implement its own listener and level handling for now. This will be remediated in a future SDK version.
1322

14-
// Cleanup on exit
15-
process.on("SIGINT", async () => {
16-
await cleanup();
17-
await server.close();
18-
process.exit(0);
19-
});
23+
let logLevel: LoggingLevel = "debug";
24+
server.setRequestHandler(SetLevelRequestSchema, async (request) => {
25+
const { level } = request.params;
26+
logLevel = level;
27+
return {};
28+
});
29+
30+
server.sendLoggingMessage = async (params: LoggingMessageNotification["params"], _: string|undefined): Promise<void> => {
31+
const LOG_LEVEL_SEVERITY = new Map(
32+
LoggingLevelSchema.options.map((level, index) => [level, index])
33+
);
34+
35+
const isMessageIgnored = (level: LoggingLevel): boolean => {
36+
const currentLevel = logLevel;
37+
return (currentLevel)
38+
? LOG_LEVEL_SEVERITY.get(level)! < LOG_LEVEL_SEVERITY.get(currentLevel)!
39+
: false;
40+
};
41+
42+
if (!isMessageIgnored(params.level)) {
43+
return server.notification({method: "notifications/message", params})
44+
}
45+
46+
}
47+
48+
await server.connect(transport);
49+
startNotificationIntervals();
50+
51+
// Cleanup on exit
52+
process.on("SIGINT", async () => {
53+
await cleanup();
54+
await server.close();
55+
process.exit(0);
56+
});
2057
}
2158

2259
main().catch((error) => {

src/everything/streamableHttp.ts

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@ app.post('/mcp', async (req: Request, res: Response) => {
2222
transport = transports.get(sessionId)!;
2323
} else if (!sessionId) {
2424

25-
const { server, cleanup } = createServer();
25+
const { server, cleanup, startNotificationIntervals } = createServer();
2626

2727
// New initialization request
2828
const eventStore = new InMemoryEventStore();
@@ -53,7 +53,11 @@ app.post('/mcp', async (req: Request, res: Response) => {
5353
await server.connect(transport);
5454

5555
await transport.handleRequest(req, res);
56-
return; // Already handled
56+
57+
// Wait until initialize is complete and transport will have a sessionId
58+
startNotificationIntervals(transport.sessionId);
59+
60+
return; // Already handled
5761
} else {
5862
// Invalid request - no session ID or not initialization request
5963
res.status(400).json({

0 commit comments

Comments
 (0)