66use std:: collections:: HashMap ;
77use std:: time:: Instant ;
88
9- use mcpmux_core:: DomainEvent ;
9+ use std:: sync:: Arc ;
10+
11+ use mcpmux_core:: { DomainEvent , LogLevel , LogSource , ServerLog , ServerLogManager } ;
1012use parking_lot:: RwLock ;
11- use rmcp:: model:: { ClientCapabilities , ClientInfo , Implementation } ;
13+ use rmcp:: model:: { ClientCapabilities , ClientInfo , Implementation , LoggingLevel } ;
1214use rmcp:: service:: { NotificationContext , RunningService } ;
1315use rmcp:: RoleClient ;
1416use serde:: { Deserialize , Serialize } ;
@@ -22,19 +24,31 @@ pub use mcpmux_core::TransportType;
2224pub type McpClient = RunningService < RoleClient , McpClientHandler > ;
2325
2426/// Client handler for MCP connections
25- #[ derive( Clone , Debug ) ]
27+ #[ derive( Clone ) ]
2628pub struct McpClientHandler {
2729 info : ClientInfo ,
2830 server_id : String ,
2931 space_id : Uuid ,
3032 event_tx : Option < tokio:: sync:: broadcast:: Sender < DomainEvent > > ,
33+ log_manager : Option < Arc < ServerLogManager > > ,
34+ }
35+
36+ impl std:: fmt:: Debug for McpClientHandler {
37+ fn fmt ( & self , f : & mut std:: fmt:: Formatter < ' _ > ) -> std:: fmt:: Result {
38+ f. debug_struct ( "McpClientHandler" )
39+ . field ( "server_id" , & self . server_id )
40+ . field ( "space_id" , & self . space_id )
41+ . field ( "log_manager" , & self . log_manager . is_some ( ) )
42+ . finish ( )
43+ }
3144}
3245
3346impl McpClientHandler {
3447 pub fn new (
3548 server_id : & str ,
3649 space_id : Uuid ,
3750 event_tx : Option < tokio:: sync:: broadcast:: Sender < DomainEvent > > ,
51+ log_manager : Option < Arc < ServerLogManager > > ,
3852 ) -> Self {
3953 Self {
4054 info : ClientInfo {
@@ -53,6 +67,20 @@ impl McpClientHandler {
5367 server_id : server_id. to_string ( ) ,
5468 space_id,
5569 event_tx,
70+ log_manager,
71+ }
72+ }
73+
74+ /// Convert MCP protocol LoggingLevel to our internal LogLevel
75+ fn convert_logging_level ( level : & LoggingLevel ) -> LogLevel {
76+ match level {
77+ LoggingLevel :: Debug => LogLevel :: Debug ,
78+ LoggingLevel :: Info | LoggingLevel :: Notice => LogLevel :: Info ,
79+ LoggingLevel :: Warning => LogLevel :: Warn ,
80+ LoggingLevel :: Error
81+ | LoggingLevel :: Critical
82+ | LoggingLevel :: Alert
83+ | LoggingLevel :: Emergency => LogLevel :: Error ,
5684 }
5785 }
5886}
@@ -191,6 +219,45 @@ impl rmcp::ClientHandler for McpClientHandler {
191219 }
192220 }
193221 }
222+
223+ fn on_logging_message (
224+ & self ,
225+ params : rmcp:: model:: LoggingMessageNotificationParam ,
226+ _context : NotificationContext < RoleClient > ,
227+ ) -> impl std:: future:: Future < Output = ( ) > + Send + ' _ {
228+ let server_id = self . server_id . clone ( ) ;
229+ let space_id = self . space_id ;
230+ let log_manager = self . log_manager . clone ( ) ;
231+ async move {
232+ // Format the log message from the MCP data field
233+ let message = match & params. data {
234+ serde_json:: Value :: String ( s) => s. clone ( ) ,
235+ other => other. to_string ( ) ,
236+ } ;
237+
238+ let level = Self :: convert_logging_level ( & params. level ) ;
239+
240+ debug ! (
241+ server_id = %server_id,
242+ space_id = %space_id,
243+ level = ?params. level,
244+ logger = ?params. logger,
245+ "[McpClientHandler] Server log: {}" ,
246+ message
247+ ) ;
248+
249+ if let Some ( log_manager) = & log_manager {
250+ let mut log = ServerLog :: new ( level, LogSource :: Server , & message) ;
251+ // Include logger name in metadata if present
252+ if let Some ( logger) = & params. logger {
253+ log = log. with_metadata ( serde_json:: json!( { "logger" : logger } ) ) ;
254+ }
255+ let _ = log_manager
256+ . append ( & space_id. to_string ( ) , & server_id, log)
257+ . await ;
258+ }
259+ }
260+ }
194261}
195262
196263/// Instance key - identifies a server instance for debugging/logging.
0 commit comments