2929LOG_DIR .mkdir (parents = True , exist_ok = True )
3030LOG_FILE = LOG_DIR / "mcp_plugin.log"
3131
32- # Configure root logger
33- logging .basicConfig (
34- level = logging .INFO ,
35- format = "%(asctime)s - %(name)s - %(levelname)s - %(message)s" ,
36- handlers = [
37- logging .FileHandler (LOG_FILE ),
38- logging .StreamHandler ()
39- ]
40- )
32+ # Configure file logger with detailed formatting
33+ file_handler = logging .FileHandler (LOG_FILE )
34+ file_handler .setFormatter (logging .Formatter (
35+ '%(asctime)s - %(name)s - %(levelname)s - %(message)s'
36+ ))
37+
38+ # Configure console logger with simpler formatting
39+ console_handler = logging .StreamHandler ()
40+ console_handler .setFormatter (logging .Formatter (
41+ '%(asctime)s - %(levelname)s - %(message)s'
42+ ))
4143
44+ # Set up the logger
4245logger = logging .getLogger ("optillm.mcp_plugin" )
46+ logger .setLevel (logging .DEBUG ) # Set to DEBUG for maximum detail
47+ logger .addHandler (file_handler )
48+ logger .addHandler (console_handler )
4349
4450# Plugin identifier
4551SLUG = "mcp"
4652
53+ # Add custom logging for MCP communication
54+ def log_mcp_message (direction : str , method : str , params : Any = None , result : Any = None , error : Any = None ):
55+ """Log MCP communication in detail"""
56+ message_parts = [f"MCP { direction } - Method: { method } " ]
57+
58+ if params :
59+ try :
60+ params_str = json .dumps (params , indent = 2 )
61+ message_parts .append (f"Params: { params_str } " )
62+ except :
63+ message_parts .append (f"Params: { params } " )
64+
65+ if result :
66+ try :
67+ result_str = json .dumps (result , indent = 2 )
68+ message_parts .append (f"Result: { result_str } " )
69+ except :
70+ message_parts .append (f"Result: { result } " )
71+
72+ if error :
73+ message_parts .append (f"Error: { error } " )
74+
75+ logger .debug ("\n " .join (message_parts ))
76+
4777def find_executable (cmd : str ) -> Optional [str ]:
4878 """
4979 Find the full path to an executable command.
@@ -123,7 +153,9 @@ def load_config(self) -> bool:
123153 return False
124154
125155 with open (self .config_path , 'r' ) as f :
126- config = json .load (f )
156+ config_data = f .read ()
157+ logger .debug (f"Raw config data: { config_data } " )
158+ config = json .loads (config_data )
127159
128160 # Set log level
129161 self .log_level = config .get ("log_level" , "INFO" )
@@ -134,6 +166,7 @@ def load_config(self) -> bool:
134166 servers_config = config .get ("mcpServers" , {})
135167 for server_name , server_config in servers_config .items ():
136168 self .servers [server_name ] = ServerConfig .from_dict (server_config )
169+ logger .debug (f"Loaded server config for { server_name } : { server_config } " )
137170
138171 logger .info (f"Loaded configuration with { len (self .servers )} servers" )
139172 return True
@@ -165,6 +198,36 @@ def create_default_config(self) -> bool:
165198 logger .error (f"Error creating default configuration: { e } " )
166199 return False
167200
201+ # Create a custom ClientSession that logs all communication
202+ class LoggingClientSession (ClientSession ):
203+ """A ClientSession that logs all communication"""
204+
205+ async def send_request (self , * args , ** kwargs ):
206+ """Log and forward requests"""
207+ method = args [0 ]
208+ params = args [1 ] if len (args ) > 1 else None
209+ log_mcp_message ("REQUEST" , method , params )
210+
211+ try :
212+ result = await super ().send_request (* args , ** kwargs )
213+ log_mcp_message ("RESPONSE" , method , result = result )
214+ return result
215+ except Exception as e :
216+ log_mcp_message ("ERROR" , method , error = str (e ))
217+ raise
218+
219+ async def send_notification (self , * args , ** kwargs ):
220+ """Log and forward notifications"""
221+ method = args [0 ]
222+ params = args [1 ] if len (args ) > 1 else None
223+ log_mcp_message ("NOTIFICATION" , method , params )
224+
225+ try :
226+ await super ().send_notification (* args , ** kwargs )
227+ except Exception as e :
228+ log_mcp_message ("ERROR" , method , error = str (e ))
229+ raise
230+
168231class MCPServer :
169232 """Represents a connection to an MCP server"""
170233
@@ -182,6 +245,7 @@ def __init__(self, server_name: str, config: ServerConfig):
182245 async def connect_and_discover (self ) -> bool :
183246 """Connect to the server and discover capabilities using proper context management"""
184247 logger .info (f"Connecting to MCP server: { self .server_name } " )
248+ logger .debug (f"Server configuration: { vars (self .config )} " )
185249
186250 # Find the full path to the command
187251 full_command = find_executable (self .config .command )
@@ -194,6 +258,10 @@ async def connect_and_discover(self) -> bool:
194258 if self .config .env :
195259 merged_env .update (self .config .env )
196260
261+ logger .debug (f"Using command: { full_command } " )
262+ logger .debug (f"Arguments: { self .config .args } " )
263+ logger .debug (f"Environment: { self .config .env } " )
264+
197265 # Create server parameters
198266 server_params = StdioServerParameters (
199267 command = full_command ,
@@ -207,7 +275,8 @@ async def connect_and_discover(self) -> bool:
207275 full_command ,
208276 * self .config .args ,
209277 env = merged_env ,
210- stderr = asyncio .subprocess .PIPE
278+ stderr = asyncio .subprocess .PIPE ,
279+ stdout = asyncio .subprocess .PIPE
211280 )
212281
213282 # Log startup message from stderr
@@ -216,22 +285,39 @@ async def log_stderr():
216285 line = await process .stderr .readline ()
217286 if not line :
218287 break
219- logger .info (f"Server { self .server_name } stderr: { line .decode ().strip ()} " )
288+ stderr_text = line .decode ().strip ()
289+ logger .info (f"Server { self .server_name } stderr: { stderr_text } " )
290+
291+ # Log stdout too for debugging
292+ async def log_stdout ():
293+ while True :
294+ line = await process .stdout .readline ()
295+ if not line :
296+ break
297+ stdout_text = line .decode ().strip ()
298+ logger .debug (f"Server { self .server_name } stdout: { stdout_text } " )
220299
221- # Start stderr logging task
300+ # Start logging tasks
222301 asyncio .create_task (log_stderr ())
302+ asyncio .create_task (log_stdout ())
223303
224304 # Wait a bit for the server to start up
305+ logger .debug (f"Waiting for server to start up..." )
225306 await asyncio .sleep (2 )
226307
227308 # Use the MCP client with proper context management
309+ logger .debug (f"Establishing MCP client connection to { self .server_name } " )
228310 async with stdio_client (server_params ) as (read_stream , write_stream ):
229- async with ClientSession (read_stream , write_stream ) as session :
311+ logger .debug (f"Connection established, creating session" )
312+ # Use our logging session instead of the regular one
313+ async with LoggingClientSession (read_stream , write_stream ) as session :
230314 logger .info (f"Connected to server: { self .server_name } " )
231315
232316 # Initialize session
317+ logger .debug (f"Initializing MCP session for { self .server_name } " )
233318 result = await session .initialize ()
234319 logger .info (f"Server { self .server_name } initialized with capabilities: { result .capabilities } " )
320+ logger .debug (f"Full initialization result: { result } " )
235321
236322 # Check which capabilities the server supports
237323 server_capabilities = result .capabilities
@@ -244,6 +330,7 @@ async def log_stderr():
244330 tools_result = await session .list_tools ()
245331 self .tools = tools_result .tools
246332 logger .info (f"Found { len (self .tools )} tools" )
333+ logger .debug (f"Tools details: { [t .name for t in self .tools ]} " )
247334 except McpError as e :
248335 logger .warning (f"Failed to list tools: { e } " )
249336
@@ -255,6 +342,7 @@ async def log_stderr():
255342 resources_result = await session .list_resources ()
256343 self .resources = resources_result .resources
257344 logger .info (f"Found { len (self .resources )} resources" )
345+ logger .debug (f"Resources details: { [r .uri for r in self .resources ]} " )
258346 except McpError as e :
259347 logger .warning (f"Failed to list resources: { e } " )
260348
@@ -266,6 +354,7 @@ async def log_stderr():
266354 prompts_result = await session .list_prompts ()
267355 self .prompts = prompts_result .prompts
268356 logger .info (f"Found { len (self .prompts )} prompts" )
357+ logger .debug (f"Prompts details: { [p .name for p in self .prompts ]} " )
269358 except McpError as e :
270359 logger .warning (f"Failed to list prompts: { e } " )
271360
@@ -304,38 +393,45 @@ async def initialize(self) -> bool:
304393 self .servers [server_name ] = MCPServer (server_name , server_config )
305394
306395 # Connect to all servers and discover capabilities
396+ connected_servers = 0
307397 for server_name , server in self .servers .items ():
308398 success = await server .connect_and_discover ()
309399 if success :
400+ connected_servers += 1
310401 # Cache server capabilities
311402 for tool in server .tools :
312- self . all_tools . append ( {
403+ tool_info = {
313404 "server" : server_name ,
314405 "name" : tool .name ,
315406 "description" : tool .description ,
316407 "input_schema" : tool .inputSchema
317- })
408+ }
409+ self .all_tools .append (tool_info )
410+ logger .debug (f"Cached tool: { tool_info } " )
318411
319412 for resource in server .resources :
320- self . all_resources . append ( {
413+ resource_info = {
321414 "server" : server_name ,
322415 "uri" : resource .uri ,
323416 "name" : resource .name ,
324417 "description" : resource .description
325- })
418+ }
419+ self .all_resources .append (resource_info )
420+ logger .debug (f"Cached resource: { resource_info } " )
326421
327422 for prompt in server .prompts :
328- self . all_prompts . append ( {
423+ prompt_info = {
329424 "server" : server_name ,
330425 "name" : prompt .name ,
331426 "description" : prompt .description ,
332427 "arguments" : prompt .arguments
333- })
428+ }
429+ self .all_prompts .append (prompt_info )
430+ logger .debug (f"Cached prompt: { prompt_info } " )
334431
335432 self .initialized = True
336433
337434 # Check if we successfully connected to any servers
338- connected_servers = sum (1 for server in self .servers .values () if server .connected )
339435 logger .info (f"Connected to { connected_servers } /{ len (self .servers )} MCP servers" )
340436 return connected_servers > 0
341437
@@ -357,6 +453,7 @@ def get_tools_for_model(self) -> List[Dict[str, Any]]:
357453 }
358454 }
359455 tools .append (tool_entry )
456+ logger .debug (f"Added tool for model: { tool_entry } " )
360457
361458 return tools
362459
@@ -435,9 +532,21 @@ async def execute_tool(server_name: str, tool_name: str, arguments: Dict[str, An
435532 )
436533
437534 try :
535+ # Log the tool call in detail
536+ logger .debug (f"Tool call details:" )
537+ logger .debug (f" Server: { server_name } " )
538+ logger .debug (f" Tool: { tool_name } " )
539+ logger .debug (f" Arguments: { json .dumps (arguments , indent = 2 )} " )
540+ logger .debug (f" Command: { full_command } " )
541+ logger .debug (f" Args: { server_config .args } " )
542+
438543 # Use the MCP client with proper context management
439544 async with stdio_client (server_params ) as (read_stream , write_stream ):
440- async with ClientSession (read_stream , write_stream ) as session :
545+ # Use our logging session
546+ async with LoggingClientSession (read_stream , write_stream ) as session :
547+ # Initialize the session
548+ await session .initialize ()
549+
441550 # Call the tool and get the result
442551 logger .info (f"Calling tool { tool_name } with arguments: { arguments } " )
443552 result = await session .call_tool (tool_name , arguments )
@@ -450,12 +559,14 @@ async def execute_tool(server_name: str, tool_name: str, arguments: Dict[str, An
450559 "type" : "text" ,
451560 "text" : content .text
452561 })
562+ logger .debug (f"Tool result (text): { content .text [:100 ]} ..." )
453563 elif content .type == "image" :
454564 content_results .append ({
455565 "type" : "image" ,
456566 "data" : content .data ,
457567 "mimeType" : content .mimeType
458568 })
569+ logger .debug (f"Tool result (image): { content .mimeType } " )
459570
460571 return {
461572 "result" : content_results ,
@@ -481,6 +592,8 @@ async def run(system_prompt: str, initial_query: str, client, model: str) -> Tup
481592 Tuple of (response text, token usage)
482593 """
483594 logger .info (f"MCP Plugin run called with model: { model } " )
595+ logger .debug (f"System prompt: { system_prompt [:100 ]} ..." )
596+ logger .debug (f"Initial query: { initial_query } " )
484597
485598 try :
486599 # Load configuration
@@ -534,6 +647,7 @@ async def run(system_prompt: str, initial_query: str, client, model: str) -> Tup
534647
535648 # Get capabilities description
536649 capabilities_description = server_manager .get_capabilities_description ()
650+ logger .debug (f"Capabilities description: { capabilities_description } " )
537651
538652 # Enhance system prompt with MCP capabilities
539653 enhanced_system_prompt = f"{ system_prompt } \n \n You have access to the following MCP capabilities:\n \n { capabilities_description } "
@@ -553,10 +667,13 @@ async def run(system_prompt: str, initial_query: str, client, model: str) -> Tup
553667 # Check if the model wants to use any tools
554668 response_message = response .choices [0 ].message
555669 response_content = response_message .content or ""
670+ logger .debug (f"Initial model response: { response_content [:100 ]} ..." )
556671
557672 # Check for tool calls
558673 if hasattr (response_message , "tool_calls" ) and response_message .tool_calls :
559674 logger .info (f"Model requested tool calls: { len (response_message .tool_calls )} " )
675+ for i , tc in enumerate (response_message .tool_calls ):
676+ logger .debug (f"Tool call { i + 1 } : { tc .function .name } with args: { tc .function .arguments } " )
560677
561678 # Create new messages with the original system and user message
562679 messages = [
@@ -587,6 +704,7 @@ async def run(system_prompt: str, initial_query: str, client, model: str) -> Tup
587704 "tool_call_id" : tool_call_id ,
588705 "content" : json .dumps (result )
589706 })
707+ logger .debug (f"Added tool result for { full_tool_name } : { json .dumps (result )[:100 ]} ..." )
590708 except Exception as e :
591709 logger .error (f"Error processing tool call { full_tool_name } : { e } " )
592710 messages .append ({
@@ -614,10 +732,12 @@ async def run(system_prompt: str, initial_query: str, client, model: str) -> Tup
614732 final_message = final_response .choices [0 ].message
615733 response_text = final_message .content or ""
616734 token_usage = final_response .usage .completion_tokens
735+ logger .debug (f"Final model response: { response_text [:100 ]} ..." )
617736 else :
618737 # Model didn't call any tools, use its initial response
619738 response_text = response_content
620739 token_usage = response .usage .completion_tokens
740+ logger .info ("Model did not request any tool calls" )
621741
622742 return response_text , token_usage
623743
0 commit comments