Jacob Magar
dab1cd6995
refactor(tools)!: consolidate 15 individual tools into single unified unraid tool
...
BREAKING CHANGE: Replaces 15 separate MCP tools (unraid_info, unraid_array,
unraid_storage, unraid_docker, unraid_vm, unraid_notifications, unraid_rclone,
unraid_users, unraid_keys, unraid_health, unraid_settings, unraid_customization,
unraid_plugins, unraid_oidc, unraid_live) with a single `unraid` tool using
action (domain) + subaction (operation) routing.
New interface: unraid(action="system", subaction="overview") replaces
unraid_info(action="overview"). All 15 domains and ~108 subactions preserved.
- Add unraid_mcp/tools/unraid.py (1891 lines, all domains consolidated)
- Remove 15 individual tool files
- Update tools/__init__.py to register single unified tool
- Update server.py for new tool registration pattern
- Update subscriptions/manager.py and resources.py for new tool names
- Update all 25 test files + integration/contract/safety/schema/property tests
- Update mcporter smoke-test script for new tool interface
- Bump version 0.6.0 → 1.0.0
Co-authored-by: Claude <noreply@anthropic.com >
2026-03-16 02:29:57 -04:00
Jacob Magar
569956ade0
fix(docker): remove 19 stale actions absent from live API v4.29.2
...
Only list, details, start, stop, restart, networks, network_details
remain. Removed logs, port_conflicts, check_updates from QUERIES and all
organizer mutations + pause/unpause/remove/update/update_all from
MUTATIONS. DESTRUCTIVE_ACTIONS is now an empty set.
2026-03-15 21:58:50 -04:00
Jacob Magar
252ec520d1
fix(lint): remove __future__ annotations from new tools, fix 4 failing tests
...
- Remove `from __future__ import annotations` from array.py, live.py,
oidc.py, plugins.py to match existing tool pattern and resolve TC002
ruff errors (fastmcp imports only needed in annotations under PEP 563)
- Add `# noqa: ASYNC109` to live.py timeout parameter (asyncio.timeout
already used internally)
- Fix test_network_sends_correct_query: query name is GetNetworkInfo
- Fix test_delete_requires_confirm: match "not confirmed" not "destructive"
- Fix test_destructive_set_matches_audit[settings]: add setup_remote_access
and enable_dynamic_remote_access to KNOWN_DESTRUCTIVE
- Fix test_logs: update mock to dict format {lines: [{timestamp, message}]}
742 tests passing, ruff clean
2026-03-15 19:57:46 -04:00
Jacob Magar
8eab5992ba
fix: resolve 21 pre-existing schema field drift failures
...
- Fix InfoOs: remove codepage (not in schema, codename already queried)
- Fix InfoVersions: use core { unraid api kernel } and packages { ... }
subtype structure instead of flat field list; remove non-existent fields
- Fix Info: remove apps field from overview query (not in Info type)
- Fix Connect query: replace missing status/sandbox/flashGuid with
dynamicRemoteAccess { enabledType runningType error }
- Fix CpuUtilization: replace used with percentTotal
- Fix Service: remove state field, add online and version
- Fix Server: replace ip/port with wanip/lanip/localurl/remoteurl
- Fix Flash: remove size field (not in schema)
- Fix UPSDevice: replace flat runtime/charge/load/voltage/frequency/temperature
with nested battery { chargeLevel estimatedRuntime health } and
power { loadPercentage inputVoltage outputVoltage } sub-types
- Fix ups_device variable type: PrefixedID! -> String! (schema uses String!)
- Fix UPSConfiguration: replace enabled/mode/cable/driver/port with
service/upsCable/upsType/device/batteryLevel/minutes/timeout/killUps/upsName
- Fix storage unassigned query: unassignedDevices not in schema, use disks
- Fix docker logs: add subfield selection for DockerContainerLogs type
- Fix docker networks/network_details: move from root dockerNetworks/dockerNetwork
to docker { networks { ... } }; filter by ID client-side for network_details
- Fix docker port_conflicts: replace containerName/port/conflictsWith with
containerPorts { privatePort type containers { id name } } and lanPorts
- Fix docker check_updates: replace id/updateAvailable/currentVersion/latestVersion
with name/updateStatus per ExplicitStatusItem schema type
- Fix keys queries: add subfield selection for permissions { resource actions },
remove lastUsed (not on ApiKey type)
- Fix health.py comprehensive check: use versions { core { unraid } }
- Update docker mutations coverage assertion to include 11 organizer mutations
- Update test_networks mock to match new docker { networks } response shape
- Update health.py runtime accessor to follow new versions.core.unraid path
2026-03-13 11:19:40 -04:00
Jacob Magar
482da4485d
fix: flash_backup validation, smoke test assertions, docker/notification test coverage
...
- storage.py: validate initiateFlashBackup response before returning success=True
- test-tools.sh: remove set -e/inherit_errexit; add success assertion to smoke tests
- test_destructive_guards.py: add confirm-guard tests for new docker destructive actions
- test_docker.py: assert mutation variables in organizer tests; add items branch test
- test_query_validation.py: add 5 missing notification mutation schema test methods
- test_notifications.py: use lowercase importance to test uppercasing logic
Resolves review threads PRRT_kwDOO6Hdxs50FgPb PRRT_kwDOO6Hdxs50FgO4 PRRT_kwDOO6Hdxs50FgO8 PRRT_kwDOO6Hdxs50FgPI PRRT_kwDOO6Hdxs50FgPL PRRT_kwDOO6Hdxs50FgPm PRRT_kwDOO6Hdxs50E2iK PRRT_kwDOO6Hdxs50E2im
2026-03-13 10:41:43 -04:00
Jacob Magar
9aee3a2448
feat: add 28 GraphQL mutations across storage, info, docker, and new settings tool
...
- storage: flash_backup mutation (initiates rclone flash backup, destructive)
- info: update_server and update_ssh mutations
- docker: 11 organizer mutations (create_folder, set_folder_children,
delete_entries, move_to_folder, move_to_position, rename_folder,
create_folder_with_items, update_view_prefs, sync_templates,
reset_template_mappings, refresh_digests); delete_entries and
reset_template_mappings added to DESTRUCTIVE_ACTIONS
- settings: new unraid_settings tool with 9 mutations (update,
update_temperature, update_time, configure_ups, update_api,
connect_sign_in, connect_sign_out, setup_remote_access,
enable_dynamic_remote_access); registered in server.py
- tests: 82 new tests (28 settings, 23 docker organizer, 7 info, 6 storage
+ 18 existing fixes for notification regex and safety audit list)
- bump version 0.3.0 → 0.4.0 (11 tools, ~104 actions)
🤖 Generated with Claude Code
Co-Authored-By: Claude <noreply@anthropic.com >
2026-03-13 03:03:37 -04:00
Jacob Magar
1751bc2984
fix: apply all PR review agent findings (silent failures, type safety, test gaps)
...
Addresses issues found by 4 parallel review agents (code-reviewer,
silent-failure-hunter, type-design-analyzer, pr-test-analyzer).
Source fixes:
- core/utils.py: add public safe_display_url() (moved from tools/health.py)
- core/client.py: rename _redact_sensitive → redact_sensitive (public API)
- core/types.py: add SubscriptionData.__post_init__ for tz-aware datetime
enforcement; remove 6 unused type aliases (SystemHealth, APIResponse, etc.)
- subscriptions/manager.py: add exc_info=True to both except-Exception blocks;
add except ValueError break-on-config-error before retry loop; import
redact_sensitive by new public name
- subscriptions/resources.py: re-raise in autostart_subscriptions() so
ensure_subscriptions_started() doesn't permanently set _subscriptions_started
- subscriptions/diagnostics.py: except ToolError: raise before broad except;
use safe_display_url() instead of raw URL slice
- tools/health.py: move _safe_display_url to core/utils; add exc_info=True;
raise ToolError (not return dict) on ImportError
- tools/info.py: use get_args(INFO_ACTIONS) instead of INFO_ACTIONS.__args__
- tools/{array,docker,keys,notifications,rclone,storage,virtualization}.py:
add Literal-vs-ALL_ACTIONS sync check at import time
Test fixes:
- test_health.py: import safe_display_url from core.utils; update
test_diagnose_import_error_internal to expect ToolError (not error dict)
- test_storage.py: add 3 safe_get tests for zero/False/empty-string values
- test_subscription_manager.py: add TestCapLogContentSingleMassiveLine (2 tests)
- test_client.py: rename _redact_sensitive → redact_sensitive; add tests for
new sensitive keys and is_cacheable explicit-keyword form
2026-02-19 02:23:04 -05:00
Jacob Magar
f76e676fd4
test: close critical coverage gaps and harden PR review fixes
...
Critical bug fixes from PR review agents:
- client.py: eager asyncio.Lock init, Final[frozenset] for _SENSITIVE_KEYS,
explicit 429 ToolError after retries exhausted, removed lazy _get_client_lock()
and _RateLimiter._get_lock() patterns
- exceptions.py: use builtin TimeoutError (UP041), explicit handler before broad
except so asyncio timeouts get descriptive messages
- docker.py: add update_all to DESTRUCTIVE_ACTIONS (was missing), remove dead
_MUTATION_ACTIONS constant
- manager.py: _cap_log_content returns new dict (immutable), lock write to
resource_data, clean dead task from active_subscriptions after loop exits
- diagnostics.py: fix inaccurate comment about semicolon injection guard
- health.py: narrow except ValueError in _safe_display_url, fix TODO comment
New test coverage (98 tests added, 529 → 598 passing):
- test_subscription_validation.py: 27 tests for _validate_subscription_query
(security-critical allow-list, forbidden keyword guards, word-boundary test)
- test_subscription_manager.py: 12 tests for _cap_log_content
(immutability, truncation, nesting, passthrough)
- test_client.py: +57 tests — _RateLimiter (token math, refill, sleep-on-empty),
_QueryCache (TTL, invalidation, is_cacheable), 429 retry loop (1/2/3 failures)
- test_health.py: +10 tests for _safe_display_url (credential strip, port,
path/query removal, malformed IPv6 → <unparseable>)
- test_notifications.py: +7 importance enum and field length validation tests
- test_rclone.py: +7 _validate_config_data security guard tests
- test_storage.py: +15 (tail_lines bounds, format_kb, safe_get)
- test_docker.py: update_all now requires confirm=True + new guard test
- test_destructive_guards.py: update audit to include update_all
Co-authored-by: Claude <noreply@anthropic.com >
2026-02-18 01:28:40 -05:00
Jacob Magar
a0721e38dd
fix: address PR review comments on test suite
...
- Rename test_start_http_401_unauthorized to test_list_http_401_unauthorized
to match the actual action="list" being tested (threads #19 , #23 )
- Use consistent PrefixedID format ("a"*64+":local") in test_start_container
instead of "abc123def456"*4 concatenation (thread #37 )
- Refactor container_actions_require_id to use @pytest.mark.parametrize
so each action runs independently (thread #18 )
- Fix docstring claiming ToolError for test that asserts success in
test_stop_mutation_returns_null (thread #26 )
- Fix inaccurate comment about `in` operator checking truthiness;
it checks key existence (thread #25 )
- Add edge case tests for temperature=0, temperature=null, and
logFile=null in test_storage.py (thread #31 )
Resolves review threads: PRRT_kwDOO6Hdxs5uvO2-, PRRT_kwDOO6Hdxs5uvOcf,
PRRT_kwDOO6Hdxs5uu7zx, PRRT_kwDOO6Hdxs5uvO28, PRRT_kwDOO6Hdxs5uvOcp,
PRRT_kwDOO6Hdxs5uvOcn, PRRT_kwDOO6Hdxs5uvKr3
2026-02-15 23:02:32 -05:00
Jacob Magar
abb7915672
feat: harden API safety and expand command docs with full test coverage
2026-02-15 22:15:51 -05:00
Jacob Magar
37e9424a5c
fix: address 54 MEDIUM/LOW priority PR review issues
...
Comprehensive fixes across Python code, shell scripts, and documentation
addressing all remaining MEDIUM and LOW priority review comments.
Python Code Fixes (27 fixes):
- tools/info.py: Simplified dispatch with lookup tables, defensive guards,
CPU fallback formatting, !s conversion flags, module-level sync assertion
- tools/docker.py: Case-insensitive container ID regex, keyword-only confirm,
module-level ALL_ACTIONS constant
- tools/virtualization.py: Normalized single-VM dict responses, unified
list/details queries
- core/client.py: Fixed HTTP client singleton race condition, compound key
substring matching for sensitive data redaction
- subscriptions/: Extracted SSL context creation to shared helper in utils.py,
replaced deprecated ssl._create_unverified_context API
- tools/array.py: Renamed parity_history to parity_status, hoisted ALL_ACTIONS
- tools/storage.py: Fixed dict(None) risks, temperature 0 falsiness bug
- tools/notifications.py, keys.py, rclone.py: Fixed dict(None) TypeError risks
- tests/: Fixed generator type annotations, added coverage for compound keys
Shell Script Fixes (13 fixes):
- dashboard.sh: Dynamic server discovery, conditional debug output, null-safe
jq, notification count guard order, removed unused variables
- unraid-query.sh: Proper JSON escaping via jq, --ignore-errors and --insecure
CLI flags, TLS verification now on by default
- validate-marketplace.sh: Removed unused YELLOW variable, defensive jq,
simplified repository URL output
Documentation Fixes (24+ fixes):
- Version consistency: Updated all references to v0.2.0 across pyproject.toml,
plugin.json, marketplace.json, MARKETPLACE.md, __init__.py, README files
- Tool count updates: Changed all "26 tools" references to "10 tools, 90 actions"
- Markdown lint: Fixed MD022, MD031, MD047 issues across multiple files
- Research docs: Fixed auth headers, removed web artifacts, corrected stale info
- Skills docs: Fixed query examples, endpoint counts, env var references
All 227 tests pass, ruff and ty checks clean.
2026-02-15 17:09:31 -05:00
Jacob Magar
2697c269a3
chore: enhance project metadata, tooling, and documentation
...
**Project Configuration:**
- Enhance pyproject.toml with comprehensive metadata, keywords, and classifiers
- Add LICENSE file (MIT) for proper open-source distribution
- Add PUBLISHING.md with comprehensive publishing guidelines
- Update .gitignore to exclude tool artifacts (.cache, .pytest_cache, .ruff_cache, .ty_cache)
- Ignore documentation working directories (.docs, .full-review, docs/plans, docs/sessions)
**Documentation:**
- Add extensive Unraid API research documentation
- API source code analysis and resolver mapping
- Competitive analysis and feature gap assessment
- Release notes analysis (7.0.0, 7.1.0, 7.2.0)
- Connect platform overview and remote access documentation
- Document known API patterns, limitations, and edge cases
**Testing & Code Quality:**
- Expand test coverage across all tool modules
- Add destructive action confirmation tests
- Improve test assertions and error case validation
- Refine type annotations for better static analysis
**Tool Improvements:**
- Enhance error handling consistency across all tools
- Improve type safety with explicit type annotations
- Refine GraphQL query construction patterns
- Better handling of optional parameters and edge cases
This commit prepares the project for v0.2.0 release with improved
metadata, comprehensive documentation, and enhanced code quality.
Co-authored-by: Claude <noreply@anthropic.com >
2026-02-15 15:32:09 -05:00
Jacob Magar
523b3edc76
feat: consolidate 26 tools into 10 tools with 90 actions
...
Refactor the entire tool layer to use the consolidated action pattern
(action: Literal[...] with QUERIES/MUTATIONS dicts). This reduces LLM
context from ~12k to ~5k tokens while adding ~60 new API capabilities.
New tools: unraid_info (19 actions), unraid_array (12), unraid_notifications (9),
unraid_users (8), unraid_keys (5). Rewritten: unraid_docker (15), unraid_vm (9),
unraid_storage (6), unraid_rclone (4), unraid_health (3).
Includes 129 tests across 10 test files, code review fixes for 16 issues
(severity ordering, PrefixedID regex, sensitive var redaction, etc.).
Removes tools/system.py (replaced by tools/info.py). Version bumped to 0.2.0.
2026-02-08 08:49:47 -05:00