mirror of
https://github.com/modelcontextprotocol/servers.git
synced 2026-04-17 15:43:24 +02:00
* in .gitignore
- add .idea/ for Jetbrains IDEs
* in everything.ts
- remove import of SetLevelRequestSchema
- remove logLevel var
- add sessionId var
- in startNotificationIntervals function
- add optional sid argument
- set sessionId to sid
- define messages to be sent, adding sessionId if present
- remove setRequestHandler call for SetLevelRequestSchema
- replace server.notification calls that sent "notifications/message" objects with calls to server.sendLoggingMessage, passing just the parameters and sessionId.
* In package.json & package-lock.json
- bump TS SDK version to 1.17.5
* In sse.ts, pass transport.sessionId to startNotificationIntervals call
* In stdio.ts
- destructure startNotificationIntervals from createServer call
- implement custom logging request handler and server.sendLoggingMessage implementation, as a
workaround for the fact that the SDK's automatic log level handling currently only tracks requested log level by session id. This will be fixed in a followup PR for the SDK
- call the startNotificationIntervals function after connecting the transport to the server
* In streamableHttp.ts
- destructure startNotificationIntervals from createServer call
- call startNotificationIntervals passing the transport.sessionId after connecting the transport to the server
This commit is contained in:
3
.gitignore
vendored
3
.gitignore
vendored
@@ -122,6 +122,9 @@ dist
|
||||
# Stores VSCode versions used for testing VSCode extensions
|
||||
.vscode-test
|
||||
|
||||
# Jetbrains IDEs
|
||||
.idea/
|
||||
|
||||
# yarn v2
|
||||
.yarn/cache
|
||||
.yarn/unplugged
|
||||
|
||||
8
package-lock.json
generated
8
package-lock.json
generated
@@ -5818,7 +5818,7 @@
|
||||
"version": "0.6.2",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@modelcontextprotocol/sdk": "^1.17.4",
|
||||
"@modelcontextprotocol/sdk": "^1.17.5",
|
||||
"express": "^4.21.1",
|
||||
"zod": "^3.23.8",
|
||||
"zod-to-json-schema": "^3.23.5"
|
||||
@@ -5833,9 +5833,9 @@
|
||||
}
|
||||
},
|
||||
"src/everything/node_modules/@modelcontextprotocol/sdk": {
|
||||
"version": "1.17.4",
|
||||
"resolved": "https://registry.npmjs.org/@modelcontextprotocol/sdk/-/sdk-1.17.4.tgz",
|
||||
"integrity": "sha512-zq24hfuAmmlNZvik0FLI58uE5sriN0WWsQzIlYnzSuKDAHFqJtBFrl/LfB1NLgJT5Y7dEBzaX4yAKqOPrcetaw==",
|
||||
"version": "1.17.5",
|
||||
"resolved": "https://registry.npmjs.org/@modelcontextprotocol/sdk/-/sdk-1.17.5.tgz",
|
||||
"integrity": "sha512-QakrKIGniGuRVfWBdMsDea/dx1PNE739QJ7gCM41s9q+qaCYTHCdsIBXQVVXry3mfWAiaM9kT22Hyz53Uw8mfg==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"ajv": "^6.12.6",
|
||||
|
||||
@@ -14,12 +14,11 @@ import {
|
||||
ReadResourceRequestSchema,
|
||||
Resource,
|
||||
RootsListChangedNotificationSchema,
|
||||
SetLevelRequestSchema,
|
||||
SubscribeRequestSchema,
|
||||
Tool,
|
||||
ToolSchema,
|
||||
UnsubscribeRequestSchema,
|
||||
type Root,
|
||||
type Root
|
||||
} from "@modelcontextprotocol/sdk/types.js";
|
||||
import { z } from "zod";
|
||||
import { zodToJsonSchema } from "zod-to-json-schema";
|
||||
@@ -174,7 +173,6 @@ export const createServer = () => {
|
||||
let subsUpdateInterval: NodeJS.Timeout | undefined;
|
||||
let stdErrUpdateInterval: NodeJS.Timeout | undefined;
|
||||
|
||||
let logLevel: LoggingLevel = "debug";
|
||||
let logsUpdateInterval: NodeJS.Timeout | undefined;
|
||||
// Store client capabilities
|
||||
let clientCapabilities: ClientCapabilities | undefined;
|
||||
@@ -182,50 +180,43 @@ export const createServer = () => {
|
||||
// Roots state management
|
||||
let currentRoots: Root[] = [];
|
||||
let clientSupportsRoots = false;
|
||||
const messages = [
|
||||
{ level: "debug", data: "Debug-level message" },
|
||||
{ level: "info", data: "Info-level message" },
|
||||
{ level: "notice", data: "Notice-level message" },
|
||||
{ level: "warning", data: "Warning-level message" },
|
||||
{ level: "error", data: "Error-level message" },
|
||||
{ level: "critical", data: "Critical-level message" },
|
||||
{ level: "alert", data: "Alert level-message" },
|
||||
{ level: "emergency", data: "Emergency-level message" },
|
||||
];
|
||||
let sessionId: string | undefined;
|
||||
|
||||
const isMessageIgnored = (level: LoggingLevel): boolean => {
|
||||
const currentLevel = messages.findIndex((msg) => logLevel === msg.level);
|
||||
const messageLevel = messages.findIndex((msg) => level === msg.level);
|
||||
return messageLevel < currentLevel;
|
||||
};
|
||||
// Function to start notification intervals when a client connects
|
||||
const startNotificationIntervals = (sid?: string|undefined) => {
|
||||
sessionId = sid;
|
||||
if (!subsUpdateInterval) {
|
||||
subsUpdateInterval = setInterval(() => {
|
||||
for (const uri of subscriptions) {
|
||||
server.notification({
|
||||
method: "notifications/resources/updated",
|
||||
params: { uri },
|
||||
});
|
||||
}
|
||||
}, 10000);
|
||||
}
|
||||
|
||||
// Function to start notification intervals when a client connects
|
||||
const startNotificationIntervals = () => {
|
||||
if (!subsUpdateInterval) {
|
||||
subsUpdateInterval = setInterval(() => {
|
||||
for (const uri of subscriptions) {
|
||||
server.notification({
|
||||
method: "notifications/resources/updated",
|
||||
params: { uri },
|
||||
});
|
||||
}
|
||||
}, 10000);
|
||||
}
|
||||
console.log(sessionId)
|
||||
const maybeAppendSessionId = sessionId ? ` - SessionId ${sessionId}`: "";
|
||||
const messages: { level: LoggingLevel; data: string }[] = [
|
||||
{ level: "debug", data: `Debug-level message${maybeAppendSessionId}` },
|
||||
{ level: "info", data: `Info-level message${maybeAppendSessionId}` },
|
||||
{ level: "notice", data: `Notice-level message${maybeAppendSessionId}` },
|
||||
{ level: "warning", data: `Warning-level message${maybeAppendSessionId}` },
|
||||
{ level: "error", data: `Error-level message${maybeAppendSessionId}` },
|
||||
{ level: "critical", data: `Critical-level message${maybeAppendSessionId}` },
|
||||
{ level: "alert", data: `Alert level-message${maybeAppendSessionId}` },
|
||||
{ level: "emergency", data: `Emergency-level message${maybeAppendSessionId}` },
|
||||
];
|
||||
|
||||
if (!logsUpdateInterval) {
|
||||
logsUpdateInterval = setInterval(() => {
|
||||
let message = {
|
||||
method: "notifications/message",
|
||||
params: messages[Math.floor(Math.random() * messages.length)],
|
||||
};
|
||||
if (!isMessageIgnored(message.params.level as LoggingLevel))
|
||||
server.notification(message);
|
||||
}, 20000);
|
||||
if (!logsUpdateInterval) {
|
||||
console.error("Starting logs update interval");
|
||||
logsUpdateInterval = setInterval(async () => {
|
||||
await server.sendLoggingMessage( messages[Math.floor(Math.random() * messages.length)], sessionId);
|
||||
}, 15000);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
|
||||
// Helper method to request sampling from client
|
||||
const requestSampling = async (
|
||||
context: string,
|
||||
@@ -918,23 +909,6 @@ export const createServer = () => {
|
||||
throw new Error(`Unknown reference type`);
|
||||
});
|
||||
|
||||
server.setRequestHandler(SetLevelRequestSchema, async (request) => {
|
||||
const { level } = request.params;
|
||||
logLevel = level;
|
||||
|
||||
// Demonstrate different log levels
|
||||
await server.notification({
|
||||
method: "notifications/message",
|
||||
params: {
|
||||
level: "debug",
|
||||
logger: "test-server",
|
||||
data: `Logging level set to: ${logLevel}`,
|
||||
},
|
||||
});
|
||||
|
||||
return {};
|
||||
});
|
||||
|
||||
// Roots protocol handlers
|
||||
server.setNotificationHandler(RootsListChangedNotificationSchema, async () => {
|
||||
try {
|
||||
@@ -944,24 +918,18 @@ export const createServer = () => {
|
||||
currentRoots = response.roots;
|
||||
|
||||
// Log the roots update for demonstration
|
||||
await server.notification({
|
||||
method: "notifications/message",
|
||||
params: {
|
||||
await server.sendLoggingMessage({
|
||||
level: "info",
|
||||
logger: "everything-server",
|
||||
data: `Roots updated: ${currentRoots.length} root(s) received from client`,
|
||||
},
|
||||
});
|
||||
}, sessionId);
|
||||
}
|
||||
} catch (error) {
|
||||
await server.notification({
|
||||
method: "notifications/message",
|
||||
params: {
|
||||
await server.sendLoggingMessage({
|
||||
level: "error",
|
||||
logger: "everything-server",
|
||||
data: `Failed to request roots from client: ${error instanceof Error ? error.message : String(error)}`,
|
||||
},
|
||||
});
|
||||
}, sessionId);
|
||||
}
|
||||
});
|
||||
|
||||
@@ -976,43 +944,31 @@ export const createServer = () => {
|
||||
if (response && 'roots' in response) {
|
||||
currentRoots = response.roots;
|
||||
|
||||
await server.notification({
|
||||
method: "notifications/message",
|
||||
params: {
|
||||
await server.sendLoggingMessage({
|
||||
level: "info",
|
||||
logger: "everything-server",
|
||||
data: `Initial roots received: ${currentRoots.length} root(s) from client`,
|
||||
},
|
||||
});
|
||||
}, sessionId);
|
||||
} else {
|
||||
await server.notification({
|
||||
method: "notifications/message",
|
||||
params: {
|
||||
await server.sendLoggingMessage({
|
||||
level: "warning",
|
||||
logger: "everything-server",
|
||||
data: "Client returned no roots set",
|
||||
},
|
||||
});
|
||||
}, sessionId);
|
||||
}
|
||||
} catch (error) {
|
||||
await server.notification({
|
||||
method: "notifications/message",
|
||||
params: {
|
||||
await server.sendLoggingMessage({
|
||||
level: "error",
|
||||
logger: "everything-server",
|
||||
data: `Failed to request initial roots from client: ${error instanceof Error ? error.message : String(error)}`,
|
||||
},
|
||||
});
|
||||
}, sessionId);
|
||||
}
|
||||
} else {
|
||||
await server.notification({
|
||||
method: "notifications/message",
|
||||
params: {
|
||||
await server.sendLoggingMessage({
|
||||
level: "info",
|
||||
logger: "everything-server",
|
||||
data: "Client does not support MCP roots protocol",
|
||||
},
|
||||
});
|
||||
}, sessionId);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
@@ -22,7 +22,7 @@
|
||||
"start:streamableHttp": "node dist/streamableHttp.js"
|
||||
},
|
||||
"dependencies": {
|
||||
"@modelcontextprotocol/sdk": "^1.17.4",
|
||||
"@modelcontextprotocol/sdk": "^1.17.5",
|
||||
"express": "^4.21.1",
|
||||
"zod": "^3.23.8",
|
||||
"zod-to-json-schema": "^3.23.5"
|
||||
|
||||
@@ -26,7 +26,7 @@ app.get("/sse", async (req, res) => {
|
||||
console.error("Client Connected: ", transport.sessionId);
|
||||
|
||||
// Start notification intervals after client connects
|
||||
startNotificationIntervals();
|
||||
startNotificationIntervals(transport.sessionId);
|
||||
|
||||
// Handle close of connection
|
||||
server.onclose = async () => {
|
||||
|
||||
@@ -2,21 +2,58 @@
|
||||
|
||||
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
|
||||
import { createServer } from "./everything.js";
|
||||
import {
|
||||
LoggingLevel,
|
||||
LoggingLevelSchema,
|
||||
LoggingMessageNotification,
|
||||
SetLevelRequestSchema
|
||||
} from "@modelcontextprotocol/sdk/types.js";
|
||||
|
||||
console.error('Starting default (STDIO) server...');
|
||||
|
||||
async function main() {
|
||||
const transport = new StdioServerTransport();
|
||||
const {server, cleanup} = createServer();
|
||||
const transport = new StdioServerTransport();
|
||||
const {server, cleanup, startNotificationIntervals } = createServer();
|
||||
|
||||
await server.connect(transport);
|
||||
// Currently, for STDIO servers, automatic log-level support is not available, as levels are tracked by sessionId.
|
||||
// The listener will be set, so if the STDIO server advertises support for logging, and the client sends a setLevel
|
||||
// request, it will be handled and thus not throw a "Method not found" error. However, the STDIO server will need to
|
||||
// implement its own listener and level handling for now. This will be remediated in a future SDK version.
|
||||
|
||||
// Cleanup on exit
|
||||
process.on("SIGINT", async () => {
|
||||
await cleanup();
|
||||
await server.close();
|
||||
process.exit(0);
|
||||
});
|
||||
let logLevel: LoggingLevel = "debug";
|
||||
server.setRequestHandler(SetLevelRequestSchema, async (request) => {
|
||||
const { level } = request.params;
|
||||
logLevel = level;
|
||||
return {};
|
||||
});
|
||||
|
||||
server.sendLoggingMessage = async (params: LoggingMessageNotification["params"], _: string|undefined): Promise<void> => {
|
||||
const LOG_LEVEL_SEVERITY = new Map(
|
||||
LoggingLevelSchema.options.map((level, index) => [level, index])
|
||||
);
|
||||
|
||||
const isMessageIgnored = (level: LoggingLevel): boolean => {
|
||||
const currentLevel = logLevel;
|
||||
return (currentLevel)
|
||||
? LOG_LEVEL_SEVERITY.get(level)! < LOG_LEVEL_SEVERITY.get(currentLevel)!
|
||||
: false;
|
||||
};
|
||||
|
||||
if (!isMessageIgnored(params.level)) {
|
||||
return server.notification({method: "notifications/message", params})
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
await server.connect(transport);
|
||||
startNotificationIntervals();
|
||||
|
||||
// Cleanup on exit
|
||||
process.on("SIGINT", async () => {
|
||||
await cleanup();
|
||||
await server.close();
|
||||
process.exit(0);
|
||||
});
|
||||
}
|
||||
|
||||
main().catch((error) => {
|
||||
|
||||
@@ -22,7 +22,7 @@ app.post('/mcp', async (req: Request, res: Response) => {
|
||||
transport = transports.get(sessionId)!;
|
||||
} else if (!sessionId) {
|
||||
|
||||
const { server, cleanup } = createServer();
|
||||
const { server, cleanup, startNotificationIntervals } = createServer();
|
||||
|
||||
// New initialization request
|
||||
const eventStore = new InMemoryEventStore();
|
||||
@@ -53,7 +53,11 @@ app.post('/mcp', async (req: Request, res: Response) => {
|
||||
await server.connect(transport);
|
||||
|
||||
await transport.handleRequest(req, res);
|
||||
return; // Already handled
|
||||
|
||||
// Wait until initialize is complete and transport will have a sessionId
|
||||
startNotificationIntervals(transport.sessionId);
|
||||
|
||||
return; // Already handled
|
||||
} else {
|
||||
// Invalid request - no session ID or not initialization request
|
||||
res.status(400).json({
|
||||
|
||||
Reference in New Issue
Block a user