mirror of
https://github.com/jmagar/unraid-mcp
synced 2026-04-21 13:37:53 +00:00
16 KiB
16 KiB
Changelog
All notable changes to this project will be documented in this file.
The format is based on Keep a Changelog, and this project adheres to Semantic Versioning.
[Unreleased]
[1.2.4] - 2026-04-04
Added
- Comprehensive test suite: Added tests for core modules, configuration, validation, subscriptions, and edge cases
- Test coverage documentation:
tests/TEST_COVERAGE.mdwith coverage map and gap analysis
Changed
- Documentation: Comprehensive updates across CLAUDE.md, README, and reference docs
- Version sync: Fixed
pyproject.tomlversion mismatch (was 1.2.2, now aligned with all manifests at 1.2.4)
[1.2.3] - 2026-04-03
Changed
- hooks/hooks.json: Removed
SessionStarthook —sync-env.shwas exiting 1 on every session start whenUNRAID_MCP_BEARER_TOKENwas not set via plugin userConfig, causing startup hook errors.
[1.2.2] - 2026-04-03
Changed
- README: Restructured to a compact reference format — removed marketing prose, replaced with minimal tables and direct headings.
- CHANGELOG: Adopted Keep a Changelog format.
- Version sync:
.codex-plugin/plugin.jsonandgemini-extension.jsonbrought to 1.2.2 (were stale at 1.2.0).
[1.2.1] - 2026-04-03
Fixed
- OAuth discovery 401 cascade (fixes #17):
BearerAuthMiddlewarewas blockingGET /.well-known/oauth-protected-resource, causing MCP clients (e.g. Claude Code) to surface a generic "unknown error" after receiving a 401. AddedWellKnownMiddleware(RFC 9728) placed beforeBearerAuthMiddlewarethat returns resource metadata withbearer_methods_supported: ["header"]and noauthorization_servers— telling clients to use a pre-configured Bearer token rather than attempting an OAuth flow.
Added
docs/AUTHENTICATION.md: New setup guide covering token generation, server config, Claude Code client config ("Authorization": "Bearer <token>"header format), and troubleshooting for common 401 errors.- README Authentication section: Added the missing
## 🔐 Authenticationsection (was linked in TOC but not present) with quick-start examples and a link to the full guide.
[1.2.0] - 2026-03-30
Added
- HTTP bearer token auth: ASGI-level
BearerAuthMiddleware(pure__call__pattern, no BaseHTTPMiddleware overhead) enforcesAuthorization: Bearer <token>on all HTTP requests. RFC 6750 compliant — missing header returnsWWW-Authenticate: Bearer realm="unraid-mcp", invalid token addserror="invalid_token". - Auto token generation: On first HTTP startup with no token configured, a
secrets.token_urlsafe(32)token is generated, written to~/.unraid-mcp/.env(mode 600), announced once on STDERR without printing the secret, and removed fromos.environso subprocesses cannot inherit it. - Per-IP rate limiting: 60 failed auth attempts per 60 seconds → 429 with
Retry-After: 60header. - Gateway escape hatch:
UNRAID_MCP_DISABLE_HTTP_AUTH=truebypasses bearer auth for users who handle authentication at a reverse proxy / gateway layer. - Startup guard: Server refuses to start in HTTP mode (
streamable-http/sse) if no token is set andUNRAID_MCP_DISABLE_HTTP_AUTHis not explicitly enabled. - Tests: 23 new tests in
tests/test_auth.pycovering pass-through scopes, 401/429 responses, RFC 6750 header differentiation, per-IP rate limiting, window expiry, token generation, and startup guard.
Changed
- Default transport:
stdio→streamable-http. Users running directly (not via Claude Desktop plugin) will now get an HTTP server by default. To keep stdio behaviour, setUNRAID_MCP_TRANSPORT=stdio. The Claude Desktop plugin (plugin.json) is unaffected — it hardcodesstdio. .env.example: Updated to document new auth variables (UNRAID_MCP_BEARER_TOKEN,UNRAID_MCP_DISABLE_HTTP_AUTH) and updated default transport comment.
Breaking Changes
- Default transport is now
streamable-http. Any script or service that relied on the default beingstdiomust explicitly setUNRAID_MCP_TRANSPORT=stdio.
[1.1.6] - 2026-03-30
Security
- Path traversal:
flash_backupsource path now validated afterposixpath.normpath(not before) — raw-string..check was bypassable via encoded sequences likefoo/bar/../..; null byte guard added - Key validation:
DANGEROUS_KEY_PATTERNnow blocks space (0x20) and DEL (0x7f) in addition to existing shell metacharacters; applies to both rclone and settings key validation
Fixed
- Settings validation:
configure_upsinput now validated via_validate_settings_inputbefore mutation — was previously passing unvalidated dict directly to GraphQL - Subscription locks:
_start_onelast_errorwrite andstop_all()keys snapshot both now take_task_lockto prevent concurrent write/read races - Keepalive handling: Removed
"ping"from keepaliveelif— ping messages require a pong response, not silent discard; only"ka"and"pong"are silently dropped - Middleware import:
middleware_refs.pyErrorHandlingMiddlewareimport changed fromTYPE_CHECKING-only to unconditional —isinstance()calls at runtime were silently broken - Health reverse map:
_STATUS_FROM_SEVERITYdict hoisted to module level — was being rebuilt on every_comprehensive_health_checkcall
Changed
- Log content cap:
_cap_log_contentnow skipped for non-log subscriptions (onlylog_tail/logFileSubscriptionhavecontentfields) — reduces unnecessary dict key lookups on every WebSocket message - Live assertion:
_handle_livenow raisesRuntimeErrorat import time ifCOLLECT_ACTIONScontains keys not in_HANDLED_COLLECT_SUBACTIONS— catches handler omissions before runtime - Subscription name guard:
start_subscriptionvalidates name matches^[a-zA-Z0-9_]+$before use as WebSocket message ID
Added
- Tests: 27 parametrized tests for
DANGEROUS_KEY_PATTERNcovering all documented dangerous characters and safe key examples (tests/test_validation.py) - Tests:
test_check_api_error_wrapped_tool_error— verifies health check returns{status: unhealthy}whenmake_graphql_requestraisesToolErrorwrappinghttpx.ConnectError
[1.1.5] - 2026-03-27
Added
- Beads issue tracking:
bd init— Dolt-backed issue tracker with prefixunraid-mcp-<hash>, hooks, and AGENTS.md integration - Lavra project config:
.lavra/config/project-setup.md— stackpython, review agents (kieran-python-reviewer, code-simplicity-reviewer, security-sentinel, performance-oracle) - Codebase profile:
.lavra/config/codebase-profile.md— auto-generated stack/architecture/conventions reference for planning and review commands
Changed
.gitignore: Added lavra session-state exclusion (.lavra/memory/session-state.md) and beads-related entriesCLAUDE.md: Added beads workflow integration block with mandatorybdusage rules and session completion protocol
[1.1.4] - 2026-03-25
Changed
- Plugin branding:
displayNameset tounRAIDinplugin.jsonandmarketplace.json - Plugin description: Expanded to list all 3 tools and all 15 action domains with full subaction inventory (107 subactions, destructive actions marked with
*)
[1.1.3] - 2026-03-24
Fixed
- Docs accuracy:
disk/logsdocs corrected to uselog_path/tail_linesparameters (werepath/lines) - Docs accuracy:
rclone/create_remotedocs corrected toprovider_type/config_data(weretype/fields) - Docs accuracy:
setting/updatedocs corrected tosettings_inputparameter (wassettings) - Docs accuracy:
key/createnow documentsrolesas optional;add_role/remove_rolecorrected toroles(plural) - Docs accuracy:
oidc/validate_sessionnow documents requiredtokenparameter - Docs accuracy:
parity_startquick-reference example now includes requiredcorrect=False - Docs accuracy:
log_tailREADME example now includes requiredpath="/var/log/syslog" - Docs accuracy:
live/parity_progressadded to event-driven subscriptions list in troubleshooting guide - Docs accuracy:
live/array_statewording softened — "may show connecting indefinitely" vs "will always show" - Markdown:
endpoints.mdtop-level heading moved before blockquote disclaimer (MD041) - Tests:
test_resources.pynow uses_get_resource()helper instead of rawmcp.providers[0]._components[...]access; isolates FastMCP internals to one location
[1.1.2] - 2026-03-23
Security
- Path traversal: Removed
/mnt/from_ALLOWED_LOG_PREFIXES— was exposing all Unraid user shares to path-based reads - Path traversal: Added early
..detection fordisk/logsandlive/log_tailbefore any filesystem access; added/boot/prefix restriction forflash_backupsource paths - Timing-safe auth:
verify_tokennow useshmac.compare_digestinstead of==to prevent timing oracle attacks on API key comparison - Traceback leak:
include_tracebackinErrorHandlingMiddlewareis now gated onDEBUGlog level; production deployments no longer expose stack traces
Fixed
- Health check:
_comprehensive_health_checknow re-raisesCredentialsNotConfiguredErrorinstead of swallowing it into a generic unhealthy status - UPS device query: Removed non-existent
nominalPowerandcurrentPowerfields fromups_devicequery — every call was failing against the live API - Stale credential bindings: Subscription modules (
manager.py,snapshot.py,utils.py,diagnostics.py) previously capturedUNRAID_API_KEY/UNRAID_API_URLat import time; replaced with_settings.ATTRcall-time access soapply_runtime_config()updates propagate correctly after credential elicitation
Added
- CI pipeline:
.github/workflows/ci.ymlwith 5 jobs — lint (ruff), typecheck (ty), test (pytest -m "not integration"), version-sync check, anduv auditdependency scan - Coverage threshold:
fail_under = 80added to[tool.coverage.report] - Version sync check:
scripts/validate-marketplace.shnow verifiespyproject.tomlandplugin.jsonversions match
Changed
- Docs: Updated
CLAUDE.md,README.mdto reflect 3 tools (1 primary + 2 diagnostic); corrected system domain count (19→18); fixed scripts comment - Docs:
docs/AUTHENTICATION.mdH1 retitled to "Authentication Setup Guide" - Docs: Added
UNRAID_CREDENTIALS_DIRcommented entry to.env.example - Removed
from __future__ import annotationsfromsnapshot.py(caused TC002 false positives with FastMCP) - Added
# noqa: ASYNC109totimeoutparameters in_handle_liveandunraid()(valid suppressions) - Fixed
start_array*→start_arrayin tool docstring table (start_arrayis not in_ARRAY_DESTRUCTIVE)
Refactored
- Path validation: Extracted
_validate_path()inunraid.py— consolidates traversal check,normpath, and prefix validation used by bothdisk/logsandlive/log_tailinto one place; eliminates duplication - WebSocket auth payload: Extracted
build_connection_init()insubscriptions/utils.py— removes 4 duplicateconnection_initblocks fromsnapshot.py(×2),manager.py, anddiagnostics.py; also fixes a bug indiagnostics.pywherex-api-key: Nonewas sent when no API key was configured - Removed
_LIVE_ALLOWED_LOG_PREFIXESalias — direct reference to_ALLOWED_LOG_PREFIXES - Moved
import hmacto module level inserver.py(was insideverify_tokenhot path)
[1.1.1] - 2026-03-16
Added
- API key auth:
Authorization: Bearer <UNRAID_MCP_API_KEY>bearer token authentication viaApiKeyVerifier— machine-to-machine access without OAuth browser flow - MultiAuth: When both Google OAuth and API key are configured,
MultiAuthaccepts either method - Google OAuth: Full
GoogleProviderintegration — browser-based OAuth 2.0 flow with JWT session tokens;UNRAID_MCP_JWT_SIGNING_KEYfor stable tokens across restarts fastmcp.json: Dev tooling configs for FastMCP
Fixed
- Auth test isolation: use
os.environ[k] = ""instead ofdelenvto prevent dotenv re-injection between test reloads
[1.1.0] - 2026-03-16
Breaking Changes
- Tool consolidation: 15 individual domain tools (
unraid_docker,unraid_vm, etc.) merged into singleunraidtool withaction+subactionrouting- Old:
unraid_docker(action="list") - New:
unraid(action="docker", subaction="list")
- Old:
Added
livetool (11 subactions): Real-time WebSocket subscription snapshots —cpu,memory,cpu_telemetry,array_state,parity_progress,ups_status,notifications_overview,notification_feed,log_tail,owner,server_statuscustomizationtool (5 subactions):theme,public_theme,is_initial_setup,sso_enabled,set_themeplugintool (3 subactions):list,add,removeoidctool (5 subactions):providers,provider,configuration,public_providers,validate_session- Persistent
SubscriptionManager:unraid://live/*MCP resources backed by long-lived WebSocket connections with auto-start and reconnection diagnose_subscriptionsandtest_subscription_querydiagnostic toolsarray: Addedparity_history,start_array,stop_array,add_disk,remove_disk,mount_disk,unmount_disk,clear_disk_statskeys: Addedadd_role,remove_rolesettings: Addedupdate_ssh(confirm required)stop_arrayadded to_ARRAY_DESTRUCTIVEgate_destructive_actionhelper incore/guards.py— centralized elicitation + confirm guard- Full safety test suite:
TestNoGraphQLCallsWhenUnconfirmed(zero-I/O guarantee for all 13 destructive actions)
Fixed
- Removed 29 actions confirmed absent from live API v4.29.2 via GraphQL introspection (Docker organizer mutations,
unassignedDevices,warningsAndAlerts, etc.) log_tailpath validated against allowlist before subscription start- WebSocket auth uses
x-api-keyconnectionParams format
[1.0.0] - 2026-03-14 through 2026-03-15
Breaking Changes
- Credential storage moved to
~/.unraid-mcp/.env(dir 700, file 600); all runtimes load from this path unraid_health(action="setup")is the only tool that triggers credential elicitation; all others propagateCredentialsNotConfiguredError
Added
CredentialsNotConfiguredErrorsentinel — propagates cleanly throughtool_error_handlerwith exact credential path in the error messageis_configured()andapply_runtime_config()insettings.pyfor runtime credential injectionelicit_and_configure()with.envpersistence and confirmation before overwrite- 28 GraphQL mutations across storage, docker, notifications, and new settings tool
- Comprehensive test suite expansion: schema validation (99 tests), HTTP layer (respx), property tests, safety audit, contract tests
Fixed
- Numerous PR review fixes across 50+ commits (CodeRabbit, ChatGPT-Codex review rounds)
- Shell scripts hardened against injection and null guards
- Notification enum validation, subscription lock split, safe_get semantics
[0.6.0] - 2026-03-15
Added
- Subscription byte/line cap to prevent unbounded memory growth
asyncio.timeoutbounds onsubscribe_once/subscribe_collect- Partial auto-start for subscriptions (best-effort on startup)
Fixed
- WebSocket URL scheme handling (
ws:///wss://) flash_backuppath validation and smoke test assertions
[0.5.0] - 2026-03-15
Tool expansion and live subscription foundation.
[0.4.x] - 2026-03-13 through 2026-03-14
Credential elicitation system, per-tool refactors, and mutation additions.
[0.2.x] - 2026-02-15 through 2026-03-13
Initial public release hardening: PR review cycles, test suite expansion, security fixes, plugin manifest.
[0.1.0] - 2026-02-08
Added
- Consolidated 26 tools into 10 tools with 90 actions
- FastMCP architecture migration with
uvtoolchain - Docker Compose support with health checks
- WebSocket subscription infrastructure
Format: Keep a Changelog. Versioning: Semantic Versioning.