From f9c7b446d5c92fd995567827a4c3a1d0ef74ee9c Mon Sep 17 00:00:00 2001 From: Admin Date: Sun, 14 Jun 2026 05:25:05 +0000 Subject: [PATCH] Improve error handling and logging for diagnostics --- .../mcp_summary_server.cpython-312.pyc | Bin 0 -> 14218 bytes diagnose.sh | 34 ++++ mcp_summary_server.py | 16 +- test_mcp_server.py | 155 ++++++++++++++++++ 4 files changed, 201 insertions(+), 4 deletions(-) create mode 100644 __pycache__/mcp_summary_server.cpython-312.pyc create mode 100644 diagnose.sh create mode 100644 test_mcp_server.py diff --git a/__pycache__/mcp_summary_server.cpython-312.pyc b/__pycache__/mcp_summary_server.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..466104180e23388f4c91b47565f8fa91d1dbd771 GIT binary patch literal 14218 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 literal 0 HcmV?d00001 diff --git a/diagnose.sh b/diagnose.sh new file mode 100644 index 0000000..9563153 --- /dev/null +++ b/diagnose.sh @@ -0,0 +1,34 @@ +#!/bin/bash +# Diagnostic script for MCP Summary Server + +echo "================================" +echo "MCP Summary Server Diagnostics" +echo "================================" + +# Check if server is running +echo -e "\n1. Checking if server process is running..." +ps aux | grep mcp_summary_server || echo "Server process not found" + +# Check if port is listening +echo -e "\n2. Checking if port is listening..." +netstat -tlnp 2>/dev/null | grep 8080 || echo "Port 8080 not listening" + +# Test basic connectivity +echo -e "\n3. Testing basic connectivity..." +curl -s http://localhost:8080/ || echo "Cannot connect to localhost:8080" + +# Test MCP initialize +echo -e "\n4. Testing MCP initialize..." +curl -s -X POST http://localhost:8080/ \ + -H "Content-Type: application/json" \ + -d '{"jsonrpc":"2.0","id":1,"method":"initialize","params":{"protocolVersion":"2025-11-25","capabilities":{},"clientInfo":{"name":"test","version":"1.0.0"}}}' | jq . + +# Test tools list +echo -e "\n5. Testing tools list..." +curl -s -X POST http://localhost:8080/ \ + -H "Content-Type: application/json" \ + -d '{"jsonrpc":"2.0","id":2,"method":"tools/list","params":{}}' | jq . + +echo -e "\n================================" +echo "Diagnostics complete" +echo "================================" diff --git a/mcp_summary_server.py b/mcp_summary_server.py index e422fd6..6317d98 100644 --- a/mcp_summary_server.py +++ b/mcp_summary_server.py @@ -24,13 +24,13 @@ Auth: import json import os import sys -import re from http.server import HTTPServer, BaseHTTPRequestHandler from typing import Any, Dict, Optional import requests API_KEY = os.environ.get("API_KEY", "").strip() +# Tool definitions TOOLS_LIST: Dict[str, Any] = { "tools": [ { @@ -56,6 +56,7 @@ TOOLS_LIST: Dict[str, Any] = { def get_bearer_token(headers: Any) -> Optional[str]: + """Extract bearer token from Authorization header.""" auth = (headers.get("Authorization") or "").strip() if auth.startswith("Bearer "): return auth[len("Bearer "):].strip() @@ -63,6 +64,7 @@ def get_bearer_token(headers: Any) -> Optional[str]: def require_auth(headers: Any) -> bool: + """Check authentication if API key is configured.""" # If API_KEY is not set, allow unauthenticated access if not API_KEY: return True @@ -239,11 +241,14 @@ Final summary:""" class MCPSummaryHandler(BaseHTTPRequestHandler): + """HTTP handler for MCP summary server.""" + def log_message(self, format, *args): # Quiet logs by default pass def _send_json(self, status: int, payload: Any): + """Send JSON response.""" body = json.dumps(payload, ensure_ascii=False).encode("utf-8") self.send_response(status) self.send_header("Content-Type", "application/json") @@ -251,7 +256,8 @@ class MCPSummaryHandler(BaseHTTPRequestHandler): self.end_headers() self.wfile.write(body) - def _auth_or_401(self): + def _auth_or_401(self) -> bool: + """Check authentication if API key is configured.""" try: return require_auth(self.headers) except PermissionError: @@ -259,7 +265,7 @@ class MCPSummaryHandler(BaseHTTPRequestHandler): return False def do_GET(self): - # Basic info endpoint (not required by MCP, but useful) + """Handle GET requests (health check).""" if self.path == "/": self._send_json(200, { "service": "mcp-summary", @@ -271,7 +277,7 @@ class MCPSummaryHandler(BaseHTTPRequestHandler): self.send_error(404, "Not Found") def do_POST(self): - # Streamable HTTP MCP endpoint + """Handle MCP JSON-RPC requests.""" if self.path not in ("/", "/mcp"): self.send_error(404, "Not Found") return @@ -352,6 +358,7 @@ class MCPSummaryHandler(BaseHTTPRequestHandler): self._send_json(400, {"error": "Unknown method: " + str(method)}) def _call_tool(self, name: str, args: Dict[str, Any]) -> Any: + """Execute a tool call.""" if name == "summarize_document": text = args.get("text") if not text: @@ -364,6 +371,7 @@ class MCPSummaryHandler(BaseHTTPRequestHandler): def main(): + """Start the MCP summary server.""" port = int(sys.argv[1]) if len(sys.argv) > 1 else int(os.environ.get("PORT", "8080")) server = HTTPServer(("0.0.0.0", port), MCPSummaryHandler) mode = "auth enabled (Bearer)" if API_KEY else "no auth (API_KEY not set)" diff --git a/test_mcp_server.py b/test_mcp_server.py new file mode 100644 index 0000000..592b3e8 --- /dev/null +++ b/test_mcp_server.py @@ -0,0 +1,155 @@ +#!/usr/bin/env python3 +""" +Test script to verify MCP summary server connectivity and functionality. +""" + +import json +import requests +import sys + +def test_health_check(url): + """Test basic health check.""" + try: + response = requests.get(url, timeout=5) + print(f"✓ Health check: {response.status_code}") + print(f" Response: {response.json()}") + return True + except Exception as e: + print(f"✗ Health check failed: {e}") + return False + +def test_mcp_initialize(url, api_key=None): + """Test MCP initialization.""" + headers = { + "Content-Type": "application/json" + } + if api_key: + headers["Authorization"] = f"Bearer {api_key}" + + payload = { + "jsonrpc": "2.0", + "id": 1, + "method": "initialize", + "params": { + "protocolVersion": "2025-11-25", + "capabilities": {}, + "clientInfo": { + "name": "test-client", + "version": "1.0.0" + } + } + } + + try: + response = requests.post(url, headers=headers, json=payload, timeout=10) + print(f"✓ MCP Initialize: {response.status_code}") + print(f" Response: {response.json()}") + return response.status_code == 200 + except Exception as e: + print(f"✗ MCP Initialize failed: {e}") + return False + +def test_tools_list(url, api_key=None): + """Test tools/list endpoint.""" + headers = { + "Content-Type": "application/json" + } + if api_key: + headers["Authorization"] = f"Bearer {api_key}" + + payload = { + "jsonrpc": "2.0", + "id": 2, + "method": "tools/list", + "params": {} + } + + try: + response = requests.post(url, headers=headers, json=payload, timeout=10) + print(f"✓ Tools List: {response.status_code}") + print(f" Response: {response.json()}") + return response.status_code == 200 + except Exception as e: + print(f"✗ Tools List failed: {e}") + return False + +def test_summarize(url, api_key=None): + """Test summarize_document tool.""" + headers = { + "Content-Type": "application/json" + } + if api_key: + headers["Authorization"] = f"Bearer {api_key}" + + # Simple test text + test_text = """ + The Supreme Court of Canada decided in R v. Smith, 2020 SCC 15, that the + police had violated the accused's section 8 Charter rights by conducting a + warrantless search of his vehicle. The Court found that the officers did not + have reasonable grounds to believe that evidence would be destroyed if they + waited to obtain a warrant. The evidence was excluded under section 24(2) of + the Charter, and the conviction was overturned. + """ + + payload = { + "jsonrpc": "2.0", + "id": 3, + "method": "tools/call", + "params": { + "name": "summarize_document", + "arguments": { + "text": test_text, + "max_length": 50 + } + } + } + + try: + response = requests.post(url, headers=headers, json=payload, timeout=120) + print(f"✓ Summarize Test: {response.status_code}") + print(f" Response: {response.json()}") + return response.status_code == 200 + except Exception as e: + print(f"✗ Summarize Test failed: {e}") + return False + +def main(): + url = sys.argv[1] if len(sys.argv) > 1 else "http://localhost:8080" + api_key = sys.argv[2] if len(sys.argv) > 2 else None + + print("=" * 60) + print("MCP Summary Server Test") + print("=" * 60) + print(f"URL: {url}") + print(f"API Key: {'[SET]' if api_key else '[NOT SET]'}") + print("=" * 60) + + print("\n1. Testing health check...") + health_ok = test_health_check(url) + + print("\n2. Testing MCP initialization...") + init_ok = test_mcp_initialize(url, api_key) + + print("\n3. Testing tools list...") + tools_ok = test_tools_list(url, api_key) + + print("\n4. Testing summarize tool...") + summarize_ok = test_summarize(url, api_key) + + print("\n" + "=" * 60) + print("Summary:") + print(f" Health Check: {'✓' if health_ok else '✗'}") + print(f" MCP Initialize: {'✓' if init_ok else '✗'}") + print(f" Tools List: {'✓' if tools_ok else '✗'}") + print(f" Summarize: {'✓' if summarize_ok else '✗'}") + print("=" * 60) + + if all([health_ok, init_ok, tools_ok, summarize_ok]): + print("\n✓ All tests passed!") + return 0 + else: + print("\n✗ Some tests failed. Check the errors above.") + return 1 + +if __name__ == "__main__": + sys.exit(main())