from __future__ import annotations import os import subprocess from collections.abc import Callable from typing import Any from .nb_client import NbAuthenticationError, NbCommandError ENABLE_RAW_DATA_ENV = "NOCOBASE_MCP_ENABLE_RAW_DATA" def safe_tool_result(operation: Callable[[], dict[str, Any]]) -> dict[str, Any]: try: return operation() except NbAuthenticationError as error: return tool_error( code="nb_auth_failed", message=str(error), hint=( "Ask the local installation Agent to run the secure connect-key flow, then enter your personal " "NocoBase API Key only in the private local prompt." ), retryable=False, ) except NbCommandError as error: return tool_error( code="nb_command_failed", message=str(error), hint="Check nb env status, NocoBase ACL, collection names, and query parameters.", retryable=False, ) except subprocess.TimeoutExpired as error: return tool_error( code="nb_command_timeout", message=str(error), hint="The NocoBase CLI call timed out. Retry with a smaller query or check NocoBase responsiveness.", retryable=True, ) except FileNotFoundError as error: return tool_error( code="nb_not_found", message=str(error), hint="Set NOCOBASE_MCP_NB_BIN to the absolute nb path, then restart with scripts/mcp_service.sh start.", retryable=False, ) except Exception as error: return tool_error( code="tool_error", message=str(error), hint="Check MCP server logs and the CRM mapping config.", retryable=False, ) def safe_raw_tool_result(include_raw: bool, operation: Callable[[], dict[str, Any]]) -> dict[str, Any]: if include_raw and os.environ.get(ENABLE_RAW_DATA_ENV, "").strip() != "1": return tool_error( code="raw_data_disabled", message="Raw NocoBase rows are disabled for this local MCP installation.", hint=f"Keep include_raw=false, or set {ENABLE_RAW_DATA_ENV}=1 after approving the data exposure policy.", retryable=False, ) return safe_tool_result(operation) def tool_error(*, code: str, message: str, hint: str, retryable: bool) -> dict[str, Any]: return {"error": {"code": code, "message": message, "hint": hint, "retryable": retryable}} # Compatibility aliases for internal imports and older tests. _safe_tool_result = safe_tool_result _safe_raw_tool_result = safe_raw_tool_result _tool_error = tool_error __all__ = [ "ENABLE_RAW_DATA_ENV", "safe_tool_result", "safe_raw_tool_result", "tool_error", "_safe_tool_result", "_safe_raw_tool_result", "_tool_error", ]