diff --git a/package-lock.json b/package-lock.json index f8f1d008..f1e347e8 100644 --- a/package-lock.json +++ b/package-lock.json @@ -11,7 +11,7 @@ "dependencies": { "@apify/datastructures": "^2.0.3", "@apify/log": "^2.5.16", - "@modelcontextprotocol/sdk": "^1.13.2", + "@modelcontextprotocol/sdk": "^1.17.4", "@types/turndown": "^5.0.5", "ajv": "^8.17.1", "algoliasearch": "^5.31.0", @@ -1257,9 +1257,9 @@ "license": "BSD-2-Clause" }, "node_modules/@modelcontextprotocol/sdk": { - "version": "1.13.2", - "resolved": "https://registry.npmjs.org/@modelcontextprotocol/sdk/-/sdk-1.13.2.tgz", - "integrity": "sha512-Vx7qOcmoKkR3qhaQ9qf3GxiVKCEu+zfJddHv6x3dY/9P6+uIwJnmuAur5aB+4FDXf41rRrDnOEGkviX5oYZ67w==", + "version": "1.17.4", + "resolved": "https://registry.npmjs.org/@modelcontextprotocol/sdk/-/sdk-1.17.4.tgz", + "integrity": "sha512-zq24hfuAmmlNZvik0FLI58uE5sriN0WWsQzIlYnzSuKDAHFqJtBFrl/LfB1NLgJT5Y7dEBzaX4yAKqOPrcetaw==", "license": "MIT", "dependencies": { "ajv": "^6.12.6", @@ -1267,6 +1267,7 @@ "cors": "^2.8.5", "cross-spawn": "^7.0.5", "eventsource": "^3.0.2", + "eventsource-parser": "^3.0.0", "express": "^5.0.1", "express-rate-limit": "^7.5.0", "pkce-challenge": "^5.0.0", diff --git a/package.json b/package.json index 22392412..8d412650 100644 --- a/package.json +++ b/package.json @@ -37,7 +37,7 @@ "dependencies": { "@apify/datastructures": "^2.0.3", "@apify/log": "^2.5.16", - "@modelcontextprotocol/sdk": "^1.13.2", + "@modelcontextprotocol/sdk": "^1.17.4", "@types/turndown": "^5.0.5", "ajv": "^8.17.1", "algoliasearch": "^5.31.0", diff --git a/src/mcp/const.ts b/src/mcp/const.ts index f0aa3d91..b04baad5 100644 --- a/src/mcp/const.ts +++ b/src/mcp/const.ts @@ -1,3 +1,14 @@ export const MAX_TOOL_NAME_LENGTH = 64; export const SERVER_ID_LENGTH = 8; export const EXTERNAL_TOOL_CALL_TIMEOUT_MSEC = 120_000; // 2 minutes + +export const LOG_LEVEL_MAP: Record = { + debug: 0, + info: 1, + notice: 2, + warning: 3, + error: 4, + critical: 5, + alert: 6, + emergency: 7, +}; diff --git a/src/mcp/server.ts b/src/mcp/server.ts index ef642063..620764e3 100644 --- a/src/mcp/server.ts +++ b/src/mcp/server.ts @@ -14,6 +14,7 @@ import { ListToolsRequestSchema, McpError, ServerNotificationSchema, + SetLevelRequestSchema, } from '@modelcontextprotocol/sdk/types.js'; import type { ValidateFunction } from 'ajv'; import { type ActorCallOptions, ApifyApiError } from 'apify-client'; @@ -31,7 +32,7 @@ import type { ActorMcpTool, ActorTool, HelperTool, ToolEntry } from '../types.js import { createProgressTracker } from '../utils/progress.js'; import { getToolPublicFieldOnly } from '../utils/tools.js'; import { connectMCPClient } from './client.js'; -import { EXTERNAL_TOOL_CALL_TIMEOUT_MSEC } from './const.js'; +import { EXTERNAL_TOOL_CALL_TIMEOUT_MSEC, LOG_LEVEL_MAP } from './const.js'; import { processParamsGetTools } from './utils.js'; type ToolsChangedHandler = (toolNames: string[]) => void; @@ -44,6 +45,7 @@ export class ActorsMcpServer { public readonly tools: Map; private toolsChangedHandler: ToolsChangedHandler | undefined; private sigintHandler: (() => Promise) | undefined; + private currentLogLevel = 'info'; constructor(setupSigintHandler = true) { this.server = new Server( @@ -59,8 +61,10 @@ export class ActorsMcpServer { }, }, ); + this.setupLoggingProxy(); this.tools = new Map(); this.setupErrorHandling(setupSigintHandler); + this.setupLoggingHandlers(); this.setupToolHandlers(); this.setupPromptHandlers(); } @@ -264,6 +268,31 @@ export class ActorsMcpServer { } } + private setupLoggingProxy(): void { + // Store original sendLoggingMessage + const originalSendLoggingMessage = this.server.sendLoggingMessage.bind(this.server); + + // Proxy sendLoggingMessage to filter logs + this.server.sendLoggingMessage = async (params: { level: string; data?: unknown; [key: string]: unknown }) => { + const messageLevelValue = LOG_LEVEL_MAP[params.level] ?? -1; // Unknown levels get -1, discard + const currentLevelValue = LOG_LEVEL_MAP[this.currentLogLevel] ?? LOG_LEVEL_MAP.info; // Default to info if invalid + if (messageLevelValue >= currentLevelValue) { + await originalSendLoggingMessage(params as Parameters[0]); + } + }; + } + + private setupLoggingHandlers(): void { + this.server.setRequestHandler(SetLevelRequestSchema, (request) => { + const { level } = request.params; + if (LOG_LEVEL_MAP[level] !== undefined) { + this.currentLogLevel = level; + } + // Sending empty result based on MCP spec + return {}; + }); + } + /** * Sets up MCP request handlers for prompts. */