fix: address all 17 PR review comments

Resolves review threads:
- PRRT_kwDOO6Hdxs50fewG (setup.py): non-eliciting clients now return True
  from elicit_reset_confirmation so they can reconfigure without being blocked
- PRRT_kwDOO6Hdxs50fewM (test-tools.sh): add notification/recalculate smoke test
- PRRT_kwDOO6Hdxs50fewP (test-tools.sh): add system/array smoke test
- PRRT_kwDOO6Hdxs50fewT (resources.py): surface manager error state instead of
  reporting 'connecting' for permanently failed subscriptions
- PRRT_kwDOO6Hdxs50feAj (resources.py): use is not None check for empty cached dicts
- PRRT_kwDOO6Hdxs50fewY (integration tests): remove duplicate snapshot-registration
  tests already covered in test_resources.py
- PRRT_kwDOO6Hdxs50fewe (test_resources.py): replace brittle import-detail test
  with behavior tests for connecting/error states
- PRRT_kwDOO6Hdxs50fewh (test_customization.py): strengthen public_theme assertion
- PRRT_kwDOO6Hdxs50fewk (test_customization.py): strengthen theme assertion
- PRRT_kwDOO6Hdxs50fewo (__init__.py): correct subaction count ~88 -> ~107
- PRRT_kwDOO6Hdxs50fewx (test_oidc.py): assert providers list value directly
- PRRT_kwDOO6Hdxs50fewz (unraid.py): remove unreachable raise after vm handler
- PRRT_kwDOO6Hdxs50few2 (unraid.py): remove unreachable raise after docker handler
- PRRT_kwDOO6Hdxs50fev8 (CLAUDE.md): replace legacy 15-tool table with unified
  unraid action/subaction table
- PRRT_kwDOO6Hdxs50fev_ (test_oidc.py): assert providers + defaultAllowedOrigins
- PRRT_kwDOO6Hdxs50feAz (CLAUDE.md): update tool categories to unified API shape
- PRRT_kwDOO6Hdxs50feBE (CLAUDE.md/setup.py): update unraid_health refs to
  unraid(action=health, subaction=setup)
This commit is contained in:
Jacob Magar
2026-03-16 02:58:54 -04:00
parent dab1cd6995
commit efaab031ae
39 changed files with 844 additions and 1225 deletions

View File

@@ -1,3 +1,5 @@
> **⚠️ DEVELOPER REFERENCE ONLY** — This file documents the raw GraphQL API schema for development and maintenance purposes (adding new queries/mutations). Do NOT use these curl/GraphQL examples for MCP tool usage. Use `unraid(action=..., subaction=...)` calls instead. See `SKILL.md` for the correct calling convention.
# Unraid API - Complete Reference Guide
**Tested on:** Unraid 7.2 x86_64

View File

@@ -1,3 +1,5 @@
> **⚠️ DEVELOPER REFERENCE ONLY** — This file documents raw GraphQL endpoints for development purposes. For MCP tool usage, use `unraid(action=..., subaction=...)` calls as documented in `SKILL.md`.
# Unraid API Endpoints Reference
Complete list of available GraphQL read-only endpoints in Unraid 7.2+.

View File

@@ -1,3 +1,5 @@
> **⚠️ DEVELOPER REFERENCE ONLY** — Full GraphQL SDL from live API introspection. Use this to verify field names and types when adding new queries/mutations to the MCP server. Not for runtime agent usage.
"""
Indicates exactly one field must be supplied and this field must not be `null`.
"""

View File

@@ -1,219 +1,115 @@
# Unraid API Quick Reference
# Unraid MCP — Quick Reference
Quick reference for the most common Unraid GraphQL API queries.
All operations use: `unraid(action="<domain>", subaction="<operation>", [params])`
## Setup
## Most Common Operations
```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 "$UNRAID_API_KEY" -q "{ online }"
### Health & Status
```
unraid(action="health", subaction="setup") # First-time credential setup
unraid(action="health", subaction="check") # Full health check
unraid(action="health", subaction="test_connection") # Quick connectivity test
unraid(action="system", subaction="overview") # Complete server summary
unraid(action="system", subaction="metrics") # CPU / RAM / I/O usage
unraid(action="system", subaction="online") # Online status
```
## Common Queries
### System Status
```graphql
{
online
metrics {
cpu { percentTotal }
memory { total used free percentTotal }
}
}
### Array & Disks
```
unraid(action="system", subaction="array") # Array status overview
unraid(action="disk", subaction="disks") # All disks with temps & health
unraid(action="array", subaction="parity_status") # Current parity check
unraid(action="array", subaction="parity_history") # Past parity results
unraid(action="array", subaction="parity_start") # Start parity check
unraid(action="array", subaction="stop_array", confirm=True) # ⚠️ Stop array
```
### Array Status
```graphql
{
array {
state
parityCheckStatus { status progress errors }
}
}
### Logs
```
### 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
}
}
unraid(action="disk", subaction="log_files") # List available logs
unraid(action="disk", subaction="logs", path="syslog", lines=50) # Read syslog
unraid(action="disk", subaction="logs", path="/var/log/syslog") # Full path also works
```
### Docker Containers
```graphql
{
docker {
containers {
id
names
image
state
status
}
}
}
```
unraid(action="docker", subaction="list")
unraid(action="docker", subaction="details", container_id="plex")
unraid(action="docker", subaction="start", container_id="nginx")
unraid(action="docker", subaction="stop", container_id="nginx")
unraid(action="docker", subaction="restart", container_id="sonarr")
unraid(action="docker", subaction="networks")
```
### 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
}
}
unraid(action="vm", subaction="list")
unraid(action="vm", subaction="details", vm_id="<id>")
unraid(action="vm", subaction="start", vm_id="<id>")
unraid(action="vm", subaction="stop", vm_id="<id>")
unraid(action="vm", subaction="reboot", vm_id="<id>")
unraid(action="vm", subaction="force_stop", vm_id="<id>", confirm=True) # ⚠️
```
### Notifications
**Counts:**
```graphql
{
notifications {
overview {
unread { info warning alert total }
archive { info warning alert total }
}
}
}
```
unraid(action="notification", subaction="overview")
unraid(action="notification", subaction="unread")
unraid(action="notification", subaction="list", filter="UNREAD", limit=10)
unraid(action="notification", subaction="archive", notification_id="<id>")
unraid(action="notification", subaction="create", title="Test", subject="Subject",
description="Body", importance="normal")
```
**List Unread:**
```graphql
{
notifications {
list(filter: { type: UNREAD, offset: 0, limit: 10 }) {
id
subject
description
timestamp
}
}
}
### API Keys
```
unraid(action="key", subaction="list")
unraid(action="key", subaction="create", name="my-key", roles=["viewer"])
unraid(action="key", subaction="delete", key_id="<id>", confirm=True) # ⚠️
```
**List Archived:**
```graphql
{
notifications {
list(filter: { type: ARCHIVE, offset: 0, limit: 10 }) {
id
subject
description
timestamp
}
}
}
### Plugins
```
unraid(action="plugin", subaction="list")
unraid(action="plugin", subaction="add", names=["community.applications"])
unraid(action="plugin", subaction="remove", names=["old.plugin"], confirm=True) # ⚠️
```
## 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": { ... }
}
}
### rclone
```
unraid(action="rclone", subaction="list_remotes")
unraid(action="rclone", subaction="delete_remote", name="<remote>", confirm=True) # ⚠️
```
Errors appear in:
```json
{
"errors": [
{ "message": "..." }
]
}
### Live Subscriptions (real-time)
```
unraid(action="live", subaction="cpu")
unraid(action="live", subaction="memory")
unraid(action="live", subaction="parity_progress")
unraid(action="live", subaction="log_tail")
unraid(action="live", subaction="notification_feed")
unraid(action="live", subaction="ups_status")
```
> Returns `{"status": "connecting"}` on first call — retry momentarily.
---
## Domain → action= Mapping
| Old tool name (pre-v1.0) | New `action=` |
|--------------------------|---------------|
| `unraid_info` | `system` |
| `unraid_health` | `health` |
| `unraid_array` | `array` |
| `unraid_storage` | `disk` |
| `unraid_docker` | `docker` |
| `unraid_vm` | `vm` |
| `unraid_notifications` | `notification` |
| `unraid_keys` | `key` |
| `unraid_plugins` | `plugin` |
| `unraid_rclone` | `rclone` |
| `unraid_settings` | `setting` |
| `unraid_customization` | `customization` |
| `unraid_oidc` | `oidc` |
| `unraid_users` | `user` |
| `unraid_live` | `live` |

View File

@@ -1,36 +1,105 @@
# Unraid API Troubleshooting Guide
# Unraid MCP — Troubleshooting Guide
Common issues and solutions when working with the Unraid GraphQL API.
## Credentials Not Configured
## "Cannot query field" error
**Error:** `CredentialsNotConfiguredError` or message containing `~/.unraid-mcp/.env`
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 } } }"
**Fix:** Run setup to configure credentials interactively:
```
unraid(action="health", subaction="setup")
```
## "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`)
This writes `UNRAID_API_URL` and `UNRAID_API_KEY` to `~/.unraid-mcp/.env`. Re-run at any time to update or rotate credentials.
## 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.
## Connection Failed / API Unreachable
## "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.
**Symptoms:** Timeout, connection refused, network error
## 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
**Diagnostic steps:**
1. Test basic connectivity:
```
unraid(action="health", subaction="test_connection")
```
2. Full diagnostic report:
```
unraid(action="health", subaction="diagnose")
```
3. Check that `UNRAID_API_URL` in `~/.unraid-mcp/.env` points to the correct Unraid GraphQL endpoint.
4. Verify the API key has the required roles. Get a new key: **Unraid UI → Settings → Management Access → API Keys → Create** (select "Viewer" role for read-only, or appropriate roles for mutations).
---
## Invalid Action / Subaction
**Error:** `Invalid action 'X'` or `Invalid subaction 'X' for action 'Y'`
**Fix:** Check the domain table in `SKILL.md` for the exact `action=` and `subaction=` strings. Common mistakes:
| Wrong | Correct |
|-------|---------|
| `action="info"` | `action="system"` |
| `action="notifications"` | `action="notification"` |
| `action="keys"` | `action="key"` |
| `action="plugins"` | `action="plugin"` |
| `action="settings"` | `action="setting"` |
| `subaction="unread"` | `subaction="mark_unread"` |
---
## Destructive Action Blocked
**Error:** `Action 'X' was not confirmed. Re-run with confirm=True to bypass elicitation.`
**Fix:** Add `confirm=True` to the call:
```
unraid(action="array", subaction="stop_array", confirm=True)
unraid(action="vm", subaction="force_stop", vm_id="<id>", confirm=True)
```
See the Destructive Actions table in `SKILL.md` for the full list.
---
## Live Subscription Returns "Connecting"
**Symptoms:** `unraid(action="live", ...)` returns `{"status": "connecting"}`
**Explanation:** The persistent WebSocket subscription has not yet received its first event. Retry in a moment.
**Known issue:** `live/array_state` uses `arraySubscription` which has a known Unraid API bug (returns null for a non-nullable field). This subscription will always show "connecting."
**Event-driven subscriptions** (`live/notifications_overview`, `live/owner`, `live/server_status`, `live/ups_status`) only populate when the server emits a change event. If the server is idle, these may never populate during a session.
**Workaround for array state:** Use `unraid(action="system", subaction="array")` for a synchronous snapshot instead.
---
## Rate Limit Exceeded
**Limit:** 100 requests / 10 seconds
**Symptoms:** HTTP 429 or rate limit error
**Fix:** Space out requests. Avoid polling in tight loops. Use `live/` subscriptions for real-time data instead of polling `system/metrics` repeatedly.
---
## Log Path Rejected
**Error:** `Invalid log path`
**Valid log path prefixes:** `/var/log/`, `/boot/logs/`, `/mnt/`
Use `unraid(action="disk", subaction="log_files")` to list available logs before reading.
---
## Container Logs Not Available
Docker container stdout/stderr are **not accessible via the Unraid API**. SSH to the Unraid server and use `docker logs <container>` directly.