diff --git a/tools/mcp_tool_handlers.py b/tools/mcp_tool_handlers.py index 488c68ccfd..d25014ffa6 100644 --- a/tools/mcp_tool_handlers.py +++ b/tools/mcp_tool_handlers.py @@ -39,9 +39,8 @@ def _trust_gate_check(server_name: str, tool_name: str) -> Optional[str]: answer = request_elicitation_consent( ( - f"MCP tool '{tool_name}' on UNTRUSTED server " - f"'{server_name}' wants to run. This tool is write-capable " - f"(no readOnlyHint=true annotation) and may modify external " + f"MCP tool '{tool_name}' on UNTRUSTED server '{server_name}' wants to run. This " + f"tool is write-capable (no readOnlyHint=true annotation) and may modify external " f"state." ), ( @@ -56,9 +55,8 @@ def _trust_gate_check(server_name: str, tool_name: str) -> Optional[str]: server_name, tool_name, exc, exc_info=True, ) return tool_error( - f"MCP tool '{tool_name}' on untrusted server '{server_name}' " - f"was blocked: the approval system was unavailable " - f"(fail-closed)." + f"MCP tool '{tool_name}' on untrusted server '{server_name}' was blocked: the approval " + f"system was unavailable (fail-closed)." ) if answer == "accept": @@ -69,9 +67,8 @@ def _trust_gate_check(server_name: str, tool_name: str) -> Optional[str]: tool_name, server_name, ) return tool_error( - f"The user did not approve running write-capable MCP tool " - f"'{tool_name}' on untrusted server '{server_name}'. The command " - f"was NOT run. Do not retry without explicit user direction." + f"The user did not approve running write-capable MCP tool '{tool_name}' on untrusted server " + f"'{server_name}'. The command was NOT run. Do not retry without explicit user direction." ) @@ -90,10 +87,8 @@ def _check_circuit_breaker(server_name: str) -> Optional[str]: return None remaining = max(1, int(_core._CIRCUIT_BREAKER_COOLDOWN_SEC - age)) return tool_error( - f"MCP server '{server_name}' is unreachable after " - f"{failures} consecutive " - f"failures. Auto-retry available in ~{remaining}s. " - f"Do NOT retry this tool yet — use alternative " + f"MCP server '{server_name}' is unreachable after {failures} consecutive failures. " + f"Auto-retry available in ~{remaining}s. Do NOT retry this tool yet — use alternative " f"approaches or ask the user to check the MCP server." ) @@ -120,9 +115,8 @@ def _acquire_call_server(server_name: str, tool_timeout: float): _core._bump_server_error(server_name) if _core._signal_reconnect(server): return None, tool_error( - f"MCP server '{server_name}' transport is down; " - f"reconnect requested. Do NOT retry this tool " - f"immediately — give it a few seconds to come back." + f"MCP server '{server_name}' transport is down; reconnect requested. Do NOT retry this " + f"tool immediately — give it a few seconds to come back." ) return None, not_connected @@ -229,10 +223,9 @@ def _handle_auth_error_and_retry( # No recovery, or retry failed: structured needs_reauth error + breaker strike. _core._bump_server_error(server_name) return tool_error( - f"MCP server '{server_name}' requires re-authentication. " - f"Run `hermes mcp login {server_name}` (or delete the tokens " - f"file under ~/.hermes/mcp-tokens/ and restart). Do NOT retry " - f"this tool — ask the user to re-authenticate.", + f"MCP server '{server_name}' requires re-authentication. Run `hermes mcp login " + f"{server_name}` (or delete the tokens file under ~/.hermes/mcp-tokens/ and restart). Do " + f"NOT retry this tool — ask the user to re-authenticate.", needs_reauth=True, server=server_name, ) @@ -325,10 +318,9 @@ def _handle_stdio_child_exited_and_retry( if not reconnected: _core._bump_server_error(server_name) return tool_error( - f"MCP server '{server_name}' stdio subprocess had exited (this is " - f"not a timeout — the call never reached the server). A respawn was " - f"requested but no fresh session came back within " - f"{_core._STDIO_RESPAWN_WAIT_SEC:.0f}s. Wait a few seconds before retrying; " + f"MCP server '{server_name}' stdio subprocess had exited (this is not a timeout — the " + f"call never reached the server). A respawn was requested but no fresh session came " + f"back within {_core._STDIO_RESPAWN_WAIT_SEC:.0f}s. Wait a few seconds before retrying; " f"if it keeps failing the server is not starting and needs the user." ) @@ -343,10 +335,9 @@ def _handle_stdio_child_exited_and_retry( server_name, op_description, retry_exc, ) message = ( - f"MCP server '{server_name}' respawned its stdio subprocess " - f"and it exited again immediately. The server is not " - f"starting cleanly — do NOT retry this tool; ask the user to " - f"check the server's command and its stderr log." + f"MCP server '{server_name}' respawned its stdio subprocess and it exited again " + f"immediately. The server is not starting cleanly — do NOT retry this tool; ask the " + f"user to check the server's command and its stderr log." ) except Exception as retry_exc: logger.warning( @@ -354,9 +345,8 @@ def _handle_stdio_child_exited_and_retry( server_name, op_description, retry_exc, ) message = _sanitize_error( - f"MCP call failed after respawning the stdio subprocess for " - f"'{server_name}': {type(retry_exc).__name__}: " - f"{_exc_str(retry_exc)}" + f"MCP call failed after respawning the stdio subprocess for '{server_name}': " + f"{type(retry_exc).__name__}: {_exc_str(retry_exc)}" ) _core._bump_server_error(server_name) return tool_error(message) diff --git a/tools/mcp_tool_health.py b/tools/mcp_tool_health.py index ee1d8c63cd..4d5c611492 100644 --- a/tools/mcp_tool_health.py +++ b/tools/mcp_tool_health.py @@ -253,9 +253,8 @@ class MCPServerHealthMixin: raise self._ping_unsupported = True logger.info( - "MCP server '%s': does not implement the optional " - "'ping' utility (-32601); using 'list_tools' for " - "keepalive on this connection.", + "MCP server '%s': does not implement the optional 'ping' utility (-32601); " + "using 'list_tools' for keepalive on this connection.", self.name, ) elif isinstance(exc, (TimeoutError, asyncio.TimeoutError)) and self._advertises_tools(): @@ -269,9 +268,8 @@ class MCPServerHealthMixin: # Transport alive; latch so later keepalives skip the 30s wait. self._ping_unsupported = True logger.info( - "MCP server '%s': ping timed out but list_tools " - "succeeded — server silently drops ping; using " - "'list_tools' for keepalive on this connection.", + "MCP server '%s': ping timed out but list_tools succeeded — server " + "silently drops ping; using 'list_tools' for keepalive on this connection.", self.name, ) return @@ -310,8 +308,7 @@ class MCPServerHealthMixin: :meth:`ensure_healthy` and recycles the transport if the probe fails.""" if self._suspect_reason is None and reason: logger.warning( - "MCP server '%s': connection marked suspect (%s); next call " - "will health-check it", + "MCP server '%s': connection marked suspect (%s); next call will health-check it", self.name, reason, ) self._suspect_reason = reason or None @@ -336,9 +333,8 @@ class MCPServerHealthMixin: except Exception as exc: root = _unwrap_exception_group(exc) logger.warning( - "MCP server '%s': suspect connection (%s) failed health " - "check (%s: %s) — requesting reconnect (state: suspect → " - "degraded)", + "MCP server '%s': suspect connection (%s) failed health check (%s: %s) — " + "requesting reconnect (state: suspect → degraded)", self.name, reason, type(root).__name__, root, ) self._suspect_reason = None @@ -348,8 +344,7 @@ class MCPServerHealthMixin: self._reconnect_event.set() return False logger.info( - "MCP server '%s': suspect connection passed health check " - "(%s) — clearing suspicion", + "MCP server '%s': suspect connection passed health check (%s) — clearing suspicion", self.name, reason, ) self._suspect_reason = None diff --git a/tools/mcp_tool_transport.py b/tools/mcp_tool_transport.py index b7ff7e04f6..79ad17a30c 100644 --- a/tools/mcp_tool_transport.py +++ b/tools/mcp_tool_transport.py @@ -260,8 +260,7 @@ class MCPServerTransportMixin: if not _core._ensure_mcp_sdk(): raise ImportError( f"MCP server '{self.name}' requires the 'mcp' Python SDK, but " - "it is not installed. Run `hermes setup` to install MCP support, " - "then retry." + "it is not installed. Run `hermes setup` to install MCP support, then retry." ) command, args, safe_env = self._resolve_stdio_config(config) await _osv_malware_preflight(self.name, command, args) @@ -394,12 +393,10 @@ class MCPServerTransportMixin: if not ct_base or ct_base in self._MCP_CONTENT_TYPES: return raise NonMcpEndpointError( - f"MCP server '{self.name}' at {url} returned Content-Type " - f"'{ct_base}', not an MCP response (expected one of: " - f"{', '.join(self._MCP_CONTENT_TYPES)}). The URL most likely " - "points at a web page rather than an MCP endpoint — check it " - "resolves to a Streamable HTTP / SSE endpoint " - "(e.g. https://host/mcp, not https://host/)." + f"MCP server '{self.name}' at {url} returned Content-Type '{ct_base}', not an MCP " + f"response (expected one of: {', '.join(self._MCP_CONTENT_TYPES)}). The URL most likely " + "points at a web page rather than an MCP endpoint — check it resolves to a Streamable " + "HTTP / SSE endpoint (e.g. https://host/mcp, not https://host/)." ) def _reconnect_or_reraise_group(self, eg: BaseExceptionGroup) -> str: