# -*- coding: utf-8 -*- """MCP JSON-RPC handler (Streamable HTTP compatible).""" import json import logging from typing import Any, Optional from . import tools LOGGER = logging.getLogger("loogle_mcp.server") PROTOCOL_VERSION = "2024-11-05" def _error(req_id: Any, code: int, message: str) -> dict: return {"jsonrpc": "2.0", "id": req_id, "error": {"code": code, "message": message}} def _result(req_id: Any, result: dict) -> dict: return {"jsonrpc": "2.0", "id": req_id, "result": result} def handle_message(body: dict, claims: Optional[dict]) -> dict: method = body.get("method") req_id = body.get("id") params = body.get("params") or {} if method == "initialize": return _result( req_id, { "protocolVersion": PROTOCOL_VERSION, "capabilities": {"tools": {}, "resources": {}}, "serverInfo": {"name": "loogle-mcp", "version": "1.0.0"}, }, ) if method == "notifications/initialized": return _result(req_id, {}) if method == "ping": return _result(req_id, {}) if not claims: return _error(req_id, -32001, "Autenticazione richiesta (Bearer token OAuth)") if method == "tools/list": return _result(req_id, {"tools": tools.tool_definitions()}) if method == "tools/call": name = params.get("name") arguments = params.get("arguments") or {} try: tool_result = tools.call_tool(name, arguments, claims) return _result(req_id, tool_result) except PermissionError as exc: return _error(req_id, -32003, str(exc)) except FileNotFoundError as exc: return _error(req_id, -32004, str(exc)) except Exception as exc: LOGGER.exception("Tool %s failed", name) return _error(req_id, -32000, str(exc)) if method == "resources/list": return _result(req_id, {"resources": tools.list_resources(claims)}) if method == "resources/read": uri = params.get("uri") try: resource = tools.read_resource(uri, claims) return _result(req_id, {"contents": [resource]}) except FileNotFoundError as exc: return _error(req_id, -32004, str(exc)) return _error(req_id, -32601, f"Metodo non supportato: {method}") def handle_batch(messages: list, claims: Optional[dict]) -> list: return [handle_message(msg, claims) for msg in messages if isinstance(msg, dict)]