20 KiB
🚀 Unraid MCP Server
A powerful MCP (Model Context Protocol) server that provides comprehensive tools to interact with an Unraid server's GraphQL API.
✨ Features
- 🔧 1 Tool, ~108 Actions: Complete Unraid management through a single consolidated MCP tool
- 🏗️ Modular Architecture: Clean, maintainable, and extensible codebase
- ⚡ High Performance: Async/concurrent operations with optimized timeouts
- 🔄 Real-time Data: WebSocket subscriptions for live metrics, logs, array state, and more
- 📊 Health Monitoring: Comprehensive system diagnostics and status
- 🐳 Docker Ready: Full containerization support with Docker Compose
- 🔒 Secure: Optional Google OAuth 2.0 authentication + SSL/TLS + API key management
- 📝 Rich Logging: Structured logging with rotation and multiple levels
📋 Table of Contents
- Claude Code Plugin
- Quick Start
- Installation
- Configuration
- Google OAuth
- Available Tools & Resources
- Development
- Architecture
- Troubleshooting
🎯 Claude Code Plugin
The easiest way to use Unraid MCP is through the Claude Code marketplace:
# Add the marketplace
/plugin marketplace add jmagar/unraid-mcp
# Install the Unraid skill
/plugin install unraid @unraid-mcp
This provides instant access to Unraid monitoring and management through Claude Code with:
- 1 MCP tool (
unraid) exposing ~108 actions viaaction+subactionrouting - Real-time system metrics and health monitoring
- Docker container and VM lifecycle management
- Disk health monitoring and storage management
See .claude-plugin/README.md for detailed plugin documentation.
⚙️ Credential Setup
Credentials are stored in ~/.unraid-mcp/.env — one location that works for the
Claude Code plugin, direct uv run invocations, and Docker.
Option 1 — Interactive (Claude Code plugin, elicitation-supported clients):
unraid(action="health", subaction="setup")
The server prompts for your API URL and key, writes ~/.unraid-mcp/.env automatically
(created with mode 700/600), and activates credentials without restart.
Option 2 — Manual:
mkdir -p ~/.unraid-mcp && chmod 700 ~/.unraid-mcp
cp .env.example ~/.unraid-mcp/.env && chmod 600 ~/.unraid-mcp/.env
# Edit ~/.unraid-mcp/.env with your values:
# UNRAID_API_URL=https://10-1-0-2.xxx.myunraid.net:31337
# UNRAID_API_KEY=your-key-from-unraid-settings
Docker: ~/.unraid-mcp/.env is loaded via env_file in docker-compose.yml —
same file, no duplication needed.
Finding your API key: Unraid → Settings → Management Access → API Keys
🚀 Quick Start
Prerequisites
- Docker and Docker Compose (recommended)
- OR Python 3.12+ with uv for development
- Unraid server with GraphQL API enabled
1. Clone Repository
git clone https://github.com/jmagar/unraid-mcp
cd unraid-mcp
2. Configure Environment
# For Docker/production use — canonical credential location (all runtimes)
mkdir -p ~/.unraid-mcp && chmod 700 ~/.unraid-mcp
cp .env.example ~/.unraid-mcp/.env && chmod 600 ~/.unraid-mcp/.env
# Edit ~/.unraid-mcp/.env with your values
# For local development only
cp .env.example .env
3. Deploy with Docker (Recommended)
# Start with Docker Compose
docker compose up -d
# View logs
docker compose logs -f unraid-mcp
OR 3. Run for Development
# Install dependencies
uv sync
# Run development server
uv run unraid-mcp-server
📂 Plugin Structure
This repository is a Claude Code plugin. Key components:
unraid-mcp/ # ${CLAUDE_PLUGIN_ROOT}
├── .claude-plugin/
│ ├── marketplace.json # Marketplace catalog
│ └── plugin.json # Plugin manifest
├── unraid_mcp/ # MCP server Python package
├── skills/unraid/ # Skill and documentation
├── pyproject.toml # Dependencies and entry points
└── scripts/ # Validation and helper scripts
- MCP Server: 1
unraidtool with ~108 actions via GraphQL API - Skill:
/unraidskill for monitoring and queries - Entry Point:
unraid-mcp-serverdefined in pyproject.toml
📦 Installation
🐳 Docker Deployment (Recommended)
The easiest way to run the Unraid MCP Server is with Docker:
# Clone repository
git clone https://github.com/jmagar/unraid-mcp
cd unraid-mcp
# Set required environment variables
export UNRAID_API_URL="http://your-unraid-server/graphql"
export UNRAID_API_KEY="your_api_key_here"
# Deploy with Docker Compose
docker compose up -d
# View logs
docker compose logs -f unraid-mcp
Manual Docker Build
# Build and run manually
docker build -t unraid-mcp-server .
docker run -d --name unraid-mcp \
--restart unless-stopped \
-p 6970:6970 \
-e UNRAID_API_URL="http://your-unraid-server/graphql" \
-e UNRAID_API_KEY="your_api_key_here" \
unraid-mcp-server
🔧 Development Installation
For development and testing:
# Clone repository
git clone https://github.com/jmagar/unraid-mcp
cd unraid-mcp
# Install dependencies with uv
uv sync
# Install development dependencies
uv sync --group dev
# Configure environment
cp .env.example .env
# Edit .env with your settings
# Run development server
uv run unraid-mcp-server
⚙️ Configuration
Environment Variables
Create .env file in the project root:
# Core API Configuration (Required)
UNRAID_API_URL=https://your-unraid-server-url/graphql
UNRAID_API_KEY=your_unraid_api_key
# MCP Server Settings
UNRAID_MCP_TRANSPORT=streamable-http # streamable-http (recommended), sse (deprecated), stdio
UNRAID_MCP_HOST=0.0.0.0
UNRAID_MCP_PORT=6970
# Logging Configuration
UNRAID_MCP_LOG_LEVEL=INFO # DEBUG, INFO, WARNING, ERROR
UNRAID_MCP_LOG_FILE=unraid-mcp.log
# SSL/TLS Configuration
UNRAID_VERIFY_SSL=true # true, false, or path to CA bundle
# Subscription Configuration
UNRAID_AUTO_START_SUBSCRIPTIONS=true # Auto-start WebSocket subscriptions on startup (default: true)
UNRAID_MAX_RECONNECT_ATTEMPTS=10 # Max WebSocket reconnection attempts (default: 10)
# Optional: Log Stream Configuration
# UNRAID_AUTOSTART_LOG_PATH=/var/log/syslog # Path for log streaming resource (unraid://logs/stream)
Transport Options
| Transport | Description | Use Case |
|---|---|---|
streamable-http |
HTTP-based (recommended) | Most compatible, best performance |
sse |
Server-Sent Events (deprecated) | Legacy support only |
stdio |
Standard I/O | Direct integration scenarios |
🔐 Authentication (Optional)
Two independent auth methods — use either or both.
Google OAuth
Protect the HTTP server with Google OAuth 2.0 — clients must complete a Google login before any tool call is executed.
# Add to ~/.unraid-mcp/.env
GOOGLE_CLIENT_ID=your-client-id.apps.googleusercontent.com
GOOGLE_CLIENT_SECRET=GOCSPX-your-secret
UNRAID_MCP_BASE_URL=http://10.1.0.2:6970 # public URL of this server
UNRAID_MCP_JWT_SIGNING_KEY=<64-char-hex> # prevents token invalidation on restart
Quick setup:
- Google Cloud Console → Credentials → OAuth 2.0 Client ID (Web application)
- Authorized redirect URI:
<UNRAID_MCP_BASE_URL>/auth/callback - Copy Client ID + Secret into
~/.unraid-mcp/.env - Generate a signing key:
python3 -c "import secrets; print(secrets.token_hex(32))" - Restart the server
API Key (Bearer Token)
Simpler option for headless/machine access — no browser flow required:
# Add to ~/.unraid-mcp/.env
UNRAID_MCP_API_KEY=your-secret-token # can be same value as UNRAID_API_KEY
Clients present it as Authorization: Bearer <UNRAID_MCP_API_KEY>. Set both GOOGLE_CLIENT_ID and UNRAID_MCP_API_KEY to accept either method simultaneously.
Omit both to run without authentication (default — open server).
Full guide: docs/AUTHENTICATION.md
🛠️ Available Tools & Resources
The single unraid tool uses action (domain) + subaction (operation) routing to expose all operations via one MCP tool, minimizing context window usage. Destructive actions require confirm=True.
Single Tool, 15 Domains, ~108 Actions
Call pattern: unraid(action="<domain>", subaction="<operation>")
| action= | Subactions | Description |
|---|---|---|
system |
overview, array, network, registration, variables, metrics, services, display, config, online, owner, settings, server, servers, flash, ups_devices, ups_device, ups_config | Server info, metrics, network, UPS (18 subactions) |
health |
check, test_connection, diagnose, setup | Health checks, connection test, diagnostics, interactive setup (4 subactions) |
array |
parity_status, parity_history, parity_start, parity_pause, parity_resume, parity_cancel, start_array, stop_array, add_disk, remove_disk, mount_disk, unmount_disk, clear_disk_stats | Parity checks, array state, disk operations (13 subactions) |
disk |
shares, disks, disk_details, log_files, logs, flash_backup | Shares, physical disks, log files (6 subactions) |
docker |
list, details, start, stop, restart, networks, network_details | Container lifecycle and network inspection (7 subactions) |
vm |
list, details, start, stop, pause, resume, force_stop, reboot, reset | Virtual machine lifecycle (9 subactions) |
notification |
overview, list, create, archive, mark_unread, delete, delete_archived, archive_all, archive_many, unarchive_many, unarchive_all, recalculate | System notifications CRUD (12 subactions) |
key |
list, get, create, update, delete, add_role, remove_role | API key management (7 subactions) |
plugin |
list, add, remove | Plugin management (3 subactions) |
rclone |
list_remotes, config_form, create_remote, delete_remote | Cloud storage remote management (4 subactions) |
setting |
update, configure_ups | System settings and UPS config (2 subactions) |
customization |
theme, public_theme, is_initial_setup, sso_enabled, set_theme | Theme and UI customization (5 subactions) |
oidc |
providers, provider, configuration, public_providers, validate_session | OIDC/SSO provider management (5 subactions) |
user |
me | Current authenticated user (1 subaction) |
live |
cpu, memory, cpu_telemetry, array_state, parity_progress, ups_status, notifications_overview, owner, server_status, log_tail, notification_feed | Real-time WebSocket subscription snapshots (11 subactions) |
Destructive Actions (require confirm=True)
- array:
stop_array,remove_disk,clear_disk_stats - vm:
force_stop,reset - notification:
delete,delete_archived - rclone:
delete_remote - key:
delete - disk:
flash_backup - setting:
configure_ups - plugin:
remove
MCP Resources (Real-time Cached Data)
The server exposes two classes of MCP resources backed by persistent WebSocket connections:
unraid://live/* — 9 snapshot resources (auto-started, always-cached):
unraid://live/cpu— CPU utilizationunraid://live/memory— Memory usageunraid://live/cpu_telemetry— Detailed CPU telemetryunraid://live/array_state— Array state changesunraid://live/parity_progress— Parity check progressunraid://live/ups_status— UPS statusunraid://live/notifications_overview— Notification countsunraid://live/owner— Owner info changesunraid://live/server_status— Server status changes
unraid://logs/stream — Live log file tail (path controlled by UNRAID_AUTOSTART_LOG_PATH)
Note
: Resources return cached data from persistent WebSocket subscriptions. A
{"status": "connecting"}placeholder is returned while the subscription initializes — retry in a moment.
log_tailandnotification_feedare accessible as tool subactions (unraid(action="live", subaction="log_tail")) but are not registered as MCP resources — they use transient one-shot subscriptions and require parameters.
🔧 Development
Project Structure
unraid-mcp/
├── unraid_mcp/ # Main package
│ ├── main.py # Entry point
│ ├── server.py # FastMCP server setup
│ ├── version.py # Version management (importlib.metadata)
│ ├── config/ # Configuration management
│ │ ├── settings.py # Environment & settings
│ │ └── logging.py # Logging setup
│ ├── core/ # Core infrastructure
│ │ ├── client.py # GraphQL client
│ │ ├── exceptions.py # Custom exceptions
│ │ ├── guards.py # Destructive action guards
│ │ ├── setup.py # Interactive credential setup
│ │ ├── types.py # Shared data types
│ │ └── utils.py # Utility functions
│ ├── subscriptions/ # Real-time subscriptions
│ │ ├── manager.py # Persistent WebSocket manager
│ │ ├── resources.py # MCP resources (unraid://live/*)
│ │ ├── snapshot.py # Transient subscribe_once helpers
│ │ ├── queries.py # Subscription query constants
│ │ ├── diagnostics.py # Diagnostic tools
│ │ └── utils.py # Subscription utility functions
│ └── tools/ # Single consolidated tool (~108 actions)
│ └── unraid.py # All 15 domains in one file
├── tests/ # Test suite
│ ├── conftest.py # Shared fixtures
│ ├── test_*.py # Unit tests (per domain)
│ ├── http_layer/ # httpx-level request tests
│ ├── integration/ # WebSocket lifecycle tests
│ ├── safety/ # Destructive action guard tests
│ └── schema/ # GraphQL query validation
├── docs/ # Documentation & API references
├── scripts/ # Build and utility scripts
├── skills/unraid/ # Claude skill assets
├── .claude-plugin/ # Plugin manifest & marketplace config
├── .env.example # Environment template
├── Dockerfile # Container image definition
├── docker-compose.yml # Docker Compose deployment
├── pyproject.toml # Project config & dependencies
└── logs/ # Log files (auto-created, gitignored)
Code Quality Commands
# Lint and format code
uv run ruff check unraid_mcp/
uv run ruff format unraid_mcp/
# Type checking
uv run ty check unraid_mcp/
# Run tests
uv run pytest
Integration Smoke-Tests (mcporter)
Live integration tests that exercise all non-destructive actions via mcporter. Two scripts cover two transport modes:
# stdio — no running server needed (good for CI)
./tests/mcporter/test-tools.sh [--parallel] [--timeout-ms N] [--verbose]
# HTTP — connects to a live server (most up-to-date coverage)
./tests/mcporter/test-actions.sh [MCP_URL] # default: http://localhost:6970/mcp
Destructive actions are always skipped in both scripts. For safe testing strategies and exact mcporter commands per destructive action, see docs/DESTRUCTIVE_ACTIONS.md.
API Schema Docs Automation
# Regenerate complete GraphQL schema reference from live introspection
set -a; source .env; set +a
uv run python scripts/generate_unraid_api_reference.py
This updates docs/UNRAID_API_COMPLETE_REFERENCE.md with all operations, directives, and types visible to your API key.
Optional cron example (daily at 03:15):
15 3 * * * cd /path/to/unraid-mcp && /usr/bin/env bash -lc 'set -a; source .env; set +a; uv run python scripts/generate_unraid_api_reference.py && git add docs/UNRAID_API_COMPLETE_REFERENCE.md && git commit -m "docs: refresh unraid graphql schema"'
Development Workflow
# Start development server
uv run unraid-mcp-server
# Or run via module directly
uv run -m unraid_mcp.main
# Hot-reload dev server (restarts on file changes)
fastmcp run fastmcp.http.json --reload
# Run via named config files
fastmcp run fastmcp.http.json # streamable-http on :6970
fastmcp run fastmcp.stdio.json # stdio transport
Ad-hoc Tool Testing (fastmcp CLI)
# Introspect the running server
fastmcp list http://localhost:6970/mcp
fastmcp list http://localhost:6970/mcp --input-schema
# Call a tool directly (HTTP)
fastmcp call http://localhost:6970/mcp unraid action=health subaction=check
fastmcp call http://localhost:6970/mcp unraid action=docker subaction=list
# Call without a running server (stdio config)
fastmcp list fastmcp.stdio.json
fastmcp call fastmcp.stdio.json unraid action=health subaction=check
🏗️ Architecture
Core Principles
- Modular Design: Separate concerns across focused modules
- Async First: All operations are non-blocking and concurrent-safe
- Error Resilience: Comprehensive error handling with graceful degradation
- Configuration Driven: Environment-based configuration with validation
- Observability: Structured logging and health monitoring
Key Components
| Component | Purpose |
|---|---|
| FastMCP Server | MCP protocol implementation and tool registration |
| GraphQL Client | Async HTTP client with timeout management |
| Subscription Manager | WebSocket connections for real-time data |
| Tool Modules | Domain-specific business logic (Docker, VMs, etc.) |
| Configuration System | Environment loading and validation |
| Logging Framework | Structured logging with file rotation |
🐛 Troubleshooting
Common Issues
🔥 Port Already in Use
# Kill existing process on port 6970, then restart
lsof -ti :6970 | xargs kill -9 2>/dev/null; uv run unraid-mcp-server
🔧 Connection Refused
# Check Unraid API configuration
curl -k "${UNRAID_API_URL}" -H "X-API-Key: ${UNRAID_API_KEY}"
📝 Import Errors
# Reinstall dependencies
uv sync --reinstall
🔍 Debug Mode
# Enable debug logging
export UNRAID_MCP_LOG_LEVEL=DEBUG
uv run unraid-mcp-server
Health Check
# Use the built-in health check tool via MCP client
# or check logs at: logs/unraid-mcp.log
📄 License
This project is licensed under the MIT License - see the LICENSE file for details.
🤝 Contributing
- Fork the repository
- Create a feature branch:
git checkout -b feature/amazing-feature - Run tests:
uv run pytest - Commit changes:
git commit -m 'Add amazing feature' - Push to branch:
git push origin feature/amazing-feature - Open a Pull Request
📞 Support
- 📚 Documentation: Check inline code documentation
- 🐛 Issues: GitHub Issues
- 💬 Discussions: Use GitHub Discussions for questions
Built with ❤️ for the Unraid community