diff --git a/unraid_mcp/tools/_docker.py b/unraid_mcp/tools/_docker.py index d698a21..474eab7 100644 --- a/unraid_mcp/tools/_docker.py +++ b/unraid_mcp/tools/_docker.py @@ -31,7 +31,10 @@ _DOCKER_MUTATIONS: dict[str, str] = { "stop": "mutation StopContainer($id: PrefixedID!) { docker { stop(id: $id) { id names state status } } }", } -_DOCKER_SUBACTIONS: set[str] = set(_DOCKER_QUERIES) | set(_DOCKER_MUTATIONS) | {"restart"} +# "logs" has no GraphQL query (field removed in Unraid 7.2.x) but is still a +# recognised subaction so validation passes and the informative ToolError below +# is returned rather than a generic "Invalid action" message. +_DOCKER_SUBACTIONS: set[str] = set(_DOCKER_QUERIES) | set(_DOCKER_MUTATIONS) | {"restart", "logs"} _DOCKER_NEEDS_CONTAINER_ID = {"start", "stop", "details", "restart"} _DOCKER_ID_PATTERN = re.compile(r"^[a-f0-9]{64}(:[a-z0-9]+)?$", re.IGNORECASE) _DOCKER_SHORT_ID_PATTERN = re.compile(r"^[a-f0-9]{12,63}$", re.IGNORECASE) @@ -131,6 +134,13 @@ async def _handle_docker( return dict(net) raise ToolError(f"Network '{network_id}' not found.") + if subaction == "logs": + raise ToolError( + "Container logs are not available via the Unraid GraphQL API. " + "Use the Unraid terminal or SSH to run: " + f"`docker logs {container_id or ''} --tail 100`" + ) + if subaction == "restart": actual_id = await _resolve_container_id(container_id or "", strict=True) stop_data = await _client.make_graphql_request( diff --git a/unraid_mcp/tools/_system.py b/unraid_mcp/tools/_system.py index 18ef4a9..5610b52 100644 --- a/unraid_mcp/tools/_system.py +++ b/unraid_mcp/tools/_system.py @@ -43,6 +43,8 @@ _SYSTEM_QUERIES: dict[str, str] = { } } """, + # Uses the vars root field — returns only the network-access subset + # (port, portssl, localTld, useSsl) alongside servers for URL construction. "network": """ query GetNetworkInfo { servers { id name status wanip lanip localurl remoteurl } @@ -54,6 +56,9 @@ _SYSTEM_QUERIES: dict[str, str] = { registration { id type keyFile { location } state expiration updateExpiration } } """, + # Uses the vars root field — returns a broad subset of system variables. + # Shares the same GraphQL root as "network" but requests a different field + # set; no overlap occurs because GraphQL returns only the requested subfields. "variables": """ query GetSelectiveUnraidVariables { vars { @@ -206,7 +211,19 @@ async def _handle_system(subaction: str, device_id: str | None) -> dict[str, Any values = settings["unified"].get("values") or {} return dict(values) if isinstance(values, dict) else {"raw": values} if subaction == "server": - return data + info = data.get("info") or {} + summary: dict[str, Any] = {} + if info.get("os"): + summary["hostname"] = info["os"].get("hostname") + summary["uptime"] = info["os"].get("uptime") + if info.get("versions") and info["versions"].get("core"): + summary["unraid_version"] = info["versions"]["core"].get("unraid") + summary["machine_id"] = info.get("machineId") + summary["time"] = info.get("time") + array = data.get("array") or {} + summary["array_state"] = array.get("state") + summary["online"] = data.get("online") + return summary if subaction == "network": servers_data = data.get("servers") or [] vars_data = data.get("vars") or {}