Process an MCP JSON-RPC message and return the response dict. Returns ``None`` for notifications (no ``id`` field).
(data: dict[str, Any])
| 248 | |
| 249 | |
| 250 | def _process_mcp_message(data: dict[str, Any]) -> dict[str, Any] | None: |
| 251 | """ |
| 252 | Process an MCP JSON-RPC message and return the response dict. |
| 253 | |
| 254 | Returns ``None`` for notifications (no ``id`` field). |
| 255 | """ |
| 256 | method = data.get("method", "") |
| 257 | params = data.get("params", {}) or {} |
| 258 | _id = data.get("id") |
| 259 | request_id: str | int = _id if isinstance(_id, (str, int)) else "" |
| 260 | |
| 261 | app = get_app() |
| 262 | if app.mcp_callback_map is None: |
| 263 | app.mcp_callback_map = CallbackAdapterCollection(app) |
| 264 | app.mcp_decorated_functions = dict(MCP_DECORATED_FUNCTIONS) |
| 265 | |
| 266 | mcp_methods = { |
| 267 | "initialize": _handle_initialize, |
| 268 | "tools/list": list_tools, |
| 269 | "tools/call": lambda: call_tool( |
| 270 | tool_name=params.get("name", ""), |
| 271 | arguments=params.get("arguments", {}), |
| 272 | task=params.get("task"), |
| 273 | ), |
| 274 | "resources/list": list_resources, |
| 275 | "resources/templates/list": list_resource_templates, |
| 276 | "resources/read": lambda: read_resource(params.get("uri", "")), |
| 277 | "tasks/get": lambda: get_task(task_id=params.get("taskId", "")), |
| 278 | "tasks/result": lambda: get_task_result(task_id=params.get("taskId", "")), |
| 279 | "tasks/cancel": lambda: cancel_task(task_id=params.get("taskId", "")), |
| 280 | } |
| 281 | |
| 282 | try: |
| 283 | handler = mcp_methods.get(method) |
| 284 | if handler is None: |
| 285 | if method.startswith("notifications/"): |
| 286 | return None |
| 287 | raise ValueError(f"Unknown method: {method}") |
| 288 | |
| 289 | result = handler() |
| 290 | |
| 291 | response = JSONRPCResponse( |
| 292 | jsonrpc="2.0", |
| 293 | id=request_id, |
| 294 | result=result.model_dump(exclude_none=True, mode="json"), |
| 295 | ) |
| 296 | return response.model_dump(exclude_none=True, mode="json") |
| 297 | |
| 298 | except MCPError as e: |
| 299 | logger.error("MCP error: %s", e) |
| 300 | return JSONRPCError( |
| 301 | jsonrpc="2.0", |
| 302 | id=request_id, |
| 303 | error=ErrorData(code=e.code, message=str(e)), |
| 304 | ).model_dump(exclude_none=True) |
| 305 | except Exception as e: # pylint: disable=broad-exception-caught |
| 306 | logger.error("MCP error: %s", e, exc_info=True) |
| 307 | return JSONRPCError( |
searching dependent graphs…