fix: address 18 CRITICAL+HIGH PR review comments

**Critical Fixes (7 issues):**
- Fix GraphQL schema field names in users tool (role→roles, remove email)
- Fix GraphQL mutation signatures (addUserInput, deleteUser input)
- Fix dict(None) TypeError guards in users tool (use `or {}` pattern)
- Fix FastAPI version constraint (0.116.1→0.115.0)
- Fix WebSocket SSL context handling (support CA bundles, bool, and None)
- Fix critical disk threshold treated as warning (split counters)

**High Priority Fixes (11 issues):**
- Fix Docker update/remove action response field mapping
- Fix path traversal vulnerability in log validation (normalize paths)
- Fix deleteApiKeys validation (check response before success)
- Fix rclone create_remote validation (check response)
- Fix keys input_data type annotation (dict[str, Any])
- Fix VM domain/domains fallback restoration

**Changes by file:**
- unraid_mcp/tools/docker.py: Response field mapping
- unraid_mcp/tools/info.py: Split critical/warning counters
- unraid_mcp/tools/storage.py: Path normalization for traversal protection
- unraid_mcp/tools/users.py: GraphQL schema + null handling
- unraid_mcp/tools/keys.py: Validation + type annotations
- unraid_mcp/tools/rclone.py: Response validation
- unraid_mcp/tools/virtualization.py: Domain fallback
- unraid_mcp/subscriptions/manager.py: SSL context creation
- pyproject.toml: FastAPI version fix
- tests/*: New tests for all fixes

**Review threads resolved:**
PRRT_kwDOO6Hdxs5uu70L, PRRT_kwDOO6Hdxs5uu70O, PRRT_kwDOO6Hdxs5uu70V,
PRRT_kwDOO6Hdxs5uu70e, PRRT_kwDOO6Hdxs5uu70i, PRRT_kwDOO6Hdxs5uu7zn,
PRRT_kwDOO6Hdxs5uu7z_, PRRT_kwDOO6Hdxs5uu7sI, PRRT_kwDOO6Hdxs5uu7sJ,
PRRT_kwDOO6Hdxs5uu7sK, PRRT_kwDOO6Hdxs5uu7Tk, PRRT_kwDOO6Hdxs5uu7Tn,
PRRT_kwDOO6Hdxs5uu7Tr, PRRT_kwDOO6Hdxs5uu7Ts, PRRT_kwDOO6Hdxs5uu7Tu,
PRRT_kwDOO6Hdxs5uu7Tv, PRRT_kwDOO6Hdxs5uu7Tw, PRRT_kwDOO6Hdxs5uu7Tx

All tests passing.

Co-authored-by: docker-fixer <agent@pr-fixes>
Co-authored-by: info-fixer <agent@pr-fixes>
Co-authored-by: storage-fixer <agent@pr-fixes>
Co-authored-by: users-fixer <agent@pr-fixes>
Co-authored-by: config-fixer <agent@pr-fixes>
Co-authored-by: websocket-fixer <agent@pr-fixes>
Co-authored-by: keys-rclone-fixer <agent@pr-fixes>
Co-authored-by: vm-fixer <agent@pr-fixes>
This commit is contained in:
Jacob Magar
2026-02-15 16:42:58 -05:00
parent 2697c269a3
commit 184b8aca1c
35 changed files with 9360 additions and 588 deletions

View File

@@ -0,0 +1,27 @@
{
"name": "unraid",
"description": "Query and monitor Unraid servers via GraphQL API - array status, disk health, containers, VMs, system monitoring",
"version": "1.1.0",
"author": {
"name": "jmagar",
"email": "jmagar@users.noreply.github.com"
},
"homepage": "https://github.com/jmagar/unraid-mcp",
"repository": "https://github.com/jmagar/unraid-mcp",
"mcpServers": {
"unraid": {
"command": "uv",
"args": [
"run",
"--directory",
"${CLAUDE_PLUGIN_ROOT}/../..",
"unraid-mcp-server"
],
"env": {
"UNRAID_API_URL": "${UNRAID_API_URL}",
"UNRAID_API_KEY": "${UNRAID_API_KEY}",
"UNRAID_MCP_TRANSPORT": "stdio"
}
}
}
}

149
skills/unraid/README.md Normal file
View File

@@ -0,0 +1,149 @@
# Unraid API Skill
Query and monitor Unraid servers via the GraphQL API.
## What's Included
This skill provides complete access to all 27 read-only Unraid GraphQL API endpoints.
### Files
```
skills/unraid/
├── SKILL.md # Main skill documentation
├── README.md # This file
├── scripts/
│ └── unraid-query.sh # GraphQL query helper script
├── examples/
│ ├── monitoring-dashboard.sh # Complete system dashboard
│ ├── disk-health.sh # Disk temperature & health check
│ └── read-logs.sh # Log file reader
└── references/
├── api-reference.md # Complete API documentation
└── quick-reference.md # Common queries cheat sheet
```
## Quick Start
1. **Set your credentials:**
```bash
export UNRAID_URL="https://your-unraid-server/graphql"
export UNRAID_API_KEY="your-api-key"
```
2. **Run a query:**
```bash
cd skills/unraid
./scripts/unraid-query.sh -q "{ online }"
```
3. **Run examples:**
```bash
./examples/monitoring-dashboard.sh
./examples/disk-health.sh
```
## Triggers
This skill activates when you mention:
- "check Unraid"
- "monitor Unraid"
- "Unraid API"
- "Unraid disk temperatures"
- "Unraid array status"
- "read Unraid logs"
- And more Unraid-related monitoring tasks
## Features
- **27 working endpoints** - All read-only queries documented
- **Helper script** - Easy CLI interface for GraphQL queries
- **Example scripts** - Ready-to-use monitoring scripts
- **Complete reference** - Detailed documentation with examples
- **Quick reference** - Common queries cheat sheet
## Endpoints Covered
### System & Monitoring
- System info (CPU, OS, hardware)
- Real-time metrics (CPU %, memory %)
- Configuration & settings
- Log files (list & read)
### Storage
- Array status & disks
- All physical disks (including cache/USB)
- Network shares
- Parity check status
### Virtualization
- Docker containers
- Virtual machines
### Power & Alerts
- UPS devices
- System notifications
### Administration
- API key management
- User & authentication
- Server registration
- UI customization
## Requirements
- **Unraid 7.2+** (GraphQL API)
- **API Key** with Viewer role
- **jq** for JSON parsing (usually pre-installed)
- **curl** for HTTP requests
## Getting an API Key
1. Log in to Unraid WebGUI
2. Settings → Management Access → API Keys
3. Click "Create API Key"
4. Name: "monitoring" (or whatever you like)
5. Role: Select "Viewer" (read-only)
6. Copy the generated key
## Documentation
- **SKILL.md** - Start here for task-oriented guidance
- **references/api-reference.md** - Complete endpoint reference
- **references/quick-reference.md** - Quick query examples
## Examples
### System Status
```bash
./scripts/unraid-query.sh -q "{ online metrics { cpu { percentTotal } } }"
```
### Disk Health
```bash
./examples/disk-health.sh
```
### Complete Dashboard
```bash
./examples/monitoring-dashboard.sh
```
### Read Logs
```bash
./examples/read-logs.sh syslog 20
```
## Notes
- All sizes are in **kilobytes**
- Temperatures are in **Celsius**
- Docker container logs are **not accessible** via API (use SSH)
- Poll no faster than every **5 seconds** to avoid server load
## Version
- **Skill Version:** 1.0.0
- **API Version:** Unraid 7.2 GraphQL
- **Tested:** 2026-01-21
- **Endpoints:** 27 working read-only queries

210
skills/unraid/SKILL.md Normal file
View File

@@ -0,0 +1,210 @@
---
name: unraid
description: "Query and monitor Unraid servers via the GraphQL API. Use when the user asks to 'check Unraid', 'monitor Unraid', 'Unraid API', 'get Unraid status', 'check disk temperatures', 'read Unraid logs', 'list Unraid shares', 'Unraid array status', 'Unraid containers', 'Unraid VMs', or mentions Unraid system monitoring, disk health, parity checks, or server status."
---
# Unraid API Skill
**⚠️ MANDATORY SKILL INVOCATION ⚠️**
**YOU MUST invoke this skill (NOT optional) when the user mentions ANY of these triggers:**
- "Unraid status", "disk health", "array status"
- "Unraid containers", "VMs on Unraid", "Unraid logs"
- "check Unraid", "Unraid monitoring", "server health"
- Any mention of Unraid servers or system monitoring
**Failure to invoke this skill when triggers occur violates your operational requirements.**
Query and monitor Unraid servers using the GraphQL API. Access all 27 read-only endpoints for system monitoring, disk health, logs, containers, VMs, and more.
## Quick Start
Set your Unraid server credentials:
```bash
export UNRAID_URL="https://your-unraid-server/graphql"
export UNRAID_API_KEY="your-api-key"
```
**Get API Key:** Settings → Management Access → API Keys → Create (select "Viewer" role)
Use the helper script for any query:
```bash
./scripts/unraid-query.sh -q "{ online }"
```
Or run example scripts:
```bash
./scripts/dashboard.sh # Complete multi-server dashboard
./examples/disk-health.sh # Disk temperatures & health
./examples/read-logs.sh syslog 20 # Read system logs
```
## Core Concepts
### GraphQL API Structure
Unraid 7.2+ uses GraphQL (not REST). Key differences:
- **Single endpoint:** `/graphql` for all queries
- **Request exactly what you need:** Specify fields in query
- **Strongly typed:** Use introspection to discover fields
- **No container logs:** Docker container output logs not accessible
### Two Resources for Stats
- **`info`** - Static hardware specs (CPU model, cores, OS version)
- **`metrics`** - Real-time usage (CPU %, memory %, current load)
Always use `metrics` for monitoring, `info` for specifications.
## Common Tasks
### System Monitoring
**Check if server is online:**
```bash
./scripts/unraid-query.sh -q "{ online }"
```
**Get CPU and memory usage:**
```bash
./scripts/unraid-query.sh -q "{ metrics { cpu { percentTotal } memory { used total percentTotal } } }"
```
**Complete dashboard:**
```bash
./scripts/dashboard.sh
```
### Disk Management
**Check disk health and temperatures:**
```bash
./examples/disk-health.sh
```
**Get array status:**
```bash
./scripts/unraid-query.sh -q "{ array { state parityCheckStatus { status progress errors } } }"
```
**List all physical disks (including cache/USB):**
```bash
./scripts/unraid-query.sh -q "{ disks { name } }"
```
### Storage Shares
**List network shares:**
```bash
./scripts/unraid-query.sh -q "{ shares { name comment } }"
```
### Logs
**List available logs:**
```bash
./scripts/unraid-query.sh -q "{ logFiles { name size modifiedAt } }"
```
**Read log content:**
```bash
./examples/read-logs.sh syslog 20
```
### Containers & VMs
**List Docker containers:**
```bash
./scripts/unraid-query.sh -q "{ docker { containers { names image state status } } }"
```
**List VMs:**
```bash
./scripts/unraid-query.sh -q "{ vms { name state cpus memory } } }"
```
**Note:** Container output logs are NOT accessible via API. Use `docker logs` via SSH.
### Notifications
**Get notification counts:**
```bash
./scripts/unraid-query.sh -q "{ notifications { overview { unread { info warning alert total } } } }"
```
## Helper Script Usage
The `scripts/unraid-query.sh` helper supports:
```bash
# Basic usage
./scripts/unraid-query.sh -u URL -k API_KEY -q "QUERY"
# Use environment variables
export UNRAID_URL="https://unraid.local/graphql"
export UNRAID_API_KEY="your-key"
./scripts/unraid-query.sh -q "{ online }"
# Format options
-f json # Raw JSON (default)
-f pretty # Pretty-printed JSON
-f raw # Just the data (no wrapper)
```
## Additional Resources
### Reference Files
For detailed documentation, consult:
- **`references/endpoints.md`** - Complete list of all 27 API endpoints
- **`references/troubleshooting.md`** - Common errors and solutions
- **`references/api-reference.md`** - Detailed field documentation
### Helper Scripts
- **`scripts/unraid-query.sh`** - Main GraphQL query tool
- **`scripts/dashboard.sh`** - Automated multi-server inventory reporter
## Quick Command Reference
```bash
# System status
./scripts/unraid-query.sh -q "{ online metrics { cpu { percentTotal } } }"
# Disk health
./examples/disk-health.sh
# Array status
./scripts/unraid-query.sh -q "{ array { state } }"
# Read logs
./examples/read-logs.sh syslog 20
# Complete dashboard
./scripts/dashboard.sh
# List shares
./scripts/unraid-query.sh -q "{ shares { name } }"
# List containers
./scripts/unraid-query.sh -q "{ docker { containers { names state } } }"
```
---
## 🔧 Agent Tool Usage Requirements
**CRITICAL:** When invoking scripts from this skill via the zsh-tool, **ALWAYS use `pty: true`**.
Without PTY mode, command output will not be visible even though commands execute successfully.
**Correct invocation pattern:**
```typescript
<invoke name="mcp__plugin_zsh-tool_zsh-tool__zsh">
<parameter name="command">./skills/SKILL_NAME/scripts/SCRIPT.sh [args]</parameter>
<parameter name="pty">true</parameter>
</invoke>
```

View File

@@ -0,0 +1,23 @@
#!/bin/bash
# Check disk health and temperatures
# Quick overview of all disks with temperature warnings
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
QUERY_SCRIPT="$SCRIPT_DIR/../scripts/unraid-query.sh"
QUERY='{ array { disks { name device temp status isSpinning } } }'
echo "=== Disk Health Report ==="
echo ""
RESPONSE=$("$QUERY_SCRIPT" -q "$QUERY" -f raw)
echo "$RESPONSE" | jq -r '.array.disks[] | "\(.name) (\(.device)): \(.temp)°C - \(.status) - \(if .isSpinning then "Spinning" else "Spun down" end)"'
echo ""
echo "Temperature warnings:"
echo "$RESPONSE" | jq -r '.array.disks[] | select(.temp > 45) | "⚠️ \(.name): \(.temp)°C (HIGH)"'
HOTTEST=$(echo "$RESPONSE" | jq -r '[.array.disks[].temp] | max')
echo ""
echo "Hottest disk: ${HOTTEST}°C"

View File

@@ -0,0 +1,23 @@
#!/bin/bash
# Read Unraid system logs
# Usage: ./read-logs.sh [log-name] [lines]
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
QUERY_SCRIPT="$SCRIPT_DIR/../scripts/unraid-query.sh"
LOG_NAME="${1:-syslog}"
LINES="${2:-20}"
echo "=== Reading $LOG_NAME (last $LINES lines) ==="
echo ""
QUERY="{ logFile(path: \"$LOG_NAME\", lines: $LINES) { path totalLines startLine content } }"
RESPONSE=$("$QUERY_SCRIPT" -q "$QUERY" -f raw)
echo "$RESPONSE" | jq -r '.logFile.content'
echo ""
echo "---"
echo "Total lines in log: $(echo "$RESPONSE" | jq -r '.logFile.totalLines')"
echo "Showing from line: $(echo "$RESPONSE" | jq -r '.logFile.startLine')"

View File

@@ -0,0 +1,946 @@
# Unraid API - Complete Reference Guide
**Tested on:** Unraid 7.2 x86_64
**Date:** 2026-01-21
**API Type:** GraphQL
**Base URL:** `https://YOUR-UNRAID-SERVER/graphql`
---
## 📊 Summary
Out of 46 total GraphQL query endpoints:
- **✅ 27 fully working read-only endpoints**
- **⚠️ 1 works but returns empty** (`plugins`)
- **❌ 3 return null** (`flash`, `parityHistory`, `services`)
- **❓ 15 untested** (mostly write/mutation operations)
---
## Authentication
All requests require the `x-api-key` header:
```bash
-H "x-api-key: YOUR_API_KEY_HERE"
```
### How to Generate API Key:
1. Log in to Unraid WebGUI
2. Settings → Management Access → API Keys
3. Create API Key with **Viewer** role (read-only)
4. Copy the generated key
---
## 🎯 All 27 Working Read-Only Endpoints
### 1. System Info & Metrics
#### **info** - Hardware Specifications
Get CPU, OS, motherboard, and hardware details.
```bash
curl -s -X POST "https://YOUR-UNRAID/graphql" \
-H "Content-Type: application/json" \
-H "x-api-key: YOUR_API_KEY" \
-d '{
"query": "{ info { time cpu { model cores threads } os { platform distro release arch } system { manufacturer model version uuid } } }"
}' | jq '.'
```
**Response:**
```json
{
"data": {
"info": {
"time": "2026-01-21T12:57:22.539Z",
"cpu": {
"model": "183",
"cores": 16,
"threads": 24
},
"os": {
"platform": "linux",
"distro": "Unraid OS",
"release": "7.2 x86_64",
"arch": "x64"
},
"system": {
"manufacturer": "Micro-Star International Co., Ltd.",
"model": "MS-7E07",
"version": "1.0",
"uuid": "fec05753-077c-8e18-a089-047c1644678a"
}
}
}
}
```
---
#### **metrics** - Real-Time Usage Stats
Get current CPU and memory usage percentages.
```bash
curl -s -X POST "https://YOUR-UNRAID/graphql" \
-H "Content-Type: application/json" \
-H "x-api-key: YOUR_API_KEY" \
-d '{
"query": "{ metrics { cpu { percentTotal } memory { total used free percentTotal swapTotal swapUsed swapFree } } }"
}' | jq '.'
```
**Response:**
```json
{
"data": {
"metrics": {
"cpu": {
"percentTotal": 20.99
},
"memory": {
"total": 134773903360,
"used": 129472622592,
"free": 5301280768,
"percentTotal": 59.97,
"swapTotal": 0,
"swapUsed": 0,
"swapFree": 0
}
}
}
}
```
**Note:** Memory values are in bytes.
---
#### **online** - Server Online Status
Simple boolean check if server is online.
```bash
curl -s -X POST "https://YOUR-UNRAID/graphql" \
-H "Content-Type: application/json" \
-H "x-api-key: YOUR_API_KEY" \
-d '{ "query": "{ online }" }' | jq '.'
```
**Response:**
```json
{
"data": {
"online": true
}
}
```
---
#### **isInitialSetup** - Initial Setup Status
Check if server has completed initial setup.
```bash
curl -s -X POST "https://YOUR-UNRAID/graphql" \
-H "Content-Type: application/json" \
-H "x-api-key: YOUR_API_KEY" \
-d '{ "query": "{ isInitialSetup }" }' | jq '.'
```
**Response:**
```json
{
"data": {
"isInitialSetup": false
}
}
```
---
### 2. Storage & Disks
#### **array** - Array Status & Disks
Get array state, disk details, temperatures, and capacity.
```bash
curl -s -X POST "https://YOUR-UNRAID/graphql" \
-H "Content-Type: application/json" \
-H "x-api-key: YOUR_API_KEY" \
-d '{
"query": "{ array { state disks { id name device size status temp fsSize fsFree fsUsed fsType rotational isSpinning } parityCheckStatus { status progress errors speed } } }"
}' | jq '.'
```
**Response (sample):**
```json
{
"data": {
"array": {
"state": "STARTED",
"disks": [
{
"id": "3cb1026338736ed07b8afec2c484e429710b0f6550dc65d0c5c410ea9d0fa6b2:WDC_WD120EDBZ-11B1HA0_5QGWN5DF",
"name": "disk1",
"device": "sdb",
"size": 11718885324,
"status": "DISK_OK",
"temp": 38,
"fsSize": 11998001574,
"fsFree": 1692508541,
"fsUsed": 10305493033,
"fsType": "xfs",
"rotational": true,
"isSpinning": true
}
],
"parityCheckStatus": {
"status": "NEVER_RUN",
"progress": 0,
"errors": null,
"speed": "0"
}
}
}
}
```
**Note:** Sizes are in kilobytes. Temperature in Celsius.
---
#### **disks** - All Physical Disks
Get ALL disks including array disks, cache SSDs, and boot USB.
```bash
curl -s -X POST "https://YOUR-UNRAID/graphql" \
-H "Content-Type: application/json" \
-H "x-api-key: YOUR_API_KEY" \
-d '{
"query": "{ disks { id name } }"
}' | jq '.'
```
**Response (sample):**
```json
{
"data": {
"disks": [
{
"id": "3cb1026338736ed07b8afec2c484e429710b0f6550dc65d0c5c410ea9d0fa6b2:04009732070823130633",
"name": "Cruzer Glide"
},
{
"id": "3cb1026338736ed07b8afec2c484e429710b0f6550dc65d0c5c410ea9d0fa6b2:5QGWN5DF",
"name": "WDC WD120EDBZ-11B1HA0"
},
{
"id": "3cb1026338736ed07b8afec2c484e429710b0f6550dc65d0c5c410ea9d0fa6b2:S6S2NS0TB18572X",
"name": "Samsung SSD 970 EVO Plus 2TB"
}
]
}
}
```
**Returns:** Array disks + Cache SSDs + Boot USB (17 disks in tested system).
---
#### **shares** - Network Shares
List all user shares with comments.
```bash
curl -s -X POST "https://YOUR-UNRAID/graphql" \
-H "Content-Type: application/json" \
-H "x-api-key: YOUR_API_KEY" \
-d '{
"query": "{ shares { id name comment } }"
}' | jq '.'
```
**Response:**
```json
{
"data": {
"shares": [
{
"id": "3cb1026338736ed07b8afec2c484e429710b0f6550dc65d0c5c410ea9d0fa6b2:appdata",
"name": "appdata",
"comment": "application data"
},
{
"id": "3cb1026338736ed07b8afec2c484e429710b0f6550dc65d0c5c410ea9d0fa6b2:backups",
"name": "backups",
"comment": "primary homelab backup target"
}
]
}
}
```
---
### 3. Virtualization
#### **docker** - Docker Containers
List all Docker containers with status and metadata.
```bash
curl -s -X POST "https://YOUR-UNRAID/graphql" \
-H "Content-Type: application/json" \
-H "x-api-key: YOUR_API_KEY" \
-d '{
"query": "{ docker { containers { id names image state status created autoStart } } }"
}' | jq '.'
```
**Response (when no containers):**
```json
{
"data": {
"docker": {
"containers": []
}
}
}
```
**Note:** Container logs are NOT accessible via this API. Use `docker logs` via SSH.
---
#### **vms** - Virtual Machines
List all VMs with status and resource allocation.
```bash
curl -s -X POST "https://YOUR-UNRAID/graphql" \
-H "Content-Type: application/json" \
-H "x-api-key: YOUR_API_KEY" \
-d '{
"query": "{ vms { id name state cpus memory autostart } }"
}' | jq '.'
```
**Response (when no VMs):**
```json
{
"data": {
"vms": []
}
}
```
---
### 4. Logs & Monitoring
#### **logFiles** - List All Log Files
Get list of all available system log files.
```bash
curl -s -X POST "https://YOUR-UNRAID/graphql" \
-H "Content-Type: application/json" \
-H "x-api-key: YOUR_API_KEY" \
-d '{
"query": "{ logFiles { name size modifiedAt } }"
}' | jq '.'
```
**Response (sample, 32 logs found):**
```json
{
"data": {
"logFiles": [
{
"name": "syslog",
"size": 142567,
"modifiedAt": "2026-01-21T13:00:00.000Z"
},
{
"name": "docker.log",
"size": 66321,
"modifiedAt": "2026-01-05T19:14:53.934Z"
},
{
"name": "dmesg",
"size": 93128,
"modifiedAt": "2025-12-19T11:09:30.200Z"
}
]
}
}
```
---
#### **logFile** - Read Log Content
Read the actual contents of a log file.
```bash
curl -s -X POST "https://YOUR-UNRAID/graphql" \
-H "Content-Type: application/json" \
-H "x-api-key: YOUR_API_KEY" \
-d '{
"query": "query { logFile(path: \"syslog\", lines: 10) { path totalLines startLine content } }"
}' | jq '.'
```
**Response:**
```json
{
"data": {
"logFile": {
"path": "/var/log/syslog",
"totalLines": 1395,
"startLine": 1386,
"content": "Jan 21 07:49:49 unraid-server sshd-session[2992319]: Accepted keyboard-interactive/pam for root from 100.80.181.18 port 49724 ssh2\n..."
}
}
}
```
**Parameters:**
- `path` - Log file name (required)
- `lines` - Number of lines to return (optional, defaults to last 100)
- `startLine` - Line number to start from (optional)
**Available logs include:**
- `syslog` - System log
- `docker.log` - Docker daemon log
- `dmesg` - Kernel messages
- `wtmp` - Login records
- And 28 more...
---
#### **notifications** - System Alerts
Get system notifications and alerts.
**Get notification counts:**
```bash
curl -s -X POST "https://YOUR-UNRAID/graphql" \
-H "Content-Type: application/json" \
-H "x-api-key: YOUR_API_KEY" \
-d '{
"query": "{ notifications { overview { unread { info warning alert total } archive { info warning alert total } } } }"
}' | jq '.'
```
**Response:**
```json
{
"data": {
"notifications": {
"overview": {
"unread": {
"info": 66,
"warning": 0,
"alert": 0,
"total": 66
},
"archive": {
"info": 581,
"warning": 4,
"alert": 1,
"total": 586
}
}
}
}
}
```
**List unread notifications:**
```bash
curl -s -X POST "https://YOUR-UNRAID/graphql" \
-H "Content-Type: application/json" \
-H "x-api-key: YOUR_API_KEY" \
-d '{
"query": "{ notifications { list(filter: { type: UNREAD, offset: 0, limit: 10 }) { id subject description timestamp } } }"
}' | jq '.'
```
**Response (sample):**
```json
{
"data": {
"notifications": {
"list": [
{
"id": "...",
"subject": "Backup Notification",
"description": "ZFS replication was successful...",
"timestamp": "2026-01-21T09:10:40.000Z"
}
]
}
}
}
```
**Parameters for list query:**
- `type` - `UNREAD` or `ARCHIVE` (required)
- `offset` - Starting index (required, use 0 for first page)
- `limit` - Number of results (required, max typically 100)
- `importance` - Filter by `INFO`, `WARNING`, or `ALERT` (optional)
---
### 5. UPS & Power
#### **upsDevices** - UPS Status
Get UPS battery backup status (if configured).
```bash
curl -s -X POST "https://YOUR-UNRAID/graphql" \
-H "Content-Type: application/json" \
-H "x-api-key: YOUR_API_KEY" \
-d '{
"query": "{ upsDevices { id name status charge load runtime } }"
}' | jq '.'
```
**Response (when no UPS):**
```json
{
"data": {
"upsDevices": []
}
}
```
---
### 6. User & Authentication
#### **me** - Current User Info
Get information about the current authenticated user.
```bash
curl -s -X POST "https://YOUR-UNRAID/graphql" \
-H "Content-Type: application/json" \
-H "x-api-key: YOUR_API_KEY" \
-d '{
"query": "{ me { id } }"
}' | jq '.'
```
---
#### **owner** - Server Owner
Get server owner information.
```bash
curl -s -X POST "https://YOUR-UNRAID/graphql" \
-H "Content-Type: application/json" \
-H "x-api-key: YOUR_API_KEY" \
-d '{
"query": "{ owner { username url avatar } }"
}' | jq '.'
```
**Response:**
```json
{
"data": {
"owner": {
"username": "root",
"url": "",
"avatar": ""
}
}
}
```
---
#### **isSSOEnabled** - SSO Status
Check if Single Sign-On is enabled.
```bash
curl -s -X POST "https://YOUR-UNRAID/graphql" \
-H "Content-Type: application/json" \
-H "x-api-key: YOUR_API_KEY" \
-d '{ "query": "{ isSSOEnabled }" }' | jq '.'
```
**Response:**
```json
{
"data": {
"isSSOEnabled": true
}
}
```
---
#### **oidcProviders** - OIDC Providers
List configured OpenID Connect providers.
```bash
curl -s -X POST "https://YOUR-UNRAID/graphql" \
-H "Content-Type: application/json" \
-H "x-api-key: YOUR_API_KEY" \
-d '{
"query": "{ oidcProviders { id } }"
}' | jq '.'
```
---
### 7. API Keys & Access
#### **apiKeys** - List API Keys
Get list of all API keys (requires appropriate permissions).
```bash
curl -s -X POST "https://YOUR-UNRAID/graphql" \
-H "Content-Type: application/json" \
-H "x-api-key: YOUR_API_KEY" \
-d '{
"query": "{ apiKeys { id name createdAt } }"
}' | jq '.'
```
**Response (sample, 4 keys found):**
```json
{
"data": {
"apiKeys": [
{
"id": "key1",
"name": "monitoring",
"createdAt": "2026-01-01T00:00:00.000Z"
}
]
}
}
```
---
### 8. Configuration & Settings
#### **config** - System Configuration
Get system configuration details.
```bash
curl -s -X POST "https://YOUR-UNRAID/graphql" \
-H "Content-Type: application/json" \
-H "x-api-key: YOUR_API_KEY" \
-d '{
"query": "{ config { id } }"
}' | jq '.'
```
---
#### **settings** - System Settings
Get system settings.
```bash
curl -s -X POST "https://YOUR-UNRAID/graphql" \
-H "Content-Type: application/json" \
-H "x-api-key: YOUR_API_KEY" \
-d '{
"query": "{ settings { id } }"
}' | jq '.'
```
---
#### **vars** - System Variables
Get system variables.
```bash
curl -s -X POST "https://YOUR-UNRAID/graphql" \
-H "Content-Type: application/json" \
-H "x-api-key: YOUR_API_KEY" \
-d '{
"query": "{ vars { id } }"
}' | jq '.'
```
---
### 9. Customization & Theming
#### **customization** - UI Customization
Get UI theme and customization settings.
```bash
curl -s -X POST "https://YOUR-UNRAID/graphql" \
-H "Content-Type: application/json" \
-H "x-api-key: YOUR_API_KEY" \
-d '{
"query": "{ customization { theme { name headerBackgroundColor headerPrimaryTextColor showBannerImage showBannerGradient } } }"
}' | jq '.'
```
**Response:**
```json
{
"data": {
"customization": {
"theme": {
"name": "white",
"headerBackgroundColor": "#2e3440",
"headerPrimaryTextColor": "#FFF",
"showBannerImage": false,
"showBannerGradient": false
}
}
}
}
```
---
#### **publicTheme** - Public Theme Settings
Get public-facing theme settings.
```bash
curl -s -X POST "https://YOUR-UNRAID/graphql" \
-H "Content-Type: application/json" \
-H "x-api-key: YOUR_API_KEY" \
-d '{
"query": "{ publicTheme { name showBannerImage showBannerGradient headerBackgroundColor headerPrimaryTextColor headerSecondaryTextColor } }"
}' | jq '.'
```
**Response:**
```json
{
"data": {
"publicTheme": {
"name": "white",
"showBannerImage": false,
"showBannerGradient": false,
"headerBackgroundColor": "#2e3440",
"headerPrimaryTextColor": "#FFF",
"headerSecondaryTextColor": "#fff"
}
}
}
```
---
#### **publicPartnerInfo** - Partner/OEM Branding
Get partner or OEM branding information.
```bash
curl -s -X POST "https://YOUR-UNRAID/graphql" \
-H "Content-Type: application/json" \
-H "x-api-key: YOUR_API_KEY" \
-d '{
"query": "{ publicPartnerInfo { partnerName partnerUrl partnerLogoUrl hasPartnerLogo } }"
}' | jq '.'
```
**Response:**
```json
{
"data": {
"publicPartnerInfo": {
"partnerName": null,
"partnerUrl": null,
"partnerLogoUrl": "/webGui/images/UN-logotype-gradient.svg",
"hasPartnerLogo": false
}
}
}
```
---
### 10. Server Management
#### **registration** - License Info
Get Unraid license/registration information.
```bash
curl -s -X POST "https://YOUR-UNRAID/graphql" \
-H "Content-Type: application/json" \
-H "x-api-key: YOUR_API_KEY" \
-d '{
"query": "{ registration { id } }"
}' | jq '.'
```
---
#### **server** - Server Metadata
Get server metadata.
```bash
curl -s -X POST "https://YOUR-UNRAID/graphql" \
-H "Content-Type: application/json" \
-H "x-api-key: YOUR_API_KEY" \
-d '{
"query": "{ server { id } }"
}' | jq '.'
```
---
#### **servers** - Multi-Server Management
Get list of servers (for multi-server setups).
```bash
curl -s -X POST "https://YOUR-UNRAID/graphql" \
-H "Content-Type: application/json" \
-H "x-api-key: YOUR_API_KEY" \
-d '{
"query": "{ servers { id } }"
}' | jq '.'
```
---
### 11. Plugins
#### **plugins** - Installed Plugins
List installed plugins.
```bash
curl -s -X POST "https://YOUR-UNRAID/graphql" \
-H "Content-Type: application/json" \
-H "x-api-key: YOUR_API_KEY" \
-d '{
"query": "{ plugins { name version author description } }"
}' | jq '.'
```
**Response (when no plugins):**
```json
{
"data": {
"plugins": []
}
}
```
---
## 🎯 Complete Dashboard Query
Get everything useful in a single query:
```bash
curl -s -X POST "https://YOUR-UNRAID/graphql" \
-H "Content-Type: application/json" \
-H "x-api-key: YOUR_API_KEY" \
-d '{
"query": "query Dashboard {
info {
time
cpu { model cores threads }
os { distro release }
system { manufacturer model }
}
metrics {
cpu { percentTotal }
memory { total used free percentTotal }
}
array {
state
disks { name device temp status fsSize fsFree fsUsed isSpinning }
parityCheckStatus { status progress errors }
}
shares { name comment }
online
isSSOEnabled
}"
}' | jq '.'
```
---
## ❌ Endpoints That Return Null
These queries exist but return `null` in Unraid 7.2:
1. **`flash`** - Boot USB drive info (returns `null`)
2. **`parityHistory`** - Historical parity checks (returns `null` - use `array.parityCheckStatus` instead)
3. **`services`** - System services (returns `null`)
---
## 🔍 Schema Discovery
### Discover Available Fields for a Type
```bash
curl -s -X POST "https://YOUR-UNRAID/graphql" \
-H "Content-Type: application/json" \
-H "x-api-key: YOUR_API_KEY" \
-d '{
"query": "{ __type(name: \"Info\") { fields { name type { name } } } }"
}' | jq -r '.data.__type.fields[] | "\(.name): \(.type.name)"'
```
### List All Available Queries
```bash
curl -s -X POST "https://YOUR-UNRAID/graphql" \
-H "Content-Type: application/json" \
-H "x-api-key: YOUR_API_KEY" \
-d '{
"query": "{ __type(name: \"Query\") { fields { name } } }"
}' | jq -r '.data.__type.fields[].name' | sort
```
---
## 📝 Field Name Reference
Common differences from online documentation:
| Online Docs | Actual Unraid 7.2 Field |
|------------|------------------------|
| `uptime` | `time` |
| `cpu.usage` | `metrics.cpu.percentTotal` |
| `memory.usage` | `metrics.memory.percentTotal` |
| `array.status` | `array.state` |
| `disk.temperature` | `disk.temp` |
| `percentUsed` | `percentTotal` |
---
## ⚡ Best Practices
1. **Use `metrics` for real-time stats** - CPU/memory usage is in `metrics`, not `info`
2. **Use `array.disks` for array disks** - The top-level `disks` query includes ALL disks (USB, SSDs, etc.)
3. **Always check errors** - GraphQL returns errors in `errors` array
4. **Use introspection** - Field names can vary between versions
5. **Sizes are in kilobytes** - Disk sizes and capacities are in KB, not bytes
6. **Temperature is Celsius** - All temperature values are in Celsius
7. **Handle empty arrays** - Many queries return `[]` when no data exists
8. **Use viewer role** - Create API keys with "Viewer" role for read-only access
---
## 🚫 Known Limitations
1. **No Docker container logs** - Container output logs are NOT accessible via API
2. **No real-time streaming** - All queries are request/response, no WebSocket subscriptions
3. **Some queries require higher permissions** - Read-only "Viewer" role may not access all queries
4. **No mutation examples included** - This guide covers read-only queries only
---
## 📚 Additional Resources
- **Unraid Docs:** https://docs.unraid.net/
- **GraphQL Spec:** https://graphql.org/
- **GraphQL Introspection:** Use `__schema` and `__type` queries to explore the API
---
**Last Updated:** 2026-01-21
**API Version:** Unraid 7.2 GraphQL API
**Total Working Endpoints:** 27 of 46

View File

@@ -0,0 +1,49 @@
# Unraid API Endpoints Reference
Complete list of available GraphQL read-only endpoints in Unraid 7.2+.
## System & Metrics (8)
1. **`info`** - Hardware specs (CPU, OS, motherboard)
2. **`metrics`** - Real-time CPU/memory usage
3. **`online`** - Server online status
4. **`isInitialSetup`** - Setup completion status
5. **`config`** - System configuration
6. **`vars`** - System variables
7. **`settings`** - System settings
8. **`logFiles`** - List all log files
## Storage (4)
9. **`array`** - Array status, disks, parity
10. **`disks`** - All physical disks (array + cache + USB)
11. **`shares`** - Network shares
12. **`logFile`** - Read log content
## Virtualization (2)
13. **`docker`** - Docker containers
14. **`vms`** - Virtual machines
## Monitoring (2)
15. **`notifications`** - System alerts
16. **`upsDevices`** - UPS battery status
## User & Auth (4)
17. **`me`** - Current user info
18. **`owner`** - Server owner
19. **`isSSOEnabled`** - SSO status
20. **`oidcProviders`** - OIDC providers
## API Management (2)
21. **`apiKeys`** - List API keys
## Customization (3)
22. **`customization`** - UI theme & settings
23. **`publicTheme`** - Public theme
24. **`publicPartnerInfo`** - Partner branding
## Server Management (3)
25. **`registration`** - License info
26. **`server`** - Server metadata
27. **`servers`** - Multi-server management
## Bonus (1)
28. **`plugins`** - Installed plugins (returns empty array if none)

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,219 @@
# Unraid API Quick Reference
Quick reference for the most common Unraid GraphQL API queries.
## Setup
```bash
# Set environment variables
export UNRAID_URL="https://your-unraid-server/graphql"
export UNRAID_API_KEY="your-api-key-here"
# Or use the helper script directly
./scripts/unraid-query.sh -u "$UNRAID_URL" -k "$API_KEY" -q "{ online }"
```
## Common Queries
### System Status
```graphql
{
online
metrics {
cpu { percentTotal }
memory { total used free percentTotal }
}
}
```
### Array Status
```graphql
{
array {
state
parityCheckStatus { status progress errors }
}
}
```
### Disk List with Temperatures
```graphql
{
array {
disks {
name
device
temp
status
fsSize
fsFree
isSpinning
}
}
}
```
### All Physical Disks (including USB/SSDs)
```graphql
{
disks {
id
name
}
}
```
### Network Shares
```graphql
{
shares {
name
comment
}
}
```
### Docker Containers
```graphql
{
docker {
containers {
id
names
image
state
status
}
}
}
```
### Virtual Machines
```graphql
{
vms {
id
name
state
cpus
memory
}
}
```
### List Log Files
```graphql
{
logFiles {
name
size
modifiedAt
}
}
```
### Read Log Content
```graphql
{
logFile(path: "syslog", lines: 20) {
content
totalLines
}
}
```
### System Info
```graphql
{
info {
time
cpu { model cores threads }
os { distro release }
system { manufacturer model }
}
}
```
### UPS Devices
```graphql
{
upsDevices {
id
name
status
charge
load
}
}
```
### Notifications
**Counts:**
```graphql
{
notifications {
overview {
unread { info warning alert total }
archive { info warning alert total }
}
}
}
```
**List Unread:**
```graphql
{
notifications {
list(filter: { type: UNREAD, offset: 0, limit: 10 }) {
id
subject
description
timestamp
}
}
}
```
**List Archived:**
```graphql
{
notifications {
list(filter: { type: ARCHIVE, offset: 0, limit: 10 }) {
id
subject
description
timestamp
}
}
}
```
## Field Name Notes
- Use `metrics` for real-time usage (CPU/memory percentages)
- Use `info` for hardware specs (cores, model, etc.)
- Temperature field is `temp` (not `temperature`)
- Status field is `state` for array (not `status`)
- Sizes are in kilobytes
- Temperatures are in Celsius
## Response Structure
All responses follow this pattern:
```json
{
"data": {
"queryName": { ... }
}
}
```
Errors appear in:
```json
{
"errors": [
{ "message": "..." }
]
}
```

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,34 @@
# Unraid API Troubleshooting Guide
Common issues and solutions when working with the Unraid GraphQL API.
## "Cannot query field" error
Field name doesn't exist in your Unraid version. Use introspection to find valid fields:
```bash
./scripts/unraid-query.sh -q "{ __type(name: \"TypeName\") { fields { name } } }"
```
## "API key validation failed"
- Check API key is correct and not truncated
- Verify key has appropriate permissions (use "Viewer" role)
- Ensure URL includes `/graphql` endpoint (e.g. `http://host/graphql`)
## Empty results
Many queries return empty arrays when no data exists:
- `docker.containers` - No containers running
- `vms` - No VMs configured (or VM service disabled)
- `notifications` - No active alerts
- `plugins` - No plugins installed
This is normal behavior, not an error. Ensure your scripts handle empty arrays gracefully.
## "VMs are not available" (GraphQL Error)
If the VM manager is disabled in Unraid settings, querying `{ vms { ... } }` will return a GraphQL error.
**Solution:** Check if VM service is enabled before querying, or use error handling (like `IGNORE_ERRORS=true` in dashboard scripts) to process partial data.
## URL connection issues
- Use HTTPS (not HTTP) for remote access if configured
- For local access: `http://unraid-server-ip/graphql`
- For Unraid Connect: Use provided URL with token in hostname
- Use `-k` (insecure) with curl if using self-signed certs on local HTTPS
- Use `-L` (follow redirects) if Unraid redirects HTTP to HTTPS

View File

@@ -0,0 +1,214 @@
#!/bin/bash
# Complete Unraid Monitoring Dashboard (Multi-Server)
# Gets system status, disk health, and resource usage for all configured servers
set -euo pipefail
SCRIPT_DIR="$(cd -P "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
REPO_ROOT="$(cd "$SCRIPT_DIR/../../.." && pwd)"
source "$REPO_ROOT/lib/load-env.sh"
QUERY_SCRIPT="$SCRIPT_DIR/unraid-query.sh"
OUTPUT_FILE="$HOME/memory/bank/unraid-inventory.md"
# Load credentials from .env for all servers
load_env_file || exit 1
for server in "TOOTIE" "SHART"; do
url_var="UNRAID_${server}_URL"
key_var="UNRAID_${server}_API_KEY"
name_var="UNRAID_${server}_NAME"
validate_env_vars "$url_var" "$key_var" || exit 1
done
# Ensure output directory exists
mkdir -p "$(dirname "$OUTPUT_FILE")"
# Start the report
echo "# Unraid Fleet Dashboard" > "$OUTPUT_FILE"
echo "Generated at: $(date)" >> "$OUTPUT_FILE"
echo "" >> "$OUTPUT_FILE"
# Function to process a single server
process_server() {
local NAME="$1"
local URL="$2"
local API_KEY="$3"
echo "Querying server: $NAME..."
export UNRAID_URL="$URL"
export UNRAID_API_KEY="$API_KEY"
export IGNORE_ERRORS="true"
QUERY='query Dashboard {
info {
time
cpu { model cores threads }
os { platform distro release arch }
system { manufacturer model version uuid }
}
metrics {
cpu { percentTotal }
memory { total used free percentTotal }
}
array {
state
capacity { kilobytes { total free used } }
disks { name device temp status fsSize fsFree fsUsed isSpinning numErrors }
caches { name device temp status fsSize fsFree fsUsed fsType type }
parityCheckStatus { status progress errors }
}
disks { id name device size status temp numErrors }
shares { name comment free }
docker {
containers { names image state status }
}
vms { domains { id name state } }
vars { timeZone regTy regTo }
notifications { id title subject description importance timestamp }
recentLog: logFile(path: \"syslog\", lines: 50) { content }
online
isSSOEnabled
}'
RESPONSE=$("$QUERY_SCRIPT" -q "$QUERY" -f json)
# Debug output
echo "$RESPONSE" > "${NAME}_debug.json"
# Check if response is valid JSON
if ! echo "$RESPONSE" | jq -e . >/dev/null 2>&1; then
echo "Error querying $NAME: Invalid response"
echo "Response saved to ${NAME}_debug.json"
echo "## Server: $NAME (⚠️ Error)" >> "$OUTPUT_FILE"
echo "Failed to retrieve data." >> "$OUTPUT_FILE"
return
fi
# Append to report
echo "## Server: $NAME" >> "$OUTPUT_FILE"
# System Info
CPU_MODEL=$(echo "$RESPONSE" | jq -r '.data.info.cpu.model')
CPU_CORES=$(echo "$RESPONSE" | jq -r '.data.info.cpu.cores')
CPU_THREADS=$(echo "$RESPONSE" | jq -r '.data.info.cpu.threads')
OS_REL=$(echo "$RESPONSE" | jq -r '.data.info.os.release')
OS_ARCH=$(echo "$RESPONSE" | jq -r '.data.info.os.arch // "x64"')
SYS_MFG=$(echo "$RESPONSE" | jq -r '.data.info.system.manufacturer // "Unknown"')
SYS_MODEL=$(echo "$RESPONSE" | jq -r '.data.info.system.model // "Unknown"')
TIMEZONE=$(echo "$RESPONSE" | jq -r '.data.vars.timeZone // "N/A"')
LICENSE=$(echo "$RESPONSE" | jq -r '.data.vars.regTy // "Unknown"')
REG_TO=$(echo "$RESPONSE" | jq -r '.data.vars.regTo // "N/A"')
CPU_LOAD=$(echo "$RESPONSE" | jq -r '.data.metrics.cpu.percentTotal // 0')
TOTAL_MEM=$(echo "$RESPONSE" | jq -r '.data.metrics.memory.total // 0')
MEM_USED_PCT=$(echo "$RESPONSE" | jq -r '.data.metrics.memory.percentTotal // 0')
TOTAL_MEM_GB=$((TOTAL_MEM / 1024 / 1024 / 1024))
echo "### System" >> "$OUTPUT_FILE"
echo "- **Hardware:** $SYS_MFG $SYS_MODEL" >> "$OUTPUT_FILE"
echo "- **OS:** Unraid $OS_REL ($OS_ARCH)" >> "$OUTPUT_FILE"
echo "- **License:** $LICENSE (Registered to: $REG_TO)" >> "$OUTPUT_FILE"
echo "- **Timezone:** $TIMEZONE" >> "$OUTPUT_FILE"
echo "- **CPU:** Model $CPU_MODEL ($CPU_CORES cores / $CPU_THREADS threads) - **${CPU_LOAD}% load**" >> "$OUTPUT_FILE"
echo "- **Memory:** ${TOTAL_MEM_GB}GB - **${MEM_USED_PCT}% used**" >> "$OUTPUT_FILE"
echo "" >> "$OUTPUT_FILE"
# Array capacity
ARRAY_TOTAL=$(echo "$RESPONSE" | jq -r '.data.array.capacity.kilobytes.total')
ARRAY_FREE=$(echo "$RESPONSE" | jq -r '.data.array.capacity.kilobytes.free')
ARRAY_USED=$(echo "$RESPONSE" | jq -r '.data.array.capacity.kilobytes.used')
if [ "$ARRAY_TOTAL" != "null" ] && [ "$ARRAY_TOTAL" -gt 0 ]; then
ARRAY_TOTAL_GB=$((ARRAY_TOTAL / 1024 / 1024))
ARRAY_FREE_GB=$((ARRAY_FREE / 1024 / 1024))
ARRAY_USED_GB=$((ARRAY_USED / 1024 / 1024))
ARRAY_USED_PCT=$((ARRAY_USED * 100 / ARRAY_TOTAL))
echo "### Storage" >> "$OUTPUT_FILE"
echo "- **Array:** ${ARRAY_USED_GB}GB / ${ARRAY_TOTAL_GB}GB used (${ARRAY_USED_PCT}%)" >> "$OUTPUT_FILE"
fi
# Cache pools
echo "- **Cache Pools:**" >> "$OUTPUT_FILE"
echo "$RESPONSE" | jq -r '.data.array.caches[] | " - \(.name) (\(.device)): \(.temp)°C - \(.status) - \(if .fsSize then "\((.fsUsed / 1024 / 1024 | floor))GB / \((.fsSize / 1024 / 1024 | floor))GB used" else "N/A" end)"' >> "$OUTPUT_FILE"
# Docker
TOTAL_CONTAINERS=$(echo "$RESPONSE" | jq '[.data.docker.containers[]] | length')
RUNNING_CONTAINERS=$(echo "$RESPONSE" | jq '[.data.docker.containers[] | select(.state == "RUNNING")] | length')
echo "" >> "$OUTPUT_FILE"
echo "### Workloads" >> "$OUTPUT_FILE"
echo "- **Docker:** ${TOTAL_CONTAINERS} containers (${RUNNING_CONTAINERS} running)" >> "$OUTPUT_FILE"
# Unhealthy containers
UNHEALTHY=$(echo "$RESPONSE" | jq -r '.data.docker.containers[] | select(.status | test("unhealthy|restarting"; "i")) | " - ⚠️ \(.names[0]): \(.status)"')
if [ -n "$UNHEALTHY" ]; then
echo "$UNHEALTHY" >> "$OUTPUT_FILE"
fi
# VMs
if [ "$(echo "$RESPONSE" | jq -r '.data.vms.domains')" != "null" ]; then
TOTAL_VMS=$(echo "$RESPONSE" | jq '[.data.vms.domains[]] | length')
RUNNING_VMS=$(echo "$RESPONSE" | jq '[.data.vms.domains[] | select(.state == "RUNNING")] | length')
echo "- **VMs:** ${TOTAL_VMS} VMs (${RUNNING_VMS} running)" >> "$OUTPUT_FILE"
else
echo "- **VMs:** Service disabled or no data" >> "$OUTPUT_FILE"
fi
# Disk Health
echo "" >> "$OUTPUT_FILE"
echo "### Health" >> "$OUTPUT_FILE"
HOT_DISKS=$(echo "$RESPONSE" | jq -r '.data.array.disks[] | select(.temp > 45) | "- ⚠️ \(.name): \(.temp)°C (HIGH)"')
DISK_ERRORS=$(echo "$RESPONSE" | jq -r '.data.array.disks[] | select(.numErrors > 0) | "- ❌ \(.name): \(.numErrors) errors"')
if [ -z "$HOT_DISKS" ] && [ -z "$DISK_ERRORS" ]; then
echo "- ✅ All disks healthy" >> "$OUTPUT_FILE"
else
[ -n "$HOT_DISKS" ] && echo "$HOT_DISKS" >> "$OUTPUT_FILE"
[ -n "$DISK_ERRORS" ] && echo "$DISK_ERRORS" >> "$OUTPUT_FILE"
fi
# Notifications (Alerts)
echo "" >> "$OUTPUT_FILE"
echo "### Notifications" >> "$OUTPUT_FILE"
NOTIF_COUNT=$(echo "$RESPONSE" | jq '[.data.notifications[]] | length' 2>/dev/null || echo "0")
if [ "$NOTIF_COUNT" -gt 0 ] && [ "$NOTIF_COUNT" != "null" ]; then
# Show recent notifications (last 10)
ALERT_NOTIFS=$(echo "$RESPONSE" | jq -r '.data.notifications | sort_by(.timestamp) | reverse | .[0:10][] | "- [\(.importance // "info")] \(.title // .subject): \(.description // "No description") (\(.timestamp | split("T")[0]))"' 2>/dev/null)
if [ -n "$ALERT_NOTIFS" ]; then
echo "$ALERT_NOTIFS" >> "$OUTPUT_FILE"
else
echo "- ✅ No recent notifications" >> "$OUTPUT_FILE"
fi
# Count by importance
ALERT_COUNT=$(echo "$RESPONSE" | jq '[.data.notifications[] | select(.importance == "alert" or .importance == "warning")] | length' 2>/dev/null || echo "0")
if [ "$ALERT_COUNT" -gt 0 ]; then
echo "" >> "$OUTPUT_FILE"
echo "**⚠️ $ALERT_COUNT alert/warning notifications**" >> "$OUTPUT_FILE"
fi
else
echo "- ✅ No notifications" >> "$OUTPUT_FILE"
fi
echo "" >> "$OUTPUT_FILE"
echo "---" >> "$OUTPUT_FILE"
echo "" >> "$OUTPUT_FILE"
}
# Main loop - process each server from environment variables
for server in "TOOTIE" "SHART"; do
name_var="UNRAID_${server}_NAME"
url_var="UNRAID_${server}_URL"
key_var="UNRAID_${server}_API_KEY"
NAME="${!name_var}"
URL="${!url_var}"
KEY="${!key_var}"
process_server "$NAME" "$URL" "$KEY"
done
echo "Dashboard saved to: $OUTPUT_FILE"
cat "$OUTPUT_FILE"

View File

@@ -0,0 +1,126 @@
#!/bin/bash
# Unraid GraphQL API Query Helper
# Makes it easy to query the Unraid API from the command line
set -e
# Usage function
usage() {
cat << EOF
Usage: $0 [OPTIONS]
Query the Unraid GraphQL API
OPTIONS:
-u, --url URL Unraid server URL (required)
-k, --key KEY API key (required)
-q, --query QUERY GraphQL query (required)
-f, --format FORMAT Output format: json (default), raw, pretty
-h, --help Show this help message
ENVIRONMENT VARIABLES:
UNRAID_URL Default Unraid server URL
UNRAID_API_KEY Default API key
EXAMPLES:
# Get system status
$0 -u https://unraid.local/graphql -k YOUR_KEY -q "{ online }"
# Use environment variables
export UNRAID_URL="https://unraid.local/graphql"
export UNRAID_API_KEY="your-api-key"
$0 -q "{ metrics { cpu { percentTotal } } }"
# Pretty print output
$0 -q "{ array { state } }" -f pretty
EOF
exit 1
}
# Default values
URL="${UNRAID_URL:-}"
API_KEY="${UNRAID_API_KEY:-}"
QUERY=""
FORMAT="json"
# Parse arguments
while [[ $# -gt 0 ]]; do
case $1 in
-u|--url)
URL="$2"
shift 2
;;
-k|--key)
API_KEY="$2"
shift 2
;;
-q|--query)
QUERY="$2"
shift 2
;;
-f|--format)
FORMAT="$2"
shift 2
;;
-h|--help)
usage
;;
*)
echo "Unknown option: $1"
usage
;;
esac
done
# Validate required arguments
if [[ -z "$URL" ]]; then
echo "Error: Unraid URL is required (use -u or set UNRAID_URL)"
exit 1
fi
if [[ -z "$API_KEY" ]]; then
echo "Error: API key is required (use -k or set UNRAID_API_KEY)"
exit 1
fi
if [[ -z "$QUERY" ]]; then
echo "Error: GraphQL query is required (use -q)"
exit 1
fi
# Make the request
RESPONSE=$(curl -skL -X POST "$URL" \
-H "Content-Type: application/json" \
-H "x-api-key: $API_KEY" \
-d "{\"query\":\"$QUERY\"}")
# Check for errors
if echo "$RESPONSE" | jq -e '.errors' > /dev/null 2>&1; then
# If we have data despite errors, and --ignore-errors is set, continue
if [[ "$IGNORE_ERRORS" == "true" ]] && echo "$RESPONSE" | jq -e '.data' > /dev/null 2>&1; then
echo "GraphQL Warning:" >&2
echo "$RESPONSE" | jq -r '.errors[0].message' >&2
else
echo "GraphQL Error:" >&2
echo "$RESPONSE" | jq -r '.errors[0].message' >&2
exit 1
fi
fi
# Output based on format
case "$FORMAT" in
json)
echo "$RESPONSE"
;;
raw)
echo "$RESPONSE" | jq -r '.data'
;;
pretty)
echo "$RESPONSE" | jq '.'
;;
*)
echo "Unknown format: $FORMAT" >&2
exit 1
;;
esac

39
skills/unraid/setup.sh Executable file
View File

@@ -0,0 +1,39 @@
#!/usr/bin/env bash
# Setup script for Unraid MCP Plugin
# Installs the MCP server dependencies
set -euo pipefail
PLUGIN_ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
PROJECT_ROOT="$(cd "$PLUGIN_ROOT/../.." && pwd)"
echo "=== Unraid MCP Plugin Setup ==="
echo ""
echo "Plugin root: $PLUGIN_ROOT"
echo "Project root: $PROJECT_ROOT"
echo ""
# Check if uv is installed
if ! command -v uv &> /dev/null; then
echo "Error: 'uv' is not installed."
echo "Install it with: curl -LsSf https://astral.sh/uv/install.sh | sh"
exit 1
fi
echo "✓ uv is installed"
# Navigate to project root and install dependencies
cd "$PROJECT_ROOT"
echo "Installing Python dependencies..."
uv sync
echo ""
echo "✓ Setup complete!"
echo ""
echo "Configure your Unraid server by setting these environment variables:"
echo " export UNRAID_API_URL='http://your-unraid-server/graphql'"
echo " export UNRAID_API_KEY='your-api-key'"
echo ""
echo "Test the MCP server with:"
echo " uv run unraid-mcp-server"