Handler for standard MCP protocol requests
| 27 | MCP_PROTOCOL_VERSION = "2024-11-05" |
| 28 | |
| 29 | class MCPHandler: |
| 30 | """Handler for standard MCP protocol requests""" |
| 31 | |
| 32 | def __init__(self): |
| 33 | self.initialized = False |
| 34 | self.capabilities = { |
| 35 | "tools": {} |
| 36 | } |
| 37 | self._tools_cache = None |
| 38 | |
| 39 | async def handle_request(self, request_data, query_params, send_response, send_chunk): |
| 40 | """ |
| 41 | Handle a JSON-RPC 2.0 MCP request |
| 42 | |
| 43 | Args: |
| 44 | request_data: Parsed JSON request data |
| 45 | query_params: URL query parameters |
| 46 | send_response: Function to send response headers |
| 47 | send_chunk: Function to send response body |
| 48 | """ |
| 49 | # Extract JSON-RPC fields |
| 50 | jsonrpc = request_data.get("jsonrpc", "2.0") |
| 51 | method = request_data.get("method") |
| 52 | params = request_data.get("params", {}) |
| 53 | request_id = request_data.get("id") |
| 54 | |
| 55 | # Check if this is a notification (no id field) |
| 56 | is_notification = request_id is None |
| 57 | |
| 58 | logger.info(f"MCP request: method={method}, id={request_id}, is_notification={is_notification}") |
| 59 | # Commented out verbose logging |
| 60 | # print(f"=== MCP REQUEST: method={method}, id={request_id}, initialized={self.initialized}, handler_id={id(self)} ===") |
| 61 | |
| 62 | try: |
| 63 | # Route based on method |
| 64 | if method == "initialize": |
| 65 | result = await self.handle_initialize(params) |
| 66 | # print(f"=== INITIALIZE COMPLETE, sending response ===") |
| 67 | elif method == "initialized" or method == "notifications/initialized": |
| 68 | # This is a notification, no response needed |
| 69 | self.initialized = True |
| 70 | logger.info("MCP server initialized") |
| 71 | # print(f"=== SERVER MARKED AS INITIALIZED ===") |
| 72 | if not is_notification: |
| 73 | result = {"status": "ok"} |
| 74 | else: |
| 75 | return # No response for notifications |
| 76 | elif method == "tools/list": |
| 77 | # Temporarily disable initialization check for debugging |
| 78 | # if not self.initialized: |
| 79 | # raise Exception("Server not initialized") |
| 80 | logger.info(f"tools/list called, initialized={self.initialized}") |
| 81 | result = await self.handle_tools_list(params) |
| 82 | elif method == "tools/call": |
| 83 | # print(f"=== TOOLS/CALL: initialized={self.initialized} ===") |
| 84 | # Remove the initialization check - MCP clients might not send initialize first |
| 85 | # if not self.initialized: |
| 86 | # raise Exception("Server not initialized") |