79 lines
2.5 KiB
Python
79 lines
2.5 KiB
Python
# -*- 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)]
|