unraid-mcp 1.2.2__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- unraid_mcp-1.2.2/.env.example +38 -0
- unraid_mcp-1.2.2/.gitignore +90 -0
- unraid_mcp-1.2.2/AGENTS.md +1 -0
- unraid_mcp-1.2.2/CLAUDE.md +301 -0
- unraid_mcp-1.2.2/GEMINI.md +1 -0
- unraid_mcp-1.2.2/LICENSE +21 -0
- unraid_mcp-1.2.2/PKG-INFO +659 -0
- unraid_mcp-1.2.2/README.md +597 -0
- unraid_mcp-1.2.2/pyproject.toml +303 -0
- unraid_mcp-1.2.2/skills/unraid/README.md +160 -0
- unraid_mcp-1.2.2/skills/unraid/SKILL.md +332 -0
- unraid_mcp-1.2.2/skills/unraid/examples/disk-health.sh +36 -0
- unraid_mcp-1.2.2/skills/unraid/examples/read-logs.sh +47 -0
- unraid_mcp-1.2.2/skills/unraid/references/api-reference.md +969 -0
- unraid_mcp-1.2.2/skills/unraid/references/endpoints.md +51 -0
- unraid_mcp-1.2.2/skills/unraid/references/introspection-schema.md +3116 -0
- unraid_mcp-1.2.2/skills/unraid/references/quick-reference.md +125 -0
- unraid_mcp-1.2.2/skills/unraid/references/schema.graphql +3114 -0
- unraid_mcp-1.2.2/skills/unraid/references/troubleshooting.md +109 -0
- unraid_mcp-1.2.2/skills/unraid/scripts/dashboard.sh +229 -0
- unraid_mcp-1.2.2/skills/unraid/scripts/unraid-query.sh +159 -0
- unraid_mcp-1.2.2/skills/unraid/setup.sh +39 -0
- unraid_mcp-1.2.2/tests/conftest.py +65 -0
- unraid_mcp-1.2.2/tests/contract/__init__.py +0 -0
- unraid_mcp-1.2.2/tests/contract/test_response_contracts.py +976 -0
- unraid_mcp-1.2.2/tests/http_layer/__init__.py +0 -0
- unraid_mcp-1.2.2/tests/http_layer/test_request_construction.py +1287 -0
- unraid_mcp-1.2.2/tests/integration/__init__.py +0 -0
- unraid_mcp-1.2.2/tests/integration/test_subscriptions.py +922 -0
- unraid_mcp-1.2.2/tests/mcporter/README.md +163 -0
- unraid_mcp-1.2.2/tests/mcporter/test-destructive.sh +338 -0
- unraid_mcp-1.2.2/tests/mcporter/test-http.sh +456 -0
- unraid_mcp-1.2.2/tests/mcporter/test-tools.sh +814 -0
- unraid_mcp-1.2.2/tests/property/__init__.py +0 -0
- unraid_mcp-1.2.2/tests/property/test_input_validation.py +755 -0
- unraid_mcp-1.2.2/tests/safety/__init__.py +0 -0
- unraid_mcp-1.2.2/tests/safety/test_destructive_guards.py +414 -0
- unraid_mcp-1.2.2/tests/schema/__init__.py +0 -0
- unraid_mcp-1.2.2/tests/schema/test_query_validation.py +1025 -0
- unraid_mcp-1.2.2/tests/test_array.py +256 -0
- unraid_mcp-1.2.2/tests/test_auth.py +707 -0
- unraid_mcp-1.2.2/tests/test_client.py +720 -0
- unraid_mcp-1.2.2/tests/test_customization.py +74 -0
- unraid_mcp-1.2.2/tests/test_docker.py +219 -0
- unraid_mcp-1.2.2/tests/test_guards.py +104 -0
- unraid_mcp-1.2.2/tests/test_health.py +445 -0
- unraid_mcp-1.2.2/tests/test_info.py +306 -0
- unraid_mcp-1.2.2/tests/test_keys.py +137 -0
- unraid_mcp-1.2.2/tests/test_live.py +162 -0
- unraid_mcp-1.2.2/tests/test_live.sh +139 -0
- unraid_mcp-1.2.2/tests/test_notifications.py +328 -0
- unraid_mcp-1.2.2/tests/test_oidc.py +93 -0
- unraid_mcp-1.2.2/tests/test_plugins.py +63 -0
- unraid_mcp-1.2.2/tests/test_rclone.py +193 -0
- unraid_mcp-1.2.2/tests/test_resources.py +167 -0
- unraid_mcp-1.2.2/tests/test_review_regressions.py +92 -0
- unraid_mcp-1.2.2/tests/test_settings.py +141 -0
- unraid_mcp-1.2.2/tests/test_setup.py +511 -0
- unraid_mcp-1.2.2/tests/test_snapshot.py +125 -0
- unraid_mcp-1.2.2/tests/test_storage.py +356 -0
- unraid_mcp-1.2.2/tests/test_subscription_manager.py +156 -0
- unraid_mcp-1.2.2/tests/test_subscription_validation.py +131 -0
- unraid_mcp-1.2.2/tests/test_users.py +75 -0
- unraid_mcp-1.2.2/tests/test_validation.py +58 -0
- unraid_mcp-1.2.2/tests/test_vm.py +195 -0
- unraid_mcp-1.2.2/unraid_mcp/__init__.py +6 -0
- unraid_mcp-1.2.2/unraid_mcp/config/__init__.py +1 -0
- unraid_mcp-1.2.2/unraid_mcp/config/logging.py +247 -0
- unraid_mcp-1.2.2/unraid_mcp/config/settings.py +183 -0
- unraid_mcp-1.2.2/unraid_mcp/core/__init__.py +1 -0
- unraid_mcp-1.2.2/unraid_mcp/core/auth.py +316 -0
- unraid_mcp-1.2.2/unraid_mcp/core/client.py +413 -0
- unraid_mcp-1.2.2/unraid_mcp/core/exceptions.py +77 -0
- unraid_mcp-1.2.2/unraid_mcp/core/guards.py +110 -0
- unraid_mcp-1.2.2/unraid_mcp/core/middleware_refs.py +27 -0
- unraid_mcp-1.2.2/unraid_mcp/core/setup.py +166 -0
- unraid_mcp-1.2.2/unraid_mcp/core/types.py +27 -0
- unraid_mcp-1.2.2/unraid_mcp/core/utils.py +94 -0
- unraid_mcp-1.2.2/unraid_mcp/core/validation.py +24 -0
- unraid_mcp-1.2.2/unraid_mcp/main.py +57 -0
- unraid_mcp-1.2.2/unraid_mcp/server.py +280 -0
- unraid_mcp-1.2.2/unraid_mcp/subscriptions/__init__.py +1 -0
- unraid_mcp-1.2.2/unraid_mcp/subscriptions/diagnostics.py +292 -0
- unraid_mcp-1.2.2/unraid_mcp/subscriptions/manager.py +757 -0
- unraid_mcp-1.2.2/unraid_mcp/subscriptions/queries.py +52 -0
- unraid_mcp-1.2.2/unraid_mcp/subscriptions/resources.py +154 -0
- unraid_mcp-1.2.2/unraid_mcp/subscriptions/snapshot.py +162 -0
- unraid_mcp-1.2.2/unraid_mcp/subscriptions/utils.py +110 -0
- unraid_mcp-1.2.2/unraid_mcp/tools/__init__.py +19 -0
- unraid_mcp-1.2.2/unraid_mcp/tools/_array.py +110 -0
- unraid_mcp-1.2.2/unraid_mcp/tools/_customization.py +60 -0
- unraid_mcp-1.2.2/unraid_mcp/tools/_disk.py +190 -0
- unraid_mcp-1.2.2/unraid_mcp/tools/_docker.py +183 -0
- unraid_mcp-1.2.2/unraid_mcp/tools/_health.py +172 -0
- unraid_mcp-1.2.2/unraid_mcp/tools/_key.py +130 -0
- unraid_mcp-1.2.2/unraid_mcp/tools/_live.py +120 -0
- unraid_mcp-1.2.2/unraid_mcp/tools/_notification.py +195 -0
- unraid_mcp-1.2.2/unraid_mcp/tools/_oidc.py +70 -0
- unraid_mcp-1.2.2/unraid_mcp/tools/_plugin.py +76 -0
- unraid_mcp-1.2.2/unraid_mcp/tools/_rclone.py +126 -0
- unraid_mcp-1.2.2/unraid_mcp/tools/_setting.py +136 -0
- unraid_mcp-1.2.2/unraid_mcp/tools/_system.py +268 -0
- unraid_mcp-1.2.2/unraid_mcp/tools/_user.py +38 -0
- unraid_mcp-1.2.2/unraid_mcp/tools/_vm.py +90 -0
- unraid_mcp-1.2.2/unraid_mcp/tools/unraid.py +479 -0
- unraid_mcp-1.2.2/unraid_mcp/version.py +11 -0
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
# Unraid MCP Server Configuration
|
|
2
|
+
# =================================
|
|
3
|
+
|
|
4
|
+
# Core API Configuration (Required)
|
|
5
|
+
# ---------------------------------
|
|
6
|
+
UNRAID_API_KEY=your_api_key
|
|
7
|
+
UNRAID_API_URL=http://your-unraid-server
|
|
8
|
+
|
|
9
|
+
# MCP Server Settings
|
|
10
|
+
# -------------------
|
|
11
|
+
# Default transport is streamable-http (v1.2.0+).
|
|
12
|
+
# Use stdio for Claude Desktop / local process-based clients.
|
|
13
|
+
# Options: streamable-http (default), stdio, sse (deprecated)
|
|
14
|
+
UNRAID_MCP_HOST=0.0.0.0
|
|
15
|
+
UNRAID_MCP_PORT=6970
|
|
16
|
+
UNRAID_MCP_TRANSPORT=streamable-http
|
|
17
|
+
|
|
18
|
+
# HTTP Bearer Token Authentication (streamable-http / sse transports)
|
|
19
|
+
# -------------------------------------------------------------------
|
|
20
|
+
# Generate a token and paste it here AND into the plugin's userConfig field:
|
|
21
|
+
# openssl rand -hex 32
|
|
22
|
+
# Required when UNRAID_MCP_TRANSPORT is not "stdio" and UNRAID_MCP_DISABLE_HTTP_AUTH is not "true".
|
|
23
|
+
UNRAID_MCP_BEARER_TOKEN=your_bearer_token
|
|
24
|
+
|
|
25
|
+
# Safety flags
|
|
26
|
+
# ------------
|
|
27
|
+
UNRAID_MCP_ALLOW_DESTRUCTIVE=false
|
|
28
|
+
UNRAID_MCP_ALLOW_YOLO=false
|
|
29
|
+
UNRAID_MCP_DISABLE_HTTP_AUTH=false
|
|
30
|
+
|
|
31
|
+
# Docker user / network
|
|
32
|
+
# ---------------------
|
|
33
|
+
# DOCKER_NETWORK is optional. If set, the container joins that external network.
|
|
34
|
+
# Leave blank (or unset) to use only the default bridge network.
|
|
35
|
+
# Create the network first: docker network create <name>
|
|
36
|
+
DOCKER_NETWORK=
|
|
37
|
+
PGID=1000
|
|
38
|
+
PUID=1000
|
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
# ── Secrets ──────────────────────────────────────────────────────────────────
|
|
2
|
+
.env
|
|
3
|
+
.env.*
|
|
4
|
+
!.env.example
|
|
5
|
+
|
|
6
|
+
# ── Runtime artifacts ────────────────────────────────────────────────────────
|
|
7
|
+
logs/
|
|
8
|
+
backups/
|
|
9
|
+
*.log
|
|
10
|
+
*.pid
|
|
11
|
+
*.bak
|
|
12
|
+
*.bak-*
|
|
13
|
+
|
|
14
|
+
# ── Claude Code / AI tooling ────────────────────────────────────────────────
|
|
15
|
+
.claude/settings.local.json
|
|
16
|
+
.claude/worktrees/
|
|
17
|
+
.omc/
|
|
18
|
+
.lavra/
|
|
19
|
+
.beads/
|
|
20
|
+
.dolt/
|
|
21
|
+
*.db
|
|
22
|
+
*.db-shm
|
|
23
|
+
*.db-wal
|
|
24
|
+
.beads-credential-key
|
|
25
|
+
.serena/
|
|
26
|
+
.worktrees/
|
|
27
|
+
.full-review/
|
|
28
|
+
.full-review-archive-*
|
|
29
|
+
.bivvy
|
|
30
|
+
|
|
31
|
+
# ── IDE / editor ─────────────────────────────────────────────────────────────
|
|
32
|
+
.vscode/
|
|
33
|
+
.cursor/
|
|
34
|
+
.windsurf/
|
|
35
|
+
.1code/
|
|
36
|
+
.idea/
|
|
37
|
+
.zed/
|
|
38
|
+
*.iml
|
|
39
|
+
*.swp
|
|
40
|
+
*.swo
|
|
41
|
+
*~
|
|
42
|
+
.emdash.json
|
|
43
|
+
|
|
44
|
+
# ── OS generated ─────────────────────────────────────────────────────────────
|
|
45
|
+
.DS_Store
|
|
46
|
+
Thumbs.db
|
|
47
|
+
|
|
48
|
+
# ── Caches (ALL tool caches go here) ─────────────────────────────────────────
|
|
49
|
+
.cache/
|
|
50
|
+
|
|
51
|
+
# ── Documentation artifacts (session/plan docs, not reference) ───────────────
|
|
52
|
+
.docs/
|
|
53
|
+
docs/plans/
|
|
54
|
+
docs/sessions/
|
|
55
|
+
docs/reports/
|
|
56
|
+
docs/research/
|
|
57
|
+
docs/superpowers/
|
|
58
|
+
specs/
|
|
59
|
+
|
|
60
|
+
# ── Python ───────────────────────────────────────────────────────────────────
|
|
61
|
+
.venv/
|
|
62
|
+
__pycache__/
|
|
63
|
+
*.py[oc]
|
|
64
|
+
*.egg-info/
|
|
65
|
+
*.egg
|
|
66
|
+
build/
|
|
67
|
+
dist/
|
|
68
|
+
sdist/
|
|
69
|
+
wheels/
|
|
70
|
+
pip-wheel-metadata/
|
|
71
|
+
*.whl
|
|
72
|
+
.hypothesis/
|
|
73
|
+
.pytest_cache/
|
|
74
|
+
.ruff_cache/
|
|
75
|
+
.ty_cache/
|
|
76
|
+
.mypy_cache/
|
|
77
|
+
.pytype/
|
|
78
|
+
.pyre/
|
|
79
|
+
.pyright/
|
|
80
|
+
htmlcov/
|
|
81
|
+
.coverage
|
|
82
|
+
.coverage.*
|
|
83
|
+
coverage.xml
|
|
84
|
+
.tox/
|
|
85
|
+
.nox/
|
|
86
|
+
pip-log.txt
|
|
87
|
+
|
|
88
|
+
# ── Repo-specific ────────────────────────────────────────────────────────────
|
|
89
|
+
/DESTRUCTIVE_ACTIONS.md
|
|
90
|
+
client_secret_*.apps.googleusercontent.com.json
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
CLAUDE.md
|
|
@@ -0,0 +1,301 @@
|
|
|
1
|
+
# CLAUDE.md
|
|
2
|
+
|
|
3
|
+
This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
|
|
4
|
+
|
|
5
|
+
## Project Overview
|
|
6
|
+
This is an MCP (Model Context Protocol) server that provides tools to interact with an Unraid server's GraphQL API. The server is built using FastMCP with a **modular architecture** consisting of separate packages for configuration, core functionality, subscriptions, and tools.
|
|
7
|
+
|
|
8
|
+
## Development Commands
|
|
9
|
+
|
|
10
|
+
### Setup
|
|
11
|
+
```bash
|
|
12
|
+
# Initialize uv virtual environment and install dependencies
|
|
13
|
+
uv sync
|
|
14
|
+
|
|
15
|
+
# Install dev dependencies
|
|
16
|
+
uv sync --group dev
|
|
17
|
+
```
|
|
18
|
+
|
|
19
|
+
### Running the Server
|
|
20
|
+
```bash
|
|
21
|
+
# Local development with uv (recommended)
|
|
22
|
+
uv run unraid-mcp-server
|
|
23
|
+
|
|
24
|
+
# Direct module execution
|
|
25
|
+
uv run -m unraid_mcp.main
|
|
26
|
+
```
|
|
27
|
+
|
|
28
|
+
### Code Quality
|
|
29
|
+
```bash
|
|
30
|
+
# Lint and format with ruff
|
|
31
|
+
uv run ruff check unraid_mcp/
|
|
32
|
+
uv run ruff format unraid_mcp/
|
|
33
|
+
|
|
34
|
+
# Type checking with ty (Astral's fast type checker)
|
|
35
|
+
uv run ty check unraid_mcp/
|
|
36
|
+
|
|
37
|
+
# Run tests
|
|
38
|
+
uv run pytest
|
|
39
|
+
```
|
|
40
|
+
|
|
41
|
+
### Environment Setup
|
|
42
|
+
Copy `.env.example` to `.env` and configure:
|
|
43
|
+
|
|
44
|
+
**Required:**
|
|
45
|
+
- `UNRAID_API_URL`: Unraid GraphQL endpoint
|
|
46
|
+
- `UNRAID_API_KEY`: Unraid API key
|
|
47
|
+
|
|
48
|
+
**Server:**
|
|
49
|
+
- `UNRAID_MCP_LOG_LEVEL`: Log verbosity (default: INFO)
|
|
50
|
+
- `UNRAID_MCP_LOG_FILE`: Log filename in logs/ (default: unraid-mcp.log)
|
|
51
|
+
|
|
52
|
+
**SSL/TLS:**
|
|
53
|
+
- `UNRAID_VERIFY_SSL`: SSL verification (default: true; set `false` for self-signed certs)
|
|
54
|
+
|
|
55
|
+
**Subscriptions:**
|
|
56
|
+
- `UNRAID_AUTO_START_SUBSCRIPTIONS`: Auto-start live subscriptions on startup (default: true)
|
|
57
|
+
- `UNRAID_MAX_RECONNECT_ATTEMPTS`: WebSocket reconnect limit (default: 10)
|
|
58
|
+
|
|
59
|
+
**Credentials override:**
|
|
60
|
+
- `UNRAID_CREDENTIALS_DIR`: Override the `~/.unraid-mcp/` credentials directory path
|
|
61
|
+
|
|
62
|
+
## Architecture
|
|
63
|
+
|
|
64
|
+
### Core Components
|
|
65
|
+
- **Main Server**: `unraid_mcp/server.py` - Modular MCP server with FastMCP integration
|
|
66
|
+
- **Entry Point**: `unraid_mcp/main.py` - Application entry point and startup logic
|
|
67
|
+
- **Configuration**: `unraid_mcp/config/` - Settings management and logging configuration
|
|
68
|
+
- **Core Infrastructure**: `unraid_mcp/core/` - GraphQL client, exceptions, and shared types
|
|
69
|
+
- `guards.py` — destructive action gating via MCP elicitation
|
|
70
|
+
- `utils.py` — shared helpers (`safe_get`, `safe_display_url`, path validation)
|
|
71
|
+
- `setup.py` — elicitation-based credential setup flow
|
|
72
|
+
- **Subscriptions**: `unraid_mcp/subscriptions/` - Real-time WebSocket subscriptions and diagnostics
|
|
73
|
+
- **Tools**: `unraid_mcp/tools/` - Domain-specific tool implementations
|
|
74
|
+
- **GraphQL Client**: Uses httpx for async HTTP requests to Unraid API
|
|
75
|
+
- **Version Helper**: `unraid_mcp/version.py` - Reads version from package metadata via importlib
|
|
76
|
+
|
|
77
|
+
### Key Design Patterns
|
|
78
|
+
- **Consolidated Action Pattern**: Each tool uses `action: Literal[...]` parameter to expose multiple operations via a single MCP tool, reducing context window usage
|
|
79
|
+
- **Pre-built Query Dicts**: `QUERIES` and `MUTATIONS` dicts prevent GraphQL injection and organize operations
|
|
80
|
+
- **Destructive Action Safety**: `DESTRUCTIVE_ACTIONS` sets require `confirm=True` for dangerous operations
|
|
81
|
+
- **Modular Architecture**: Clean separation of concerns across focused modules
|
|
82
|
+
- **Error Handling**: Uses ToolError for user-facing errors, detailed logging for debugging
|
|
83
|
+
- **Timeout Management**: Custom timeout configurations for different query types (90s for disk ops)
|
|
84
|
+
- **Data Processing**: Tools return both human-readable summaries and detailed raw data
|
|
85
|
+
- **Health Monitoring**: Comprehensive health check tool for system monitoring
|
|
86
|
+
- **Real-time Subscriptions**: WebSocket-based live data streaming
|
|
87
|
+
- **Persistent Subscription Manager**: `live` action subactions use a shared `SubscriptionManager`
|
|
88
|
+
that maintains persistent WebSocket connections. Resources serve cached data via
|
|
89
|
+
`subscription_manager.get_resource_data(action)`. A "connecting" placeholder is returned
|
|
90
|
+
while the subscription starts — callers should retry in a moment. When
|
|
91
|
+
`UNRAID_AUTO_START_SUBSCRIPTIONS=false`, resources fall back to on-demand `subscribe_once`.
|
|
92
|
+
|
|
93
|
+
### Tool Categories (3 Tools: 1 Primary + 2 Diagnostic)
|
|
94
|
+
|
|
95
|
+
The server registers **3 MCP tools**:
|
|
96
|
+
- **`unraid`** — primary tool with `action` (domain) + `subaction` (operation) routing, 107 subactions. Call it as `unraid(action="docker", subaction="list")`.
|
|
97
|
+
- **`diagnose_subscriptions`** — inspect subscription connection states, errors, and WebSocket URLs.
|
|
98
|
+
- **`test_subscription_query`** — test a specific GraphQL subscription query (allowlisted fields only).
|
|
99
|
+
|
|
100
|
+
| action | subactions |
|
|
101
|
+
|--------|-----------|
|
|
102
|
+
| **system** (18) | overview, array, network, registration, variables, metrics, services, display, config, online, owner, settings, server, servers, flash, ups_devices, ups_device, ups_config |
|
|
103
|
+
| **health** (4) | check, test_connection, diagnose, setup |
|
|
104
|
+
| **array** (13) | 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* |
|
|
105
|
+
| **disk** (6) | shares, disks, disk_details, log_files, logs, flash_backup* |
|
|
106
|
+
| **docker** (7) | list, details, start, stop, restart, networks, network_details |
|
|
107
|
+
| **vm** (9) | list, details, start, stop, pause, resume, force_stop*, reboot, reset* |
|
|
108
|
+
| **notification** (12) | overview, list, create, archive, mark_unread, recalculate, archive_all, archive_many, unarchive_many, unarchive_all, delete*, delete_archived* |
|
|
109
|
+
| **key** (7) | list, get, create, update, delete*, add_role, remove_role |
|
|
110
|
+
| **plugin** (3) | list, add, remove* |
|
|
111
|
+
| **rclone** (4) | list_remotes, config_form, create_remote, delete_remote* |
|
|
112
|
+
| **setting** (2) | update, configure_ups* |
|
|
113
|
+
| **customization** (5) | theme, public_theme, is_initial_setup, sso_enabled, set_theme |
|
|
114
|
+
| **oidc** (5) | providers, provider, configuration, public_providers, validate_session |
|
|
115
|
+
| **user** (1) | me |
|
|
116
|
+
| **live** (11) | cpu, memory, cpu_telemetry, array_state, parity_progress, ups_status, notifications_overview, notification_feed, log_tail, owner, server_status |
|
|
117
|
+
|
|
118
|
+
`*` = destructive, requires `confirm=True`
|
|
119
|
+
|
|
120
|
+
### Destructive Actions (require `confirm=True`)
|
|
121
|
+
- **array**: stop_array, remove_disk, clear_disk_stats
|
|
122
|
+
- **vm**: force_stop, reset
|
|
123
|
+
- **notification**: delete, delete_archived
|
|
124
|
+
- **rclone**: delete_remote
|
|
125
|
+
- **key**: delete
|
|
126
|
+
- **disk**: flash_backup
|
|
127
|
+
- **setting**: configure_ups
|
|
128
|
+
- **plugin**: remove
|
|
129
|
+
|
|
130
|
+
### Environment Variable Hierarchy
|
|
131
|
+
The server loads environment variables from multiple locations in order:
|
|
132
|
+
1. `~/.unraid-mcp/.env` (primary — canonical credentials dir, all runtimes)
|
|
133
|
+
2. `~/.unraid-mcp/.env.local` (local overrides, only used if primary is absent)
|
|
134
|
+
3. `../.env.local` (project root local overrides)
|
|
135
|
+
4. `../.env` (project root fallback)
|
|
136
|
+
5. `unraid_mcp/.env` (last resort)
|
|
137
|
+
|
|
138
|
+
### Error Handling Strategy
|
|
139
|
+
- GraphQL errors are converted to ToolError with descriptive messages
|
|
140
|
+
- HTTP errors include status codes and response details
|
|
141
|
+
- Network errors are caught and wrapped with connection context
|
|
142
|
+
- All errors are logged with full context for debugging
|
|
143
|
+
|
|
144
|
+
### Middleware Chain
|
|
145
|
+
`server.py` wraps all tools in a 4-layer stack (order matters — outermost first):
|
|
146
|
+
1. **LoggingMiddleware** — logs every `tools/call` and `resources/read` with duration
|
|
147
|
+
2. **ErrorHandlingMiddleware** — converts unhandled exceptions to proper MCP errors
|
|
148
|
+
3. **SlidingWindowRateLimitingMiddleware** — 540 req/min sliding window
|
|
149
|
+
4. **ResponseLimitingMiddleware** — truncates responses > 512 KB with a clear suffix
|
|
150
|
+
|
|
151
|
+
Note: `ResponseCachingMiddleware` was removed — the consolidated `unraid` tool mixes reads and mutations under one name, making per-subaction cache exclusion impossible.
|
|
152
|
+
|
|
153
|
+
### Performance Considerations
|
|
154
|
+
- Increased timeouts for disk operations (90s read timeout)
|
|
155
|
+
- Selective queries to avoid GraphQL type overflow issues
|
|
156
|
+
- Optional caching controls for Docker container queries
|
|
157
|
+
- Log file overwrite at 10MB cap to prevent disk space issues
|
|
158
|
+
|
|
159
|
+
## Critical Gotchas
|
|
160
|
+
|
|
161
|
+
### Mutation Handler Ordering
|
|
162
|
+
**Mutation handlers MUST return before the domain query dict lookup.** Mutations are not in the domain `_*_QUERIES` dicts (e.g., `_DOCKER_QUERIES`, `_ARRAY_QUERIES`) — reaching that line for a mutation subaction causes a `KeyError`. Always add early-return `if subaction == "mutation_name": ... return` blocks BEFORE the queries lookup.
|
|
163
|
+
|
|
164
|
+
### Test Patching
|
|
165
|
+
- Patch at the **tool module level**: `unraid_mcp.tools.unraid.make_graphql_request` (not core)
|
|
166
|
+
- `conftest.py`'s `mock_graphql_request` patches the core module — wrong for tool-level tests
|
|
167
|
+
- Use `conftest.py`'s `make_tool_fn()` helper or local `_make_tool()` pattern
|
|
168
|
+
|
|
169
|
+
### Test Suite Structure
|
|
170
|
+
```
|
|
171
|
+
tests/
|
|
172
|
+
├── conftest.py # Shared fixtures + make_tool_fn() helper
|
|
173
|
+
├── test_*.py # Unit tests (mock at tool module level)
|
|
174
|
+
├── http_layer/ # httpx-level request/response tests (respx)
|
|
175
|
+
├── integration/ # WebSocket subscription lifecycle tests (slow)
|
|
176
|
+
├── safety/ # Destructive action guard tests
|
|
177
|
+
├── schema/ # GraphQL query validation (119 tests)
|
|
178
|
+
├── contract/ # Response shape contract tests
|
|
179
|
+
└── property/ # Input validation property-based tests
|
|
180
|
+
```
|
|
181
|
+
|
|
182
|
+
### Running Targeted Tests
|
|
183
|
+
```bash
|
|
184
|
+
uv run pytest tests/safety/ # Destructive action guards only
|
|
185
|
+
uv run pytest tests/schema/ # GraphQL query validation only
|
|
186
|
+
uv run pytest tests/http_layer/ # HTTP/httpx layer only
|
|
187
|
+
uv run pytest tests/test_docker.py # Single tool only
|
|
188
|
+
uv run pytest -x # Fail fast on first error
|
|
189
|
+
```
|
|
190
|
+
|
|
191
|
+
### Scripts
|
|
192
|
+
```bash
|
|
193
|
+
# HTTP smoke-test against a live server (non-destructive actions, all domains)
|
|
194
|
+
./tests/mcporter/test-actions.sh [MCP_URL] # default: http://localhost:6970/mcp
|
|
195
|
+
|
|
196
|
+
# stdio smoke-test, no running server needed (good for CI)
|
|
197
|
+
./tests/mcporter/test-tools.sh [--parallel] [--timeout-ms N] [--verbose]
|
|
198
|
+
|
|
199
|
+
# Destructive action smoke-test (confirms guard blocks without confirm=True)
|
|
200
|
+
./tests/mcporter/test-destructive.sh [MCP_URL]
|
|
201
|
+
```
|
|
202
|
+
See `tests/mcporter/README.md` for transport differences and `docs/DESTRUCTIVE_ACTIONS.md` for exact destructive-action test commands.
|
|
203
|
+
|
|
204
|
+
### API Reference Docs
|
|
205
|
+
- `docs/UNRAID_API_COMPLETE_REFERENCE.md` — Full GraphQL schema reference
|
|
206
|
+
- `docs/UNRAID_API_OPERATIONS.md` — All supported operations with examples
|
|
207
|
+
- `docs/MARKETPLACE.md` — Plugin marketplace listing and publishing guide
|
|
208
|
+
- `docs/PUBLISHING.md` — Step-by-step instructions for publishing to Claude plugin registry
|
|
209
|
+
|
|
210
|
+
Use these when adding new queries/mutations.
|
|
211
|
+
|
|
212
|
+
### Version Bumps
|
|
213
|
+
When bumping the version, **always update both files** — they must stay in sync:
|
|
214
|
+
- `pyproject.toml` → `version = "X.Y.Z"` under `[project]`
|
|
215
|
+
- `.claude-plugin/plugin.json` → `"version": "X.Y.Z"`
|
|
216
|
+
|
|
217
|
+
### Credential Storage (`~/.unraid-mcp/.env`)
|
|
218
|
+
All runtimes (plugin, direct `uv run`) load credentials from `~/.unraid-mcp/.env`.
|
|
219
|
+
- **Plugin/direct:** `unraid action=health subaction=setup` writes this file automatically via elicitation,
|
|
220
|
+
**Safe to re-run**: always prompts for confirmation before overwriting existing credentials,
|
|
221
|
+
whether the connection is working or not (failed probe may be a transient outage, not bad creds).
|
|
222
|
+
or manual: `mkdir -p ~/.unraid-mcp && cp .env.example ~/.unraid-mcp/.env` then edit.
|
|
223
|
+
- **No symlinks needed.** Version bumps do not affect this path.
|
|
224
|
+
- **Permissions:** dir=700, file=600 (set automatically by elicitation; set manually if
|
|
225
|
+
using `cp`: `chmod 700 ~/.unraid-mcp && chmod 600 ~/.unraid-mcp/.env`).
|
|
226
|
+
|
|
227
|
+
### Symlinks
|
|
228
|
+
`AGENTS.md` and `GEMINI.md` are symlinks to `CLAUDE.md` for Codex/Gemini compatibility:
|
|
229
|
+
```bash
|
|
230
|
+
ln -sf CLAUDE.md AGENTS.md && ln -sf CLAUDE.md GEMINI.md
|
|
231
|
+
```
|
|
232
|
+
|
|
233
|
+
<!-- BEGIN BEADS INTEGRATION v:1 profile:minimal hash:ca08a54f -->
|
|
234
|
+
## Beads Issue Tracker
|
|
235
|
+
|
|
236
|
+
This project uses **bd (beads)** for issue tracking. Run `bd prime` to see full workflow context and commands.
|
|
237
|
+
|
|
238
|
+
### Quick Reference
|
|
239
|
+
|
|
240
|
+
```bash
|
|
241
|
+
bd ready # Find available work
|
|
242
|
+
bd show <id> # View issue details
|
|
243
|
+
bd update <id> --claim # Claim work
|
|
244
|
+
bd close <id> # Complete work
|
|
245
|
+
```
|
|
246
|
+
|
|
247
|
+
### Rules
|
|
248
|
+
|
|
249
|
+
- Use `bd` for ALL task tracking — do NOT use TodoWrite, TaskCreate, or markdown TODO lists
|
|
250
|
+
- Run `bd prime` for detailed command reference and session close protocol
|
|
251
|
+
- Use `bd remember` for persistent knowledge — do NOT use MEMORY.md files
|
|
252
|
+
|
|
253
|
+
## Session Completion
|
|
254
|
+
|
|
255
|
+
**When ending a work session**, you MUST complete ALL steps below. Work is NOT complete until `git push` succeeds.
|
|
256
|
+
|
|
257
|
+
**MANDATORY WORKFLOW:**
|
|
258
|
+
|
|
259
|
+
1. **File issues for remaining work** - Create issues for anything that needs follow-up
|
|
260
|
+
2. **Run quality gates** (if code changed) - Tests, linters, builds
|
|
261
|
+
3. **Update issue status** - Close finished work, update in-progress items
|
|
262
|
+
4. **PUSH TO REMOTE** - This is MANDATORY:
|
|
263
|
+
```bash
|
|
264
|
+
git pull --rebase
|
|
265
|
+
bd dolt push
|
|
266
|
+
git push
|
|
267
|
+
git status # MUST show "up to date with origin"
|
|
268
|
+
```
|
|
269
|
+
5. **Clean up** - Clear stashes, prune remote branches
|
|
270
|
+
6. **Verify** - All changes committed AND pushed
|
|
271
|
+
7. **Hand off** - Provide context for next session
|
|
272
|
+
|
|
273
|
+
**CRITICAL RULES:**
|
|
274
|
+
- Work is NOT complete until `git push` succeeds
|
|
275
|
+
- NEVER stop before pushing - that leaves work stranded locally
|
|
276
|
+
- NEVER say "ready to push when you are" - YOU must push
|
|
277
|
+
- If push fails, resolve and retry until it succeeds
|
|
278
|
+
<!-- END BEADS INTEGRATION -->
|
|
279
|
+
|
|
280
|
+
|
|
281
|
+
## Version Bumping
|
|
282
|
+
|
|
283
|
+
**Every feature branch push MUST bump the version in ALL version-bearing files.**
|
|
284
|
+
|
|
285
|
+
Bump type is determined by the commit message prefix:
|
|
286
|
+
- `feat!:` or `BREAKING CHANGE` → **major** (X+1.0.0)
|
|
287
|
+
- `feat` or `feat(...)` → **minor** (X.Y+1.0)
|
|
288
|
+
- Everything else (`fix`, `chore`, `refactor`, `test`, `docs`, etc.) → **patch** (X.Y.Z+1)
|
|
289
|
+
|
|
290
|
+
**Files to update (if they exist in this repo):**
|
|
291
|
+
- `Cargo.toml` — `version = "X.Y.Z"` in `[package]`
|
|
292
|
+
- `package.json` — `"version": "X.Y.Z"`
|
|
293
|
+
- `pyproject.toml` — `version = "X.Y.Z"` in `[project]`
|
|
294
|
+
- `.claude-plugin/plugin.json` — `"version": "X.Y.Z"`
|
|
295
|
+
- `.codex-plugin/plugin.json` — `"version": "X.Y.Z"`
|
|
296
|
+
- `gemini-extension.json` — `"version": "X.Y.Z"`
|
|
297
|
+
- `README.md` — version badge or header
|
|
298
|
+
- `CHANGELOG.md` — new entry under the bumped version
|
|
299
|
+
|
|
300
|
+
All files MUST have the same version. Never bump only one file.
|
|
301
|
+
CHANGELOG.md must have an entry for every version bump.
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
CLAUDE.md
|
unraid_mcp-1.2.2/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 jmagar
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|