@@ -10,6 +10,8 @@ import {
1010 SubscribeRequestSchema ,
1111 UnsubscribeRequestSchema ,
1212 CompleteRequestSchema ,
13+ SetLevelRequestSchema ,
14+ CancelledNotificationSchema ,
1315} from '@modelcontextprotocol/sdk/types.js' ;
1416import { ToolProtocol } from '../tools/BaseTool.js' ;
1517import { PromptProtocol } from '../prompts/BasePrompt.js' ;
@@ -31,6 +33,14 @@ import { AuthConfig } from '../auth/types.js';
3133import { createRequire } from 'module' ;
3234
3335const require = createRequire ( import . meta. url ) ;
36+
37+ export type MCPLogLevel = 'debug' | 'info' | 'notice' | 'warning' | 'error' | 'critical' | 'alert' | 'emergency' ;
38+
39+ export const LOG_LEVEL_SEVERITY : Record < MCPLogLevel , number > = {
40+ debug : 0 , info : 1 , notice : 2 , warning : 3 ,
41+ error : 4 , critical : 5 , alert : 6 , emergency : 7 ,
42+ } ;
43+
3444export type TransportType = 'stdio' | 'sse' | 'http-stream' ;
3545
3646export interface TransportConfig {
@@ -44,6 +54,7 @@ export interface MCPServerConfig {
4454 version ?: string ;
4555 basePath ?: string ;
4656 transport ?: TransportConfig ;
57+ logging ?: boolean ;
4758}
4859
4960export type ServerCapabilities = {
@@ -58,6 +69,7 @@ export type ServerCapabilities = {
5869 subscribe ?: true ;
5970 } ;
6071 completions ?: { } ;
72+ logging ?: { } ;
6173} ;
6274
6375export class MCPServer {
@@ -77,12 +89,16 @@ export class MCPServer {
7789 private transport ?: BaseTransport ;
7890 private shutdownPromise ?: Promise < void > ;
7991 private shutdownResolve ?: ( ) => void ;
92+ private _logLevel : MCPLogLevel = 'warning' ;
93+ private _loggingEnabled : boolean = false ;
94+ private _inFlightAbortControllers = new Map < string | number , AbortController > ( ) ;
8095
8196 constructor ( config : MCPServerConfig = { } ) {
8297 this . basePath = this . resolveBasePath ( config . basePath ) ;
8398 this . serverName = config . name ?? this . getDefaultName ( ) ;
8499 this . serverVersion = config . version ?? this . getDefaultVersion ( ) ;
85100 this . transportConfig = config . transport ?? { type : 'stdio' } ;
101+ this . _loggingEnabled = config . logging ?? false ;
86102
87103 if ( this . transportConfig . auth && this . transportConfig . options ) {
88104 ( this . transportConfig . options as any ) . auth = this . transportConfig . auth ;
@@ -253,7 +269,7 @@ export class MCPServer {
253269 return response ;
254270 } ) ;
255271
256- targetServer . setRequestHandler ( CallToolRequestSchema , async ( request : any ) => {
272+ targetServer . setRequestHandler ( CallToolRequestSchema , async ( request : any , extra : any ) => {
257273 logger . debug ( `Tool call request received for: ${ request . params . name } ` ) ;
258274 logger . debug ( `Tool call arguments: ${ JSON . stringify ( request . params . arguments ) } ` ) ;
259275
@@ -272,9 +288,20 @@ export class MCPServer {
272288 method : 'tools/call' as const ,
273289 } ;
274290
275- const result = await tool . toolCall ( toolRequest ) ;
276- logger . debug ( `Tool execution successful: ${ JSON . stringify ( result ) } ` ) ;
277- return result ;
291+ // Set progress token and abort signal from the SDK extra context
292+ const progressToken = request . params ?. _meta ?. progressToken ;
293+ const abortSignal = extra ?. signal as AbortSignal | undefined ;
294+ tool . setProgressToken ( progressToken ) ;
295+ tool . setAbortSignal ( abortSignal ) ;
296+
297+ try {
298+ const result = await tool . toolCall ( toolRequest ) ;
299+ logger . debug ( `Tool execution successful: ${ JSON . stringify ( result ) } ` ) ;
300+ return result ;
301+ } finally {
302+ tool . setProgressToken ( undefined ) ;
303+ tool . setAbortSignal ( undefined ) ;
304+ }
278305 } catch ( error ) {
279306 const errorMsg = `Tool execution failed: ${ error } ` ;
280307 logger . error ( errorMsg ) ;
@@ -399,6 +426,30 @@ export class MCPServer {
399426 return { completion : { values : [ ] } } ;
400427 } ) ;
401428 }
429+
430+ if ( this . capabilities . logging ) {
431+ targetServer . setRequestHandler ( SetLevelRequestSchema , async ( request : any ) => {
432+ const level = request . params . level as MCPLogLevel ;
433+ if ( ! LOG_LEVEL_SEVERITY . hasOwnProperty ( level ) ) {
434+ throw new Error ( `Invalid log level: ${ level } ` ) ;
435+ }
436+ this . _logLevel = level ;
437+ logger . info ( `MCP log level set to: ${ level } ` ) ;
438+ return { } ;
439+ } ) ;
440+ }
441+
442+ targetServer . setNotificationHandler ( CancelledNotificationSchema , async ( notification : any ) => {
443+ const requestId = notification . params . requestId ;
444+ if ( requestId != null ) {
445+ const controller = this . _inFlightAbortControllers . get ( requestId ) ;
446+ if ( controller ) {
447+ controller . abort ( notification . params . reason ?? 'Request cancelled' ) ;
448+ this . _inFlightAbortControllers . delete ( requestId ) ;
449+ logger . info ( `Request ${ requestId } cancelled: ${ notification . params . reason ?? 'no reason' } ` ) ;
450+ }
451+ }
452+ } ) ;
402453 }
403454
404455 private async detectCapabilities ( ) : Promise < ServerCapabilities > {
@@ -417,6 +468,11 @@ export class MCPServer {
417468 logger . debug ( 'Resources capability enabled' ) ;
418469 }
419470
471+ if ( this . _loggingEnabled ) {
472+ this . capabilities . logging = { } ;
473+ logger . debug ( 'Logging capability enabled' ) ;
474+ }
475+
420476 if ( this . capabilities . prompts || this . capabilities . resources ) {
421477 this . capabilities . completions = { } ;
422478 logger . debug ( 'Completions capability enabled' ) ;
@@ -608,4 +664,38 @@ export class MCPServer {
608664 get IsRunning ( ) : boolean {
609665 return this . isRunning ;
610666 }
667+
668+ /**
669+ * Query the client for its filesystem root boundaries.
670+ * Returns an empty array if the client doesn't support roots.
671+ */
672+ public async listRoots ( ) : Promise < Array < { uri : string ; name ?: string } > > {
673+ if ( ! this . server ) return [ ] ;
674+ try {
675+ const result = await this . server . listRoots ( ) ;
676+ return result . roots ?? [ ] ;
677+ } catch {
678+ return [ ] ;
679+ }
680+ }
681+
682+ /**
683+ * Send a logging message to the client via the MCP logging protocol.
684+ * Messages below the current log level threshold will be silently dropped.
685+ */
686+ public async sendLog ( level : MCPLogLevel , loggerName : string , data : unknown ) : Promise < void > {
687+ if ( ! this . _loggingEnabled || ! this . server ) return ;
688+ if ( LOG_LEVEL_SEVERITY [ level ] < LOG_LEVEL_SEVERITY [ this . _logLevel ] ) return ;
689+
690+ try {
691+ await this . server . sendLoggingMessage ( {
692+ level,
693+ logger : loggerName ,
694+ data,
695+ } ) ;
696+ } catch ( error ) {
697+ // Don't throw on logging failures
698+ logger . debug ( `Failed to send log message: ${ error } ` ) ;
699+ }
700+ }
611701}
0 commit comments