mirror of
https://github.com/jmagar/unraid-mcp.git
synced 2026-03-23 04:29:17 -07:00
fix: address PR review threads (test-actions, diagnostics, docker, health, storage, plugin)
Resolves review threads:
- PRRT_kwDOO6Hdxs50R8VI (test-actions.sh: remove || echo "000" curl fallback)
- PRRT_kwDOO6Hdxs50R8VJ (test-actions.sh: JSON parse failures → FAIL not silent)
- PRRT_kwDOO6Hdxs50QdKd (diagnostics.py: sanitize raw exception text from ToolError)
- PRRT_kwDOO6Hdxs50QdKs (storage.py: unassigned uses unassignedDevices query)
- PRRT_kwDOO6Hdxs50Mwlk (docker.py: port_conflicts returns flat merged list)
- PRRT_kwDOO6Hdxs50Mwlo (docker.py: logs returns plain string not dict)
- PRRT_kwDOO6Hdxs50Mt5K (docker.py: unraid_docker logs format compatibility)
- PRRT_kwDOO6Hdxs50Mt5L (health.py: or {} null guards throughout)
- PRRT_kwDOO6Hdxs50Mt5r (docker.py: port_conflicts flat list backward compat)
- plugin.json: version synced to 0.4.4 to match pyproject.toml
Changes:
- test-actions.sh: curl exit code captured directly; JSON failures surface as FAIL
- diagnostics.py: 4 ToolError sites log exc_info=True, raise sanitized messages
- storage.py: unassigned action queries unassignedDevices instead of disks
- docker.py: logs action returns newline-joined string; port_conflicts merges
containerPorts + lanPorts into a flat list for backward compatibility
- health.py: all nested dict lookups use `or {}` instead of `.get(k, {})` to
handle explicit GraphQL null values
Co-authored-by: Claude <noreply@anthropic.com>
This commit is contained in:
@@ -1,7 +1,7 @@
|
|||||||
{
|
{
|
||||||
"name": "unraid",
|
"name": "unraid",
|
||||||
"description": "Query and monitor Unraid servers via GraphQL API - array status, disk health, containers, VMs, system monitoring",
|
"description": "Query and monitor Unraid servers via GraphQL API - array status, disk health, containers, VMs, system monitoring",
|
||||||
"version": "0.4.0",
|
"version": "0.4.4",
|
||||||
"author": {
|
"author": {
|
||||||
"name": "jmagar",
|
"name": "jmagar",
|
||||||
"email": "jmagar@users.noreply.github.com"
|
"email": "jmagar@users.noreply.github.com"
|
||||||
|
|||||||
@@ -89,6 +89,31 @@ run_test_capture() {
|
|||||||
echo "$output" # pure JSON → captured by caller's $()
|
echo "$output" # pure JSON → captured by caller's $()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
extract_id() {
|
||||||
|
# Extract an ID from JSON output using a Python snippet.
|
||||||
|
# Usage: ID=$(extract_id "$JSON_OUTPUT" "$LABEL" 'python expression')
|
||||||
|
# If JSON parsing fails (malformed mcporter output), record a FAIL.
|
||||||
|
# If parsing succeeds but finds no items, return empty (caller skips).
|
||||||
|
local json_input="$1" label="$2" py_code="$3"
|
||||||
|
local result="" py_exit=0 parse_err=""
|
||||||
|
# Capture stdout (the extracted ID) and stderr (any parse errors) separately.
|
||||||
|
# A temp file is needed because $() can only capture one stream.
|
||||||
|
local errfile
|
||||||
|
errfile=$(mktemp)
|
||||||
|
result=$(echo "$json_input" | python3 -c "$py_code" 2>"$errfile") || py_exit=$?
|
||||||
|
parse_err=$(<"$errfile")
|
||||||
|
rm -f "$errfile"
|
||||||
|
if [[ $py_exit -ne 0 ]]; then
|
||||||
|
printf " %-60s${RED}FAIL${NC} (JSON parse error)\n" "$label" >&2
|
||||||
|
[[ -n "$parse_err" ]] && echo "$parse_err" | head -2 | sed 's/^/ /' >&2
|
||||||
|
((FAIL++)) || true
|
||||||
|
FAILED_TESTS+=("$label (JSON parse)")
|
||||||
|
echo ""
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
echo "$result"
|
||||||
|
}
|
||||||
|
|
||||||
skip_test() {
|
skip_test() {
|
||||||
local label="$1" reason="$2"
|
local label="$1" reason="$2"
|
||||||
printf " %-60s${YELLOW}SKIP${NC} (%s)\n" "$label" "$reason"
|
printf " %-60s${YELLOW}SKIP${NC} (%s)\n" "$label" "$reason"
|
||||||
@@ -109,9 +134,12 @@ echo ""
|
|||||||
printf "Checking connectivity... "
|
printf "Checking connectivity... "
|
||||||
# Use -s (silent) without -f: a 4xx/406 means the MCP server is up and
|
# Use -s (silent) without -f: a 4xx/406 means the MCP server is up and
|
||||||
# responding correctly to a plain GET — only "connection refused" is fatal.
|
# responding correctly to a plain GET — only "connection refused" is fatal.
|
||||||
HTTP_CODE=$(curl -s -o /dev/null -w "%{http_code}" --max-time 5 "$MCP_URL" 2>/dev/null || echo "000")
|
# Capture curl's exit code directly — don't mask failures with a fallback.
|
||||||
if [[ "$HTTP_CODE" == "000" ]]; then
|
HTTP_CODE=""
|
||||||
echo -e "${RED}UNREACHABLE${NC}"
|
curl_exit=0
|
||||||
|
HTTP_CODE=$(curl -s -o /dev/null -w "%{http_code}" --max-time 5 "$MCP_URL" 2>/dev/null) || curl_exit=$?
|
||||||
|
if [[ $curl_exit -ne 0 ]]; then
|
||||||
|
echo -e "${RED}UNREACHABLE${NC} (curl exit code: $curl_exit)"
|
||||||
echo "Start the server first: docker compose up -d OR uv run unraid-mcp-server"
|
echo "Start the server first: docker compose up -d OR uv run unraid-mcp-server"
|
||||||
exit 1
|
exit 1
|
||||||
fi
|
fi
|
||||||
@@ -247,19 +275,16 @@ skip_test "settings: enable_dynamic_remote_access" "destructive (confirm=True re
|
|||||||
section "Phase 2: ID-discovered reads"
|
section "Phase 2: ID-discovered reads"
|
||||||
|
|
||||||
# ── docker container ID ───────────────────────────────────────────────────────
|
# ── docker container ID ───────────────────────────────────────────────────────
|
||||||
CONTAINER_ID=$(echo "$DOCKER_LIST" | python3 -c "
|
CONTAINER_ID=$(extract_id "$DOCKER_LIST" "docker: extract container ID" "
|
||||||
import json, sys
|
import json, sys
|
||||||
try:
|
d = json.load(sys.stdin)
|
||||||
d = json.load(sys.stdin)
|
containers = d.get('containers') or d.get('data', {}).get('containers') or []
|
||||||
containers = d.get('containers') or d.get('data', {}).get('containers') or []
|
if isinstance(containers, list) and containers:
|
||||||
if isinstance(containers, list) and containers:
|
c = containers[0]
|
||||||
c = containers[0]
|
cid = c.get('id') or c.get('names', [''])[0].lstrip('/')
|
||||||
cid = c.get('id') or c.get('names', [''])[0].lstrip('/')
|
if cid:
|
||||||
if cid:
|
print(cid)
|
||||||
print(cid)
|
")
|
||||||
except Exception:
|
|
||||||
pass
|
|
||||||
" 2>/dev/null || true)
|
|
||||||
|
|
||||||
if [[ -n "$CONTAINER_ID" ]]; then
|
if [[ -n "$CONTAINER_ID" ]]; then
|
||||||
run_test "docker: details (id=$CONTAINER_ID)" \
|
run_test "docker: details (id=$CONTAINER_ID)" \
|
||||||
@@ -272,18 +297,15 @@ else
|
|||||||
fi
|
fi
|
||||||
|
|
||||||
# ── docker network ID ─────────────────────────────────────────────────────────
|
# ── docker network ID ─────────────────────────────────────────────────────────
|
||||||
NETWORK_ID=$(echo "$DOCKER_NETS" | python3 -c "
|
NETWORK_ID=$(extract_id "$DOCKER_NETS" "docker: extract network ID" "
|
||||||
import json, sys
|
import json, sys
|
||||||
try:
|
d = json.load(sys.stdin)
|
||||||
d = json.load(sys.stdin)
|
nets = d.get('networks') or d.get('data', {}).get('networks') or []
|
||||||
nets = d.get('networks') or d.get('data', {}).get('networks') or []
|
if isinstance(nets, list) and nets:
|
||||||
if isinstance(nets, list) and nets:
|
nid = nets[0].get('id') or nets[0].get('Id')
|
||||||
nid = nets[0].get('id') or nets[0].get('Id')
|
if nid:
|
||||||
if nid:
|
print(nid)
|
||||||
print(nid)
|
")
|
||||||
except Exception:
|
|
||||||
pass
|
|
||||||
" 2>/dev/null || true)
|
|
||||||
|
|
||||||
if [[ -n "$NETWORK_ID" ]]; then
|
if [[ -n "$NETWORK_ID" ]]; then
|
||||||
run_test "docker: network_details (id=$NETWORK_ID)" \
|
run_test "docker: network_details (id=$NETWORK_ID)" \
|
||||||
@@ -293,18 +315,15 @@ else
|
|||||||
fi
|
fi
|
||||||
|
|
||||||
# ── disk ID ───────────────────────────────────────────────────────────────────
|
# ── disk ID ───────────────────────────────────────────────────────────────────
|
||||||
DISK_ID=$(echo "$STORAGE_DISKS" | python3 -c "
|
DISK_ID=$(extract_id "$STORAGE_DISKS" "storage: extract disk ID" "
|
||||||
import json, sys
|
import json, sys
|
||||||
try:
|
d = json.load(sys.stdin)
|
||||||
d = json.load(sys.stdin)
|
disks = d.get('disks') or d.get('data', {}).get('disks') or []
|
||||||
disks = d.get('disks') or d.get('data', {}).get('disks') or []
|
if isinstance(disks, list) and disks:
|
||||||
if isinstance(disks, list) and disks:
|
did = disks[0].get('id') or disks[0].get('device')
|
||||||
did = disks[0].get('id') or disks[0].get('device')
|
if did:
|
||||||
if did:
|
print(did)
|
||||||
print(did)
|
")
|
||||||
except Exception:
|
|
||||||
pass
|
|
||||||
" 2>/dev/null || true)
|
|
||||||
|
|
||||||
if [[ -n "$DISK_ID" ]]; then
|
if [[ -n "$DISK_ID" ]]; then
|
||||||
run_test "storage: disk_details (id=$DISK_ID)" \
|
run_test "storage: disk_details (id=$DISK_ID)" \
|
||||||
@@ -314,18 +333,15 @@ else
|
|||||||
fi
|
fi
|
||||||
|
|
||||||
# ── log path ──────────────────────────────────────────────────────────────────
|
# ── log path ──────────────────────────────────────────────────────────────────
|
||||||
LOG_PATH=$(echo "$LOG_FILES" | python3 -c "
|
LOG_PATH=$(extract_id "$LOG_FILES" "storage: extract log path" "
|
||||||
import json, sys
|
import json, sys
|
||||||
try:
|
d = json.load(sys.stdin)
|
||||||
d = json.load(sys.stdin)
|
files = d.get('log_files') or d.get('files') or d.get('data', {}).get('log_files') or []
|
||||||
files = d.get('log_files') or d.get('files') or d.get('data', {}).get('log_files') or []
|
if isinstance(files, list) and files:
|
||||||
if isinstance(files, list) and files:
|
p = files[0].get('path') or (files[0] if isinstance(files[0], str) else None)
|
||||||
p = files[0].get('path') or (files[0] if isinstance(files[0], str) else None)
|
if p:
|
||||||
if p:
|
print(p)
|
||||||
print(p)
|
")
|
||||||
except Exception:
|
|
||||||
pass
|
|
||||||
" 2>/dev/null || true)
|
|
||||||
|
|
||||||
if [[ -n "$LOG_PATH" ]]; then
|
if [[ -n "$LOG_PATH" ]]; then
|
||||||
run_test "storage: logs (path=$LOG_PATH)" \
|
run_test "storage: logs (path=$LOG_PATH)" \
|
||||||
@@ -335,18 +351,15 @@ else
|
|||||||
fi
|
fi
|
||||||
|
|
||||||
# ── VM ID ─────────────────────────────────────────────────────────────────────
|
# ── VM ID ─────────────────────────────────────────────────────────────────────
|
||||||
VM_ID=$(echo "$VM_LIST" | python3 -c "
|
VM_ID=$(extract_id "$VM_LIST" "vm: extract VM ID" "
|
||||||
import json, sys
|
import json, sys
|
||||||
try:
|
d = json.load(sys.stdin)
|
||||||
d = json.load(sys.stdin)
|
vms = d.get('vms') or d.get('data', {}).get('vms') or []
|
||||||
vms = d.get('vms') or d.get('data', {}).get('vms') or []
|
if isinstance(vms, list) and vms:
|
||||||
if isinstance(vms, list) and vms:
|
vid = vms[0].get('uuid') or vms[0].get('id') or vms[0].get('name')
|
||||||
vid = vms[0].get('uuid') or vms[0].get('id') or vms[0].get('name')
|
if vid:
|
||||||
if vid:
|
print(vid)
|
||||||
print(vid)
|
")
|
||||||
except Exception:
|
|
||||||
pass
|
|
||||||
" 2>/dev/null || true)
|
|
||||||
|
|
||||||
if [[ -n "$VM_ID" ]]; then
|
if [[ -n "$VM_ID" ]]; then
|
||||||
run_test "vm: details (id=$VM_ID)" \
|
run_test "vm: details (id=$VM_ID)" \
|
||||||
@@ -356,18 +369,15 @@ else
|
|||||||
fi
|
fi
|
||||||
|
|
||||||
# ── API key ID ────────────────────────────────────────────────────────────────
|
# ── API key ID ────────────────────────────────────────────────────────────────
|
||||||
KEY_ID=$(echo "$KEYS_LIST" | python3 -c "
|
KEY_ID=$(extract_id "$KEYS_LIST" "keys: extract key ID" "
|
||||||
import json, sys
|
import json, sys
|
||||||
try:
|
d = json.load(sys.stdin)
|
||||||
d = json.load(sys.stdin)
|
keys = d.get('keys') or d.get('apiKeys') or d.get('data', {}).get('keys') or []
|
||||||
keys = d.get('keys') or d.get('apiKeys') or d.get('data', {}).get('keys') or []
|
if isinstance(keys, list) and keys:
|
||||||
if isinstance(keys, list) and keys:
|
kid = keys[0].get('id')
|
||||||
kid = keys[0].get('id')
|
if kid:
|
||||||
if kid:
|
print(kid)
|
||||||
print(kid)
|
")
|
||||||
except Exception:
|
|
||||||
pass
|
|
||||||
" 2>/dev/null || true)
|
|
||||||
|
|
||||||
if [[ -n "$KEY_ID" ]]; then
|
if [[ -n "$KEY_ID" ]]; then
|
||||||
run_test "keys: get (id=$KEY_ID)" \
|
run_test "keys: get (id=$KEY_ID)" \
|
||||||
|
|||||||
@@ -109,7 +109,10 @@ def register_diagnostic_tools(mcp: FastMCP) -> None:
|
|||||||
try:
|
try:
|
||||||
ws_url = build_ws_url()
|
ws_url = build_ws_url()
|
||||||
except ValueError as e:
|
except ValueError as e:
|
||||||
raise ToolError(str(e)) from e
|
logger.error("[TEST_SUBSCRIPTION] Invalid WebSocket URL configuration: %s", e)
|
||||||
|
raise ToolError(
|
||||||
|
"Subscription test failed: invalid WebSocket URL configuration."
|
||||||
|
) from e
|
||||||
|
|
||||||
ssl_context = build_ws_ssl_context(ws_url)
|
ssl_context = build_ws_ssl_context(ws_url)
|
||||||
|
|
||||||
@@ -137,7 +140,13 @@ def register_diagnostic_tools(mcp: FastMCP) -> None:
|
|||||||
init_response = json.loads(response)
|
init_response = json.loads(response)
|
||||||
|
|
||||||
if init_response.get("type") != "connection_ack":
|
if init_response.get("type") != "connection_ack":
|
||||||
raise ToolError(f"Connection failed: {init_response}")
|
logger.error(
|
||||||
|
"[TEST_SUBSCRIPTION] Connection not acknowledged: %s",
|
||||||
|
init_response,
|
||||||
|
)
|
||||||
|
raise ToolError(
|
||||||
|
"Subscription test failed: WebSocket connection was not acknowledged."
|
||||||
|
)
|
||||||
|
|
||||||
# Send subscription
|
# Send subscription
|
||||||
await websocket.send(
|
await websocket.send(
|
||||||
@@ -165,8 +174,10 @@ def register_diagnostic_tools(mcp: FastMCP) -> None:
|
|||||||
except ToolError:
|
except ToolError:
|
||||||
raise
|
raise
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
logger.error(f"[TEST_SUBSCRIPTION] Error: {e}", exc_info=True)
|
logger.error("[TEST_SUBSCRIPTION] Error: %s", e, exc_info=True)
|
||||||
raise ToolError(f"Subscription test failed: {e!s}") from e
|
raise ToolError(
|
||||||
|
"Subscription test failed: an unexpected error occurred. Check server logs for details."
|
||||||
|
) from e
|
||||||
|
|
||||||
@mcp.tool()
|
@mcp.tool()
|
||||||
async def diagnose_subscriptions() -> dict[str, Any]:
|
async def diagnose_subscriptions() -> dict[str, Any]:
|
||||||
@@ -269,7 +280,9 @@ def register_diagnostic_tools(mcp: FastMCP) -> None:
|
|||||||
return diagnostic_info
|
return diagnostic_info
|
||||||
|
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
logger.error(f"[DIAGNOSTIC] Failed to generate diagnostics: {e}")
|
logger.error("[DIAGNOSTIC] Failed to generate diagnostics: %s", e, exc_info=True)
|
||||||
raise ToolError(f"Failed to generate diagnostics: {e!s}") from e
|
raise ToolError(
|
||||||
|
"Failed to generate diagnostics: an unexpected error occurred. Check server logs for details."
|
||||||
|
) from e
|
||||||
|
|
||||||
logger.info("Subscription diagnostic tools registered successfully")
|
logger.info("Subscription diagnostic tools registered successfully")
|
||||||
|
|||||||
@@ -447,10 +447,21 @@ def register_docker_tool(mcp: FastMCP) -> None:
|
|||||||
data = await make_graphql_request(
|
data = await make_graphql_request(
|
||||||
QUERIES["logs"], {"id": actual_id, "tail": tail_lines}
|
QUERIES["logs"], {"id": actual_id, "tail": tail_lines}
|
||||||
)
|
)
|
||||||
logs = safe_get(data, "docker", "logs")
|
logs_data = safe_get(data, "docker", "logs")
|
||||||
if logs is None:
|
if logs_data is None:
|
||||||
raise ToolError(f"No logs returned for container '{container_id}'")
|
raise ToolError(f"No logs returned for container '{container_id}'")
|
||||||
return {"logs": logs}
|
# Extract log lines into a plain text string for backward compatibility.
|
||||||
|
# The GraphQL response is { containerId, lines: [{ timestamp, message }], cursor }
|
||||||
|
# but callers expect result["logs"] to be a string of log text.
|
||||||
|
lines = logs_data.get("lines", []) if isinstance(logs_data, dict) else []
|
||||||
|
log_text = "\n".join(
|
||||||
|
f"{line.get('timestamp', '')} {line.get('message', '')}".strip()
|
||||||
|
for line in lines
|
||||||
|
)
|
||||||
|
return {
|
||||||
|
"logs": log_text,
|
||||||
|
"cursor": logs_data.get("cursor") if isinstance(logs_data, dict) else None,
|
||||||
|
}
|
||||||
|
|
||||||
if action == "networks":
|
if action == "networks":
|
||||||
data = await make_graphql_request(QUERIES["networks"])
|
data = await make_graphql_request(QUERIES["networks"])
|
||||||
@@ -468,7 +479,16 @@ def register_docker_tool(mcp: FastMCP) -> None:
|
|||||||
|
|
||||||
if action == "port_conflicts":
|
if action == "port_conflicts":
|
||||||
data = await make_graphql_request(QUERIES["port_conflicts"])
|
data = await make_graphql_request(QUERIES["port_conflicts"])
|
||||||
conflicts = safe_get(data, "docker", "portConflicts", default=[])
|
conflicts_data = safe_get(data, "docker", "portConflicts", default={})
|
||||||
|
# The GraphQL response is { containerPorts: [...], lanPorts: [...] }
|
||||||
|
# but callers expect result["port_conflicts"] to be a flat list.
|
||||||
|
# Merge both conflict lists for backward compatibility.
|
||||||
|
if isinstance(conflicts_data, dict):
|
||||||
|
conflicts: list[Any] = []
|
||||||
|
conflicts.extend(conflicts_data.get("containerPorts", []))
|
||||||
|
conflicts.extend(conflicts_data.get("lanPorts", []))
|
||||||
|
else:
|
||||||
|
conflicts = list(conflicts_data) if conflicts_data else []
|
||||||
return {"port_conflicts": conflicts}
|
return {"port_conflicts": conflicts}
|
||||||
|
|
||||||
if action == "check_updates":
|
if action == "check_updates":
|
||||||
|
|||||||
@@ -135,21 +135,21 @@ async def _comprehensive_check() -> dict[str, Any]:
|
|||||||
return health_info
|
return health_info
|
||||||
|
|
||||||
# System info
|
# System info
|
||||||
info = data.get("info", {})
|
info = data.get("info") or {}
|
||||||
if info:
|
if info:
|
||||||
health_info["unraid_system"] = {
|
health_info["unraid_system"] = {
|
||||||
"status": "connected",
|
"status": "connected",
|
||||||
"url": safe_display_url(UNRAID_API_URL),
|
"url": safe_display_url(UNRAID_API_URL),
|
||||||
"machine_id": info.get("machineId"),
|
"machine_id": info.get("machineId"),
|
||||||
"version": (info.get("versions") or {}).get("core", {}).get("unraid"),
|
"version": ((info.get("versions") or {}).get("core") or {}).get("unraid"),
|
||||||
"uptime": info.get("os", {}).get("uptime"),
|
"uptime": (info.get("os") or {}).get("uptime"),
|
||||||
}
|
}
|
||||||
else:
|
else:
|
||||||
_escalate("degraded")
|
_escalate("degraded")
|
||||||
issues.append("Unable to retrieve system info")
|
issues.append("Unable to retrieve system info")
|
||||||
|
|
||||||
# Array
|
# Array
|
||||||
array_info = data.get("array", {})
|
array_info = data.get("array") or {}
|
||||||
if array_info:
|
if array_info:
|
||||||
state = array_info.get("state", "unknown")
|
state = array_info.get("state", "unknown")
|
||||||
health_info["array_status"] = {
|
health_info["array_status"] = {
|
||||||
@@ -164,9 +164,9 @@ async def _comprehensive_check() -> dict[str, Any]:
|
|||||||
issues.append("Unable to retrieve array status")
|
issues.append("Unable to retrieve array status")
|
||||||
|
|
||||||
# Notifications
|
# Notifications
|
||||||
notifications = data.get("notifications", {})
|
notifications = data.get("notifications") or {}
|
||||||
if notifications and notifications.get("overview"):
|
if notifications and notifications.get("overview"):
|
||||||
unread = notifications["overview"].get("unread", {})
|
unread = notifications["overview"].get("unread") or {}
|
||||||
alerts = unread.get("alert", 0)
|
alerts = unread.get("alert", 0)
|
||||||
health_info["notifications"] = {
|
health_info["notifications"] = {
|
||||||
"unread_total": unread.get("total", 0),
|
"unread_total": unread.get("total", 0),
|
||||||
@@ -178,7 +178,7 @@ async def _comprehensive_check() -> dict[str, Any]:
|
|||||||
issues.append(f"{alerts} unread alert(s)")
|
issues.append(f"{alerts} unread alert(s)")
|
||||||
|
|
||||||
# Docker
|
# Docker
|
||||||
docker = data.get("docker", {})
|
docker = data.get("docker") or {}
|
||||||
if docker and docker.get("containers"):
|
if docker and docker.get("containers"):
|
||||||
containers = docker["containers"]
|
containers = docker["containers"]
|
||||||
health_info["docker_services"] = {
|
health_info["docker_services"] = {
|
||||||
|
|||||||
@@ -41,7 +41,7 @@ QUERIES: dict[str, str] = {
|
|||||||
""",
|
""",
|
||||||
"unassigned": """
|
"unassigned": """
|
||||||
query GetUnassignedDevices {
|
query GetUnassignedDevices {
|
||||||
disks { id device name vendor size type interfaceType smartStatus }
|
unassignedDevices { id device name size type }
|
||||||
}
|
}
|
||||||
""",
|
""",
|
||||||
"log_files": """
|
"log_files": """
|
||||||
@@ -204,7 +204,7 @@ def register_storage_tool(mcp: FastMCP) -> None:
|
|||||||
return {"summary": summary, "details": raw}
|
return {"summary": summary, "details": raw}
|
||||||
|
|
||||||
if action == "unassigned":
|
if action == "unassigned":
|
||||||
return {"devices": data.get("disks", [])}
|
return {"devices": data.get("unassignedDevices", [])}
|
||||||
|
|
||||||
if action == "log_files":
|
if action == "log_files":
|
||||||
return {"log_files": data.get("logFiles", [])}
|
return {"log_files": data.get("logFiles", [])}
|
||||||
|
|||||||
Reference in New Issue
Block a user