claude-mpm 3.9.8__py3-none-any.whl → 3.9.9__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- claude_mpm/VERSION +1 -1
- claude_mpm/agents/base_agent.json +1 -1
- claude_mpm/cli/__init__.py +3 -1
- claude_mpm/cli/commands/__init__.py +3 -1
- claude_mpm/cli/commands/cleanup.py +21 -1
- claude_mpm/cli/commands/mcp.py +821 -0
- claude_mpm/cli/parser.py +148 -1
- claude_mpm/config/memory_guardian_config.py +325 -0
- claude_mpm/constants.py +13 -0
- claude_mpm/hooks/claude_hooks/hook_handler.py +76 -19
- claude_mpm/models/state_models.py +433 -0
- claude_mpm/services/communication/__init__.py +2 -2
- claude_mpm/services/communication/socketio.py +18 -16
- claude_mpm/services/infrastructure/__init__.py +4 -1
- claude_mpm/services/infrastructure/logging.py +3 -3
- claude_mpm/services/infrastructure/memory_guardian.py +770 -0
- claude_mpm/services/mcp_gateway/__init__.py +28 -12
- claude_mpm/services/mcp_gateway/main.py +326 -0
- claude_mpm/services/mcp_gateway/registry/__init__.py +6 -3
- claude_mpm/services/mcp_gateway/registry/service_registry.py +397 -0
- claude_mpm/services/mcp_gateway/registry/tool_registry.py +477 -0
- claude_mpm/services/mcp_gateway/server/__init__.py +9 -3
- claude_mpm/services/mcp_gateway/server/mcp_server.py +430 -0
- claude_mpm/services/mcp_gateway/server/mcp_server_simple.py +444 -0
- claude_mpm/services/mcp_gateway/server/stdio_handler.py +373 -0
- claude_mpm/services/mcp_gateway/tools/__init__.py +16 -3
- claude_mpm/services/mcp_gateway/tools/base_adapter.py +497 -0
- claude_mpm/services/mcp_gateway/tools/document_summarizer.py +729 -0
- claude_mpm/services/mcp_gateway/tools/hello_world.py +551 -0
- claude_mpm/utils/file_utils.py +293 -0
- claude_mpm/utils/platform_memory.py +524 -0
- claude_mpm/utils/subprocess_utils.py +305 -0
- {claude_mpm-3.9.8.dist-info → claude_mpm-3.9.9.dist-info}/METADATA +3 -1
- {claude_mpm-3.9.8.dist-info → claude_mpm-3.9.9.dist-info}/RECORD +39 -28
- claude_mpm/agents/templates/.claude-mpm/memories/README.md +0 -36
- claude_mpm/agents/templates/.claude-mpm/memories/engineer_agent.md +0 -39
- claude_mpm/agents/templates/.claude-mpm/memories/qa_agent.md +0 -38
- claude_mpm/agents/templates/.claude-mpm/memories/research_agent.md +0 -39
- claude_mpm/agents/templates/.claude-mpm/memories/version_control_agent.md +0 -38
- /claude_mpm/agents/templates/{research_memory_efficient.json → backup/research_memory_efficient.json} +0 -0
- {claude_mpm-3.9.8.dist-info → claude_mpm-3.9.9.dist-info}/WHEEL +0 -0
- {claude_mpm-3.9.8.dist-info → claude_mpm-3.9.9.dist-info}/entry_points.txt +0 -0
- {claude_mpm-3.9.8.dist-info → claude_mpm-3.9.9.dist-info}/licenses/LICENSE +0 -0
- {claude_mpm-3.9.8.dist-info → claude_mpm-3.9.9.dist-info}/top_level.txt +0 -0
| @@ -0,0 +1,373 @@ | |
| 1 | 
            +
            """
         | 
| 2 | 
            +
            STDIO Communication Handler for MCP
         | 
| 3 | 
            +
            ====================================
         | 
| 4 | 
            +
             | 
| 5 | 
            +
            Handles stdio-based communication for the MCP server.
         | 
| 6 | 
            +
            Manages JSON-RPC message exchange over stdin/stdout.
         | 
| 7 | 
            +
             | 
| 8 | 
            +
            Part of ISS-0035: MCP Server Implementation - Core Server and Tool Registry
         | 
| 9 | 
            +
            """
         | 
| 10 | 
            +
             | 
| 11 | 
            +
            import asyncio
         | 
| 12 | 
            +
            import json
         | 
| 13 | 
            +
            import sys
         | 
| 14 | 
            +
            from typing import Dict, Any, Optional, Callable
         | 
| 15 | 
            +
            from asyncio import StreamReader, StreamWriter
         | 
| 16 | 
            +
            import traceback
         | 
| 17 | 
            +
             | 
| 18 | 
            +
            from claude_mpm.services.mcp_gateway.core.interfaces import IMCPCommunication
         | 
| 19 | 
            +
            from claude_mpm.services.mcp_gateway.core.base import BaseMCPService
         | 
| 20 | 
            +
             | 
| 21 | 
            +
             | 
| 22 | 
            +
            class StdioHandler(BaseMCPService, IMCPCommunication):
         | 
| 23 | 
            +
                """
         | 
| 24 | 
            +
                STDIO-based communication handler for MCP.
         | 
| 25 | 
            +
                
         | 
| 26 | 
            +
                WHY: The MCP protocol uses stdio (stdin/stdout) for communication between
         | 
| 27 | 
            +
                Claude Desktop and MCP servers. This handler manages the low-level
         | 
| 28 | 
            +
                message exchange, ensuring proper JSON-RPC formatting and error handling.
         | 
| 29 | 
            +
                
         | 
| 30 | 
            +
                DESIGN DECISIONS:
         | 
| 31 | 
            +
                - Use asyncio streams for non-blocking I/O
         | 
| 32 | 
            +
                - Implement message framing with Content-Length headers (LSP-style)
         | 
| 33 | 
            +
                - Handle both notification and request/response patterns
         | 
| 34 | 
            +
                - Provide robust error recovery and logging
         | 
| 35 | 
            +
                """
         | 
| 36 | 
            +
                
         | 
| 37 | 
            +
                def __init__(self):
         | 
| 38 | 
            +
                    """Initialize the STDIO handler."""
         | 
| 39 | 
            +
                    super().__init__("StdioHandler")
         | 
| 40 | 
            +
                    
         | 
| 41 | 
            +
                    # Async streams
         | 
| 42 | 
            +
                    self._reader: Optional[StreamReader] = None
         | 
| 43 | 
            +
                    self._writer: Optional[StreamWriter] = None
         | 
| 44 | 
            +
                    
         | 
| 45 | 
            +
                    # Connection state
         | 
| 46 | 
            +
                    self._connected = False
         | 
| 47 | 
            +
                    
         | 
| 48 | 
            +
                    # Message buffer for partial reads
         | 
| 49 | 
            +
                    self._buffer = b""
         | 
| 50 | 
            +
                    
         | 
| 51 | 
            +
                    # Metrics
         | 
| 52 | 
            +
                    self._metrics = {
         | 
| 53 | 
            +
                        "messages_sent": 0,
         | 
| 54 | 
            +
                        "messages_received": 0,
         | 
| 55 | 
            +
                        "errors": 0,
         | 
| 56 | 
            +
                        "bytes_sent": 0,
         | 
| 57 | 
            +
                        "bytes_received": 0
         | 
| 58 | 
            +
                    }
         | 
| 59 | 
            +
                
         | 
| 60 | 
            +
                async def _do_initialize(self) -> bool:
         | 
| 61 | 
            +
                    """
         | 
| 62 | 
            +
                    Initialize the STDIO handler.
         | 
| 63 | 
            +
                    
         | 
| 64 | 
            +
                    Returns:
         | 
| 65 | 
            +
                        True if initialization successful
         | 
| 66 | 
            +
                    """
         | 
| 67 | 
            +
                    try:
         | 
| 68 | 
            +
                        self.log_info("Initializing STDIO handler")
         | 
| 69 | 
            +
                        
         | 
| 70 | 
            +
                        # Create async streams for stdin/stdout
         | 
| 71 | 
            +
                        loop = asyncio.get_event_loop()
         | 
| 72 | 
            +
                        
         | 
| 73 | 
            +
                        # For stdin
         | 
| 74 | 
            +
                        self._reader = asyncio.StreamReader()
         | 
| 75 | 
            +
                        stdin_protocol = asyncio.StreamReaderProtocol(self._reader)
         | 
| 76 | 
            +
                        await loop.connect_read_pipe(lambda: stdin_protocol, sys.stdin)
         | 
| 77 | 
            +
                        
         | 
| 78 | 
            +
                        # For stdout (we'll write directly to sys.stdout)
         | 
| 79 | 
            +
                        # Note: stdout doesn't need async handling for writes
         | 
| 80 | 
            +
                        
         | 
| 81 | 
            +
                        self._connected = True
         | 
| 82 | 
            +
                        self.log_info("STDIO handler initialized")
         | 
| 83 | 
            +
                        return True
         | 
| 84 | 
            +
                        
         | 
| 85 | 
            +
                    except Exception as e:
         | 
| 86 | 
            +
                        self.log_error(f"Failed to initialize STDIO handler: {e}")
         | 
| 87 | 
            +
                        return False
         | 
| 88 | 
            +
                
         | 
| 89 | 
            +
                async def _do_shutdown(self) -> None:
         | 
| 90 | 
            +
                    """Shutdown the STDIO handler."""
         | 
| 91 | 
            +
                    self.log_info("Shutting down STDIO handler")
         | 
| 92 | 
            +
                    
         | 
| 93 | 
            +
                    self._connected = False
         | 
| 94 | 
            +
                    
         | 
| 95 | 
            +
                    # Close streams if needed
         | 
| 96 | 
            +
                    if self._reader:
         | 
| 97 | 
            +
                        self._reader = None
         | 
| 98 | 
            +
                    
         | 
| 99 | 
            +
                    self.log_info("STDIO handler shutdown complete")
         | 
| 100 | 
            +
                
         | 
| 101 | 
            +
                async def send_message(self, message: Dict[str, Any]) -> None:
         | 
| 102 | 
            +
                    """
         | 
| 103 | 
            +
                    Send a message to the MCP client via stdout.
         | 
| 104 | 
            +
                    
         | 
| 105 | 
            +
                    Uses Content-Length header for message framing (LSP-style).
         | 
| 106 | 
            +
                    
         | 
| 107 | 
            +
                    Args:
         | 
| 108 | 
            +
                        message: Message to send
         | 
| 109 | 
            +
                    """
         | 
| 110 | 
            +
                    try:
         | 
| 111 | 
            +
                        if not self._connected:
         | 
| 112 | 
            +
                            raise RuntimeError("STDIO handler not connected")
         | 
| 113 | 
            +
                        
         | 
| 114 | 
            +
                        # Convert message to JSON
         | 
| 115 | 
            +
                        json_str = json.dumps(message, separators=(',', ':'))
         | 
| 116 | 
            +
                        json_bytes = json_str.encode('utf-8')
         | 
| 117 | 
            +
                        
         | 
| 118 | 
            +
                        # Create Content-Length header
         | 
| 119 | 
            +
                        content_length = len(json_bytes)
         | 
| 120 | 
            +
                        header = f"Content-Length: {content_length}\r\n\r\n"
         | 
| 121 | 
            +
                        header_bytes = header.encode('ascii')
         | 
| 122 | 
            +
                        
         | 
| 123 | 
            +
                        # Write header and content to stdout
         | 
| 124 | 
            +
                        sys.stdout.buffer.write(header_bytes)
         | 
| 125 | 
            +
                        sys.stdout.buffer.write(json_bytes)
         | 
| 126 | 
            +
                        sys.stdout.buffer.flush()
         | 
| 127 | 
            +
                        
         | 
| 128 | 
            +
                        # Update metrics
         | 
| 129 | 
            +
                        self._metrics["messages_sent"] += 1
         | 
| 130 | 
            +
                        self._metrics["bytes_sent"] += len(header_bytes) + len(json_bytes)
         | 
| 131 | 
            +
                        
         | 
| 132 | 
            +
                        self.log_debug(f"Sent message: {message.get('method', message.get('id', 'unknown'))}")
         | 
| 133 | 
            +
                        
         | 
| 134 | 
            +
                    except Exception as e:
         | 
| 135 | 
            +
                        self.log_error(f"Error sending message: {e}")
         | 
| 136 | 
            +
                        self._metrics["errors"] += 1
         | 
| 137 | 
            +
                        raise
         | 
| 138 | 
            +
                
         | 
| 139 | 
            +
                async def receive_message(self) -> Optional[Dict[str, Any]]:
         | 
| 140 | 
            +
                    """
         | 
| 141 | 
            +
                    Receive a message from the MCP client via stdin.
         | 
| 142 | 
            +
                    
         | 
| 143 | 
            +
                    Handles Content-Length based message framing.
         | 
| 144 | 
            +
                    
         | 
| 145 | 
            +
                    Returns:
         | 
| 146 | 
            +
                        Received message or None if no message available
         | 
| 147 | 
            +
                    """
         | 
| 148 | 
            +
                    try:
         | 
| 149 | 
            +
                        if not self._connected or not self._reader:
         | 
| 150 | 
            +
                            return None
         | 
| 151 | 
            +
                        
         | 
| 152 | 
            +
                        # Read header to get content length
         | 
| 153 | 
            +
                        headers = {}
         | 
| 154 | 
            +
                        while True:
         | 
| 155 | 
            +
                            line_bytes = await self._reader.readline()
         | 
| 156 | 
            +
                            if not line_bytes:
         | 
| 157 | 
            +
                                # EOF reached
         | 
| 158 | 
            +
                                self._connected = False
         | 
| 159 | 
            +
                                return None
         | 
| 160 | 
            +
                            
         | 
| 161 | 
            +
                            line = line_bytes.decode('utf-8').rstrip('\r\n')
         | 
| 162 | 
            +
                            
         | 
| 163 | 
            +
                            if not line:
         | 
| 164 | 
            +
                                # Empty line indicates end of headers
         | 
| 165 | 
            +
                                break
         | 
| 166 | 
            +
                            
         | 
| 167 | 
            +
                            # Parse header
         | 
| 168 | 
            +
                            if ':' in line:
         | 
| 169 | 
            +
                                key, value = line.split(':', 1)
         | 
| 170 | 
            +
                                headers[key.strip()] = value.strip()
         | 
| 171 | 
            +
                        
         | 
| 172 | 
            +
                        # Get content length
         | 
| 173 | 
            +
                        content_length = headers.get('Content-Length')
         | 
| 174 | 
            +
                        if not content_length:
         | 
| 175 | 
            +
                            self.log_warning("No Content-Length header found")
         | 
| 176 | 
            +
                            return None
         | 
| 177 | 
            +
                        
         | 
| 178 | 
            +
                        content_length = int(content_length)
         | 
| 179 | 
            +
                        
         | 
| 180 | 
            +
                        # Read content
         | 
| 181 | 
            +
                        content_bytes = await self._reader.readexactly(content_length)
         | 
| 182 | 
            +
                        
         | 
| 183 | 
            +
                        # Parse JSON
         | 
| 184 | 
            +
                        message = json.loads(content_bytes.decode('utf-8'))
         | 
| 185 | 
            +
                        
         | 
| 186 | 
            +
                        # Update metrics
         | 
| 187 | 
            +
                        self._metrics["messages_received"] += 1
         | 
| 188 | 
            +
                        self._metrics["bytes_received"] += len(line_bytes) + content_length
         | 
| 189 | 
            +
                        
         | 
| 190 | 
            +
                        self.log_debug(f"Received message: {message.get('method', message.get('id', 'unknown'))}")
         | 
| 191 | 
            +
                        
         | 
| 192 | 
            +
                        return message
         | 
| 193 | 
            +
                        
         | 
| 194 | 
            +
                    except asyncio.IncompleteReadError:
         | 
| 195 | 
            +
                        self.log_warning("Incomplete read - client may have disconnected")
         | 
| 196 | 
            +
                        self._connected = False
         | 
| 197 | 
            +
                        return None
         | 
| 198 | 
            +
                    except json.JSONDecodeError as e:
         | 
| 199 | 
            +
                        self.log_error(f"Invalid JSON received: {e}")
         | 
| 200 | 
            +
                        self._metrics["errors"] += 1
         | 
| 201 | 
            +
                        return None
         | 
| 202 | 
            +
                    except Exception as e:
         | 
| 203 | 
            +
                        self.log_error(f"Error receiving message: {e}")
         | 
| 204 | 
            +
                        self._metrics["errors"] += 1
         | 
| 205 | 
            +
                        return None
         | 
| 206 | 
            +
                
         | 
| 207 | 
            +
                async def send_response(self, request_id: str, result: Any) -> None:
         | 
| 208 | 
            +
                    """
         | 
| 209 | 
            +
                    Send a response to a request.
         | 
| 210 | 
            +
                    
         | 
| 211 | 
            +
                    Args:
         | 
| 212 | 
            +
                        request_id: ID of the request being responded to
         | 
| 213 | 
            +
                        result: Result data
         | 
| 214 | 
            +
                    """
         | 
| 215 | 
            +
                    response = {
         | 
| 216 | 
            +
                        "jsonrpc": "2.0",
         | 
| 217 | 
            +
                        "id": request_id,
         | 
| 218 | 
            +
                        "result": result
         | 
| 219 | 
            +
                    }
         | 
| 220 | 
            +
                    await self.send_message(response)
         | 
| 221 | 
            +
                
         | 
| 222 | 
            +
                async def send_error(self, request_id: str, error: str, code: int = -1) -> None:
         | 
| 223 | 
            +
                    """
         | 
| 224 | 
            +
                    Send an error response.
         | 
| 225 | 
            +
                    
         | 
| 226 | 
            +
                    Args:
         | 
| 227 | 
            +
                        request_id: ID of the request that caused the error
         | 
| 228 | 
            +
                        error: Error message
         | 
| 229 | 
            +
                        code: Error code (default -1 for generic error)
         | 
| 230 | 
            +
                    """
         | 
| 231 | 
            +
                    response = {
         | 
| 232 | 
            +
                        "jsonrpc": "2.0",
         | 
| 233 | 
            +
                        "id": request_id,
         | 
| 234 | 
            +
                        "error": {
         | 
| 235 | 
            +
                            "code": code,
         | 
| 236 | 
            +
                            "message": error
         | 
| 237 | 
            +
                        }
         | 
| 238 | 
            +
                    }
         | 
| 239 | 
            +
                    await self.send_message(response)
         | 
| 240 | 
            +
                
         | 
| 241 | 
            +
                async def send_notification(self, method: str, params: Optional[Dict[str, Any]] = None) -> None:
         | 
| 242 | 
            +
                    """
         | 
| 243 | 
            +
                    Send a notification (no response expected).
         | 
| 244 | 
            +
                    
         | 
| 245 | 
            +
                    Args:
         | 
| 246 | 
            +
                        method: Notification method
         | 
| 247 | 
            +
                        params: Optional parameters
         | 
| 248 | 
            +
                    """
         | 
| 249 | 
            +
                    notification = {
         | 
| 250 | 
            +
                        "jsonrpc": "2.0",
         | 
| 251 | 
            +
                        "method": method
         | 
| 252 | 
            +
                    }
         | 
| 253 | 
            +
                    if params:
         | 
| 254 | 
            +
                        notification["params"] = params
         | 
| 255 | 
            +
                    
         | 
| 256 | 
            +
                    await self.send_message(notification)
         | 
| 257 | 
            +
                
         | 
| 258 | 
            +
                def is_connected(self) -> bool:
         | 
| 259 | 
            +
                    """
         | 
| 260 | 
            +
                    Check if communication channel is connected.
         | 
| 261 | 
            +
                    
         | 
| 262 | 
            +
                    Returns:
         | 
| 263 | 
            +
                        True if connected
         | 
| 264 | 
            +
                    """
         | 
| 265 | 
            +
                    return self._connected
         | 
| 266 | 
            +
                
         | 
| 267 | 
            +
                def get_metrics(self) -> Dict[str, Any]:
         | 
| 268 | 
            +
                    """
         | 
| 269 | 
            +
                    Get communication metrics.
         | 
| 270 | 
            +
                    
         | 
| 271 | 
            +
                    Returns:
         | 
| 272 | 
            +
                        Metrics dictionary
         | 
| 273 | 
            +
                    """
         | 
| 274 | 
            +
                    return self._metrics.copy()
         | 
| 275 | 
            +
             | 
| 276 | 
            +
             | 
| 277 | 
            +
            class AlternativeStdioHandler(StdioHandler):
         | 
| 278 | 
            +
                """
         | 
| 279 | 
            +
                Alternative STDIO handler using direct sys.stdin/stdout.
         | 
| 280 | 
            +
                
         | 
| 281 | 
            +
                This implementation doesn't use asyncio streams but instead
         | 
| 282 | 
            +
                reads directly from sys.stdin in a blocking manner, which
         | 
| 283 | 
            +
                can be simpler for some use cases.
         | 
| 284 | 
            +
                
         | 
| 285 | 
            +
                WHY: Some MCP implementations may work better with simpler
         | 
| 286 | 
            +
                blocking I/O, especially when running as a subprocess.
         | 
| 287 | 
            +
                """
         | 
| 288 | 
            +
                
         | 
| 289 | 
            +
                async def _do_initialize(self) -> bool:
         | 
| 290 | 
            +
                    """
         | 
| 291 | 
            +
                    Initialize the alternative STDIO handler.
         | 
| 292 | 
            +
                    
         | 
| 293 | 
            +
                    Returns:
         | 
| 294 | 
            +
                        True if initialization successful
         | 
| 295 | 
            +
                    """
         | 
| 296 | 
            +
                    try:
         | 
| 297 | 
            +
                        self.log_info("Initializing alternative STDIO handler")
         | 
| 298 | 
            +
                        self._connected = True
         | 
| 299 | 
            +
                        self.log_info("Alternative STDIO handler initialized")
         | 
| 300 | 
            +
                        return True
         | 
| 301 | 
            +
                        
         | 
| 302 | 
            +
                    except Exception as e:
         | 
| 303 | 
            +
                        self.log_error(f"Failed to initialize alternative STDIO handler: {e}")
         | 
| 304 | 
            +
                        return False
         | 
| 305 | 
            +
                
         | 
| 306 | 
            +
                async def receive_message(self) -> Optional[Dict[str, Any]]:
         | 
| 307 | 
            +
                    """
         | 
| 308 | 
            +
                    Receive a message using blocking I/O with asyncio executor.
         | 
| 309 | 
            +
                    
         | 
| 310 | 
            +
                    Returns:
         | 
| 311 | 
            +
                        Received message or None if no message available
         | 
| 312 | 
            +
                    """
         | 
| 313 | 
            +
                    try:
         | 
| 314 | 
            +
                        if not self._connected:
         | 
| 315 | 
            +
                            return None
         | 
| 316 | 
            +
                        
         | 
| 317 | 
            +
                        # Run blocking I/O in executor
         | 
| 318 | 
            +
                        loop = asyncio.get_event_loop()
         | 
| 319 | 
            +
                        message = await loop.run_in_executor(None, self._blocking_receive)
         | 
| 320 | 
            +
                        
         | 
| 321 | 
            +
                        if message:
         | 
| 322 | 
            +
                            self._metrics["messages_received"] += 1
         | 
| 323 | 
            +
                            self.log_debug(f"Received message: {message.get('method', message.get('id', 'unknown'))}")
         | 
| 324 | 
            +
                        
         | 
| 325 | 
            +
                        return message
         | 
| 326 | 
            +
                        
         | 
| 327 | 
            +
                    except Exception as e:
         | 
| 328 | 
            +
                        self.log_error(f"Error receiving message: {e}")
         | 
| 329 | 
            +
                        self._metrics["errors"] += 1
         | 
| 330 | 
            +
                        return None
         | 
| 331 | 
            +
                
         | 
| 332 | 
            +
                def _blocking_receive(self) -> Optional[Dict[str, Any]]:
         | 
| 333 | 
            +
                    """
         | 
| 334 | 
            +
                    Blocking receive implementation.
         | 
| 335 | 
            +
                    
         | 
| 336 | 
            +
                    Returns:
         | 
| 337 | 
            +
                        Received message or None
         | 
| 338 | 
            +
                    """
         | 
| 339 | 
            +
                    try:
         | 
| 340 | 
            +
                        # Read headers
         | 
| 341 | 
            +
                        headers = {}
         | 
| 342 | 
            +
                        while True:
         | 
| 343 | 
            +
                            line = sys.stdin.readline()
         | 
| 344 | 
            +
                            if not line:
         | 
| 345 | 
            +
                                # EOF
         | 
| 346 | 
            +
                                self._connected = False
         | 
| 347 | 
            +
                                return None
         | 
| 348 | 
            +
                            
         | 
| 349 | 
            +
                            line = line.rstrip('\r\n')
         | 
| 350 | 
            +
                            if not line:
         | 
| 351 | 
            +
                                # End of headers
         | 
| 352 | 
            +
                                break
         | 
| 353 | 
            +
                            
         | 
| 354 | 
            +
                            if ':' in line:
         | 
| 355 | 
            +
                                key, value = line.split(':', 1)
         | 
| 356 | 
            +
                                headers[key.strip()] = value.strip()
         | 
| 357 | 
            +
                        
         | 
| 358 | 
            +
                        # Get content length
         | 
| 359 | 
            +
                        content_length = headers.get('Content-Length')
         | 
| 360 | 
            +
                        if not content_length:
         | 
| 361 | 
            +
                            return None
         | 
| 362 | 
            +
                        
         | 
| 363 | 
            +
                        content_length = int(content_length)
         | 
| 364 | 
            +
                        
         | 
| 365 | 
            +
                        # Read content
         | 
| 366 | 
            +
                        content = sys.stdin.read(content_length)
         | 
| 367 | 
            +
                        
         | 
| 368 | 
            +
                        # Parse JSON
         | 
| 369 | 
            +
                        return json.loads(content)
         | 
| 370 | 
            +
                        
         | 
| 371 | 
            +
                    except Exception as e:
         | 
| 372 | 
            +
                        self.log_error(f"Error in blocking receive: {e}")
         | 
| 373 | 
            +
                        return None
         | 
| @@ -2,8 +2,21 @@ | |
| 2 2 | 
             
            MCP Gateway Tools Module
         | 
| 3 3 | 
             
            ========================
         | 
| 4 4 |  | 
| 5 | 
            -
            Tool  | 
| 5 | 
            +
            Tool adapters and implementations for the MCP Gateway service.
         | 
| 6 6 | 
             
            """
         | 
| 7 7 |  | 
| 8 | 
            -
             | 
| 9 | 
            -
             | 
| 8 | 
            +
            from .base_adapter import (
         | 
| 9 | 
            +
                BaseToolAdapter,
         | 
| 10 | 
            +
                EchoToolAdapter,
         | 
| 11 | 
            +
                CalculatorToolAdapter,
         | 
| 12 | 
            +
                SystemInfoToolAdapter,
         | 
| 13 | 
            +
            )
         | 
| 14 | 
            +
            from .document_summarizer import DocumentSummarizerTool
         | 
| 15 | 
            +
             | 
| 16 | 
            +
            __all__ = [
         | 
| 17 | 
            +
                "BaseToolAdapter",
         | 
| 18 | 
            +
                "EchoToolAdapter",
         | 
| 19 | 
            +
                "CalculatorToolAdapter",
         | 
| 20 | 
            +
                "SystemInfoToolAdapter",
         | 
| 21 | 
            +
                "DocumentSummarizerTool",
         | 
| 22 | 
            +
            ]
         |