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>
This commit is contained in:
Jacob Magar
2026-03-16 02:29:57 -04:00
parent faf9fb9ad7
commit dab1cd6995
48 changed files with 3591 additions and 4903 deletions

View File

@@ -35,116 +35,116 @@ class TestInfoQueries:
"""Validate all queries from unraid_mcp/tools/info.py."""
def test_overview_query(self, schema: GraphQLSchema) -> None:
from unraid_mcp.tools.info import QUERIES
from unraid_mcp.tools.unraid import _SYSTEM_QUERIES as QUERIES
errors = _validate_operation(schema, QUERIES["overview"])
assert not errors, f"overview query validation failed: {errors}"
def test_array_query(self, schema: GraphQLSchema) -> None:
from unraid_mcp.tools.info import QUERIES
from unraid_mcp.tools.unraid import _SYSTEM_QUERIES as QUERIES
errors = _validate_operation(schema, QUERIES["array"])
assert not errors, f"array query validation failed: {errors}"
def test_network_query(self, schema: GraphQLSchema) -> None:
from unraid_mcp.tools.info import QUERIES
from unraid_mcp.tools.unraid import _SYSTEM_QUERIES as QUERIES
errors = _validate_operation(schema, QUERIES["network"])
assert not errors, f"network query validation failed: {errors}"
def test_registration_query(self, schema: GraphQLSchema) -> None:
from unraid_mcp.tools.info import QUERIES
from unraid_mcp.tools.unraid import _SYSTEM_QUERIES as QUERIES
errors = _validate_operation(schema, QUERIES["registration"])
assert not errors, f"registration query validation failed: {errors}"
def test_variables_query(self, schema: GraphQLSchema) -> None:
from unraid_mcp.tools.info import QUERIES
from unraid_mcp.tools.unraid import _SYSTEM_QUERIES as QUERIES
errors = _validate_operation(schema, QUERIES["variables"])
assert not errors, f"variables query validation failed: {errors}"
def test_metrics_query(self, schema: GraphQLSchema) -> None:
from unraid_mcp.tools.info import QUERIES
from unraid_mcp.tools.unraid import _SYSTEM_QUERIES as QUERIES
errors = _validate_operation(schema, QUERIES["metrics"])
assert not errors, f"metrics query validation failed: {errors}"
def test_services_query(self, schema: GraphQLSchema) -> None:
from unraid_mcp.tools.info import QUERIES
from unraid_mcp.tools.unraid import _SYSTEM_QUERIES as QUERIES
errors = _validate_operation(schema, QUERIES["services"])
assert not errors, f"services query validation failed: {errors}"
def test_display_query(self, schema: GraphQLSchema) -> None:
from unraid_mcp.tools.info import QUERIES
from unraid_mcp.tools.unraid import _SYSTEM_QUERIES as QUERIES
errors = _validate_operation(schema, QUERIES["display"])
assert not errors, f"display query validation failed: {errors}"
def test_config_query(self, schema: GraphQLSchema) -> None:
from unraid_mcp.tools.info import QUERIES
from unraid_mcp.tools.unraid import _SYSTEM_QUERIES as QUERIES
errors = _validate_operation(schema, QUERIES["config"])
assert not errors, f"config query validation failed: {errors}"
def test_online_query(self, schema: GraphQLSchema) -> None:
from unraid_mcp.tools.info import QUERIES
from unraid_mcp.tools.unraid import _SYSTEM_QUERIES as QUERIES
errors = _validate_operation(schema, QUERIES["online"])
assert not errors, f"online query validation failed: {errors}"
def test_owner_query(self, schema: GraphQLSchema) -> None:
from unraid_mcp.tools.info import QUERIES
from unraid_mcp.tools.unraid import _SYSTEM_QUERIES as QUERIES
errors = _validate_operation(schema, QUERIES["owner"])
assert not errors, f"owner query validation failed: {errors}"
def test_settings_query(self, schema: GraphQLSchema) -> None:
from unraid_mcp.tools.info import QUERIES
from unraid_mcp.tools.unraid import _SYSTEM_QUERIES as QUERIES
errors = _validate_operation(schema, QUERIES["settings"])
assert not errors, f"settings query validation failed: {errors}"
def test_server_query(self, schema: GraphQLSchema) -> None:
from unraid_mcp.tools.info import QUERIES
from unraid_mcp.tools.unraid import _SYSTEM_QUERIES as QUERIES
errors = _validate_operation(schema, QUERIES["server"])
assert not errors, f"server query validation failed: {errors}"
def test_servers_query(self, schema: GraphQLSchema) -> None:
from unraid_mcp.tools.info import QUERIES
from unraid_mcp.tools.unraid import _SYSTEM_QUERIES as QUERIES
errors = _validate_operation(schema, QUERIES["servers"])
assert not errors, f"servers query validation failed: {errors}"
def test_flash_query(self, schema: GraphQLSchema) -> None:
from unraid_mcp.tools.info import QUERIES
from unraid_mcp.tools.unraid import _SYSTEM_QUERIES as QUERIES
errors = _validate_operation(schema, QUERIES["flash"])
assert not errors, f"flash query validation failed: {errors}"
def test_ups_devices_query(self, schema: GraphQLSchema) -> None:
from unraid_mcp.tools.info import QUERIES
from unraid_mcp.tools.unraid import _SYSTEM_QUERIES as QUERIES
errors = _validate_operation(schema, QUERIES["ups_devices"])
assert not errors, f"ups_devices query validation failed: {errors}"
def test_ups_device_query(self, schema: GraphQLSchema) -> None:
from unraid_mcp.tools.info import QUERIES
from unraid_mcp.tools.unraid import _SYSTEM_QUERIES as QUERIES
errors = _validate_operation(schema, QUERIES["ups_device"])
assert not errors, f"ups_device query validation failed: {errors}"
def test_ups_config_query(self, schema: GraphQLSchema) -> None:
from unraid_mcp.tools.info import QUERIES
from unraid_mcp.tools.unraid import _SYSTEM_QUERIES as QUERIES
errors = _validate_operation(schema, QUERIES["ups_config"])
assert not errors, f"ups_config query validation failed: {errors}"
def test_all_info_actions_covered(self, schema: GraphQLSchema) -> None:
"""Ensure every key in QUERIES has a corresponding test."""
from unraid_mcp.tools.info import QUERIES
from unraid_mcp.tools.unraid import _SYSTEM_QUERIES as QUERIES
expected_actions = {
"overview",
@@ -177,19 +177,19 @@ class TestArrayQueries:
"""Validate all queries from unraid_mcp/tools/array.py."""
def test_parity_status_query(self, schema: GraphQLSchema) -> None:
from unraid_mcp.tools.array import QUERIES
from unraid_mcp.tools.unraid import _ARRAY_QUERIES as QUERIES
errors = _validate_operation(schema, QUERIES["parity_status"])
assert not errors, f"parity_status query validation failed: {errors}"
def test_parity_history_query(self, schema: GraphQLSchema) -> None:
from unraid_mcp.tools.array import QUERIES
from unraid_mcp.tools.unraid import _ARRAY_QUERIES as QUERIES
errors = _validate_operation(schema, QUERIES["parity_history"])
assert not errors, f"parity_history query validation failed: {errors}"
def test_all_array_queries_covered(self, schema: GraphQLSchema) -> None:
from unraid_mcp.tools.array import QUERIES
from unraid_mcp.tools.unraid import _ARRAY_QUERIES as QUERIES
assert set(QUERIES.keys()) == {"parity_status", "parity_history"}
@@ -198,73 +198,73 @@ class TestArrayMutations:
"""Validate all mutations from unraid_mcp/tools/array.py."""
def test_parity_start_mutation(self, schema: GraphQLSchema) -> None:
from unraid_mcp.tools.array import MUTATIONS
from unraid_mcp.tools.unraid import _ARRAY_MUTATIONS as MUTATIONS
errors = _validate_operation(schema, MUTATIONS["parity_start"])
assert not errors, f"parity_start mutation validation failed: {errors}"
def test_parity_pause_mutation(self, schema: GraphQLSchema) -> None:
from unraid_mcp.tools.array import MUTATIONS
from unraid_mcp.tools.unraid import _ARRAY_MUTATIONS as MUTATIONS
errors = _validate_operation(schema, MUTATIONS["parity_pause"])
assert not errors, f"parity_pause mutation validation failed: {errors}"
def test_parity_resume_mutation(self, schema: GraphQLSchema) -> None:
from unraid_mcp.tools.array import MUTATIONS
from unraid_mcp.tools.unraid import _ARRAY_MUTATIONS as MUTATIONS
errors = _validate_operation(schema, MUTATIONS["parity_resume"])
assert not errors, f"parity_resume mutation validation failed: {errors}"
def test_parity_cancel_mutation(self, schema: GraphQLSchema) -> None:
from unraid_mcp.tools.array import MUTATIONS
from unraid_mcp.tools.unraid import _ARRAY_MUTATIONS as MUTATIONS
errors = _validate_operation(schema, MUTATIONS["parity_cancel"])
assert not errors, f"parity_cancel mutation validation failed: {errors}"
def test_start_array_mutation(self, schema: GraphQLSchema) -> None:
from unraid_mcp.tools.array import MUTATIONS
from unraid_mcp.tools.unraid import _ARRAY_MUTATIONS as MUTATIONS
errors = _validate_operation(schema, MUTATIONS["start_array"])
assert not errors, f"start_array mutation validation failed: {errors}"
def test_stop_array_mutation(self, schema: GraphQLSchema) -> None:
from unraid_mcp.tools.array import MUTATIONS
from unraid_mcp.tools.unraid import _ARRAY_MUTATIONS as MUTATIONS
errors = _validate_operation(schema, MUTATIONS["stop_array"])
assert not errors, f"stop_array mutation validation failed: {errors}"
def test_add_disk_mutation(self, schema: GraphQLSchema) -> None:
from unraid_mcp.tools.array import MUTATIONS
from unraid_mcp.tools.unraid import _ARRAY_MUTATIONS as MUTATIONS
errors = _validate_operation(schema, MUTATIONS["add_disk"])
assert not errors, f"add_disk mutation validation failed: {errors}"
def test_remove_disk_mutation(self, schema: GraphQLSchema) -> None:
from unraid_mcp.tools.array import MUTATIONS
from unraid_mcp.tools.unraid import _ARRAY_MUTATIONS as MUTATIONS
errors = _validate_operation(schema, MUTATIONS["remove_disk"])
assert not errors, f"remove_disk mutation validation failed: {errors}"
def test_mount_disk_mutation(self, schema: GraphQLSchema) -> None:
from unraid_mcp.tools.array import MUTATIONS
from unraid_mcp.tools.unraid import _ARRAY_MUTATIONS as MUTATIONS
errors = _validate_operation(schema, MUTATIONS["mount_disk"])
assert not errors, f"mount_disk mutation validation failed: {errors}"
def test_unmount_disk_mutation(self, schema: GraphQLSchema) -> None:
from unraid_mcp.tools.array import MUTATIONS
from unraid_mcp.tools.unraid import _ARRAY_MUTATIONS as MUTATIONS
errors = _validate_operation(schema, MUTATIONS["unmount_disk"])
assert not errors, f"unmount_disk mutation validation failed: {errors}"
def test_clear_disk_stats_mutation(self, schema: GraphQLSchema) -> None:
from unraid_mcp.tools.array import MUTATIONS
from unraid_mcp.tools.unraid import _ARRAY_MUTATIONS as MUTATIONS
errors = _validate_operation(schema, MUTATIONS["clear_disk_stats"])
assert not errors, f"clear_disk_stats mutation validation failed: {errors}"
def test_all_array_mutations_covered(self, schema: GraphQLSchema) -> None:
from unraid_mcp.tools.array import MUTATIONS
from unraid_mcp.tools.unraid import _ARRAY_MUTATIONS as MUTATIONS
expected = {
"parity_start",
@@ -289,37 +289,37 @@ class TestStorageQueries:
"""Validate all queries from unraid_mcp/tools/storage.py."""
def test_shares_query(self, schema: GraphQLSchema) -> None:
from unraid_mcp.tools.storage import QUERIES
from unraid_mcp.tools.unraid import _DISK_QUERIES as QUERIES
errors = _validate_operation(schema, QUERIES["shares"])
assert not errors, f"shares query validation failed: {errors}"
def test_disks_query(self, schema: GraphQLSchema) -> None:
from unraid_mcp.tools.storage import QUERIES
from unraid_mcp.tools.unraid import _DISK_QUERIES as QUERIES
errors = _validate_operation(schema, QUERIES["disks"])
assert not errors, f"disks query validation failed: {errors}"
def test_disk_details_query(self, schema: GraphQLSchema) -> None:
from unraid_mcp.tools.storage import QUERIES
from unraid_mcp.tools.unraid import _DISK_QUERIES as QUERIES
errors = _validate_operation(schema, QUERIES["disk_details"])
assert not errors, f"disk_details query validation failed: {errors}"
def test_log_files_query(self, schema: GraphQLSchema) -> None:
from unraid_mcp.tools.storage import QUERIES
from unraid_mcp.tools.unraid import _DISK_QUERIES as QUERIES
errors = _validate_operation(schema, QUERIES["log_files"])
assert not errors, f"log_files query validation failed: {errors}"
def test_logs_query(self, schema: GraphQLSchema) -> None:
from unraid_mcp.tools.storage import QUERIES
from unraid_mcp.tools.unraid import _DISK_QUERIES as QUERIES
errors = _validate_operation(schema, QUERIES["logs"])
assert not errors, f"logs query validation failed: {errors}"
def test_all_storage_queries_covered(self, schema: GraphQLSchema) -> None:
from unraid_mcp.tools.storage import QUERIES
from unraid_mcp.tools.unraid import _DISK_QUERIES as QUERIES
expected = {"shares", "disks", "disk_details", "log_files", "logs"}
assert set(QUERIES.keys()) == expected
@@ -329,13 +329,13 @@ class TestStorageMutations:
"""Validate all mutations from unraid_mcp/tools/storage.py."""
def test_flash_backup_mutation(self, schema: GraphQLSchema) -> None:
from unraid_mcp.tools.storage import MUTATIONS
from unraid_mcp.tools.unraid import _DISK_MUTATIONS as MUTATIONS
errors = _validate_operation(schema, MUTATIONS["flash_backup"])
assert not errors, f"flash_backup mutation validation failed: {errors}"
def test_all_storage_mutations_covered(self, schema: GraphQLSchema) -> None:
from unraid_mcp.tools.storage import MUTATIONS
from unraid_mcp.tools.unraid import _DISK_MUTATIONS as MUTATIONS
assert set(MUTATIONS.keys()) == {"flash_backup"}
@@ -347,31 +347,31 @@ class TestDockerQueries:
"""Validate all queries from unraid_mcp/tools/docker.py."""
def test_list_query(self, schema: GraphQLSchema) -> None:
from unraid_mcp.tools.docker import QUERIES
from unraid_mcp.tools.unraid import _DOCKER_QUERIES as QUERIES
errors = _validate_operation(schema, QUERIES["list"])
assert not errors, f"list query validation failed: {errors}"
def test_details_query(self, schema: GraphQLSchema) -> None:
from unraid_mcp.tools.docker import QUERIES
from unraid_mcp.tools.unraid import _DOCKER_QUERIES as QUERIES
errors = _validate_operation(schema, QUERIES["details"])
assert not errors, f"details query validation failed: {errors}"
def test_networks_query(self, schema: GraphQLSchema) -> None:
from unraid_mcp.tools.docker import QUERIES
from unraid_mcp.tools.unraid import _DOCKER_QUERIES as QUERIES
errors = _validate_operation(schema, QUERIES["networks"])
assert not errors, f"networks query validation failed: {errors}"
def test_network_details_query(self, schema: GraphQLSchema) -> None:
from unraid_mcp.tools.docker import QUERIES
from unraid_mcp.tools.unraid import _DOCKER_QUERIES as QUERIES
errors = _validate_operation(schema, QUERIES["network_details"])
assert not errors, f"network_details query validation failed: {errors}"
def test_all_docker_queries_covered(self, schema: GraphQLSchema) -> None:
from unraid_mcp.tools.docker import QUERIES
from unraid_mcp.tools.unraid import _DOCKER_QUERIES as QUERIES
expected = {
"list",
@@ -386,19 +386,19 @@ class TestDockerMutations:
"""Validate all mutations from unraid_mcp/tools/docker.py."""
def test_start_mutation(self, schema: GraphQLSchema) -> None:
from unraid_mcp.tools.docker import MUTATIONS
from unraid_mcp.tools.unraid import _DOCKER_MUTATIONS as MUTATIONS
errors = _validate_operation(schema, MUTATIONS["start"])
assert not errors, f"start mutation validation failed: {errors}"
def test_stop_mutation(self, schema: GraphQLSchema) -> None:
from unraid_mcp.tools.docker import MUTATIONS
from unraid_mcp.tools.unraid import _DOCKER_MUTATIONS as MUTATIONS
errors = _validate_operation(schema, MUTATIONS["stop"])
assert not errors, f"stop mutation validation failed: {errors}"
def test_all_docker_mutations_covered(self, schema: GraphQLSchema) -> None:
from unraid_mcp.tools.docker import MUTATIONS
from unraid_mcp.tools.unraid import _DOCKER_MUTATIONS as MUTATIONS
expected = {
"start",
@@ -414,19 +414,19 @@ class TestVmQueries:
"""Validate all queries from unraid_mcp/tools/virtualization.py."""
def test_list_query(self, schema: GraphQLSchema) -> None:
from unraid_mcp.tools.virtualization import QUERIES
from unraid_mcp.tools.unraid import _VM_QUERIES as QUERIES
errors = _validate_operation(schema, QUERIES["list"])
assert not errors, f"list query validation failed: {errors}"
def test_details_query(self, schema: GraphQLSchema) -> None:
from unraid_mcp.tools.virtualization import QUERIES
from unraid_mcp.tools.unraid import _VM_QUERIES as QUERIES
errors = _validate_operation(schema, QUERIES["details"])
assert not errors, f"details query validation failed: {errors}"
def test_all_vm_queries_covered(self, schema: GraphQLSchema) -> None:
from unraid_mcp.tools.virtualization import QUERIES
from unraid_mcp.tools.unraid import _VM_QUERIES as QUERIES
assert set(QUERIES.keys()) == {"list", "details"}
@@ -435,49 +435,49 @@ class TestVmMutations:
"""Validate all mutations from unraid_mcp/tools/virtualization.py."""
def test_start_mutation(self, schema: GraphQLSchema) -> None:
from unraid_mcp.tools.virtualization import MUTATIONS
from unraid_mcp.tools.unraid import _VM_MUTATIONS as MUTATIONS
errors = _validate_operation(schema, MUTATIONS["start"])
assert not errors, f"start mutation validation failed: {errors}"
def test_stop_mutation(self, schema: GraphQLSchema) -> None:
from unraid_mcp.tools.virtualization import MUTATIONS
from unraid_mcp.tools.unraid import _VM_MUTATIONS as MUTATIONS
errors = _validate_operation(schema, MUTATIONS["stop"])
assert not errors, f"stop mutation validation failed: {errors}"
def test_pause_mutation(self, schema: GraphQLSchema) -> None:
from unraid_mcp.tools.virtualization import MUTATIONS
from unraid_mcp.tools.unraid import _VM_MUTATIONS as MUTATIONS
errors = _validate_operation(schema, MUTATIONS["pause"])
assert not errors, f"pause mutation validation failed: {errors}"
def test_resume_mutation(self, schema: GraphQLSchema) -> None:
from unraid_mcp.tools.virtualization import MUTATIONS
from unraid_mcp.tools.unraid import _VM_MUTATIONS as MUTATIONS
errors = _validate_operation(schema, MUTATIONS["resume"])
assert not errors, f"resume mutation validation failed: {errors}"
def test_force_stop_mutation(self, schema: GraphQLSchema) -> None:
from unraid_mcp.tools.virtualization import MUTATIONS
from unraid_mcp.tools.unraid import _VM_MUTATIONS as MUTATIONS
errors = _validate_operation(schema, MUTATIONS["force_stop"])
assert not errors, f"force_stop mutation validation failed: {errors}"
def test_reboot_mutation(self, schema: GraphQLSchema) -> None:
from unraid_mcp.tools.virtualization import MUTATIONS
from unraid_mcp.tools.unraid import _VM_MUTATIONS as MUTATIONS
errors = _validate_operation(schema, MUTATIONS["reboot"])
assert not errors, f"reboot mutation validation failed: {errors}"
def test_reset_mutation(self, schema: GraphQLSchema) -> None:
from unraid_mcp.tools.virtualization import MUTATIONS
from unraid_mcp.tools.unraid import _VM_MUTATIONS as MUTATIONS
errors = _validate_operation(schema, MUTATIONS["reset"])
assert not errors, f"reset mutation validation failed: {errors}"
def test_all_vm_mutations_covered(self, schema: GraphQLSchema) -> None:
from unraid_mcp.tools.virtualization import MUTATIONS
from unraid_mcp.tools.unraid import _VM_MUTATIONS as MUTATIONS
expected = {"start", "stop", "pause", "resume", "force_stop", "reboot", "reset"}
assert set(MUTATIONS.keys()) == expected
@@ -490,19 +490,19 @@ class TestNotificationQueries:
"""Validate all queries from unraid_mcp/tools/notifications.py."""
def test_overview_query(self, schema: GraphQLSchema) -> None:
from unraid_mcp.tools.notifications import QUERIES
from unraid_mcp.tools.unraid import _NOTIFICATION_QUERIES as QUERIES
errors = _validate_operation(schema, QUERIES["overview"])
assert not errors, f"overview query validation failed: {errors}"
def test_list_query(self, schema: GraphQLSchema) -> None:
from unraid_mcp.tools.notifications import QUERIES
from unraid_mcp.tools.unraid import _NOTIFICATION_QUERIES as QUERIES
errors = _validate_operation(schema, QUERIES["list"])
assert not errors, f"list query validation failed: {errors}"
def test_all_notification_queries_covered(self, schema: GraphQLSchema) -> None:
from unraid_mcp.tools.notifications import QUERIES
from unraid_mcp.tools.unraid import _NOTIFICATION_QUERIES as QUERIES
assert set(QUERIES.keys()) == {"overview", "list"}
@@ -511,67 +511,67 @@ class TestNotificationMutations:
"""Validate all mutations from unraid_mcp/tools/notifications.py."""
def test_create_mutation(self, schema: GraphQLSchema) -> None:
from unraid_mcp.tools.notifications import MUTATIONS
from unraid_mcp.tools.unraid import _NOTIFICATION_MUTATIONS as MUTATIONS
errors = _validate_operation(schema, MUTATIONS["create"])
assert not errors, f"create mutation validation failed: {errors}"
def test_archive_mutation(self, schema: GraphQLSchema) -> None:
from unraid_mcp.tools.notifications import MUTATIONS
from unraid_mcp.tools.unraid import _NOTIFICATION_MUTATIONS as MUTATIONS
errors = _validate_operation(schema, MUTATIONS["archive"])
assert not errors, f"archive mutation validation failed: {errors}"
def test_unread_mutation(self, schema: GraphQLSchema) -> None:
from unraid_mcp.tools.notifications import MUTATIONS
from unraid_mcp.tools.unraid import _NOTIFICATION_MUTATIONS as MUTATIONS
errors = _validate_operation(schema, MUTATIONS["unread"])
assert not errors, f"unread mutation validation failed: {errors}"
def test_delete_mutation(self, schema: GraphQLSchema) -> None:
from unraid_mcp.tools.notifications import MUTATIONS
from unraid_mcp.tools.unraid import _NOTIFICATION_MUTATIONS as MUTATIONS
errors = _validate_operation(schema, MUTATIONS["delete"])
assert not errors, f"delete mutation validation failed: {errors}"
def test_delete_archived_mutation(self, schema: GraphQLSchema) -> None:
from unraid_mcp.tools.notifications import MUTATIONS
from unraid_mcp.tools.unraid import _NOTIFICATION_MUTATIONS as MUTATIONS
errors = _validate_operation(schema, MUTATIONS["delete_archived"])
assert not errors, f"delete_archived mutation validation failed: {errors}"
def test_archive_all_mutation(self, schema: GraphQLSchema) -> None:
from unraid_mcp.tools.notifications import MUTATIONS
from unraid_mcp.tools.unraid import _NOTIFICATION_MUTATIONS as MUTATIONS
errors = _validate_operation(schema, MUTATIONS["archive_all"])
assert not errors, f"archive_all mutation validation failed: {errors}"
def test_archive_many_mutation(self, schema: GraphQLSchema) -> None:
from unraid_mcp.tools.notifications import MUTATIONS
from unraid_mcp.tools.unraid import _NOTIFICATION_MUTATIONS as MUTATIONS
errors = _validate_operation(schema, MUTATIONS["archive_many"])
assert not errors, f"archive_many mutation validation failed: {errors}"
def test_unarchive_many_mutation(self, schema: GraphQLSchema) -> None:
from unraid_mcp.tools.notifications import MUTATIONS
from unraid_mcp.tools.unraid import _NOTIFICATION_MUTATIONS as MUTATIONS
errors = _validate_operation(schema, MUTATIONS["unarchive_many"])
assert not errors, f"unarchive_many mutation validation failed: {errors}"
def test_unarchive_all_mutation(self, schema: GraphQLSchema) -> None:
from unraid_mcp.tools.notifications import MUTATIONS
from unraid_mcp.tools.unraid import _NOTIFICATION_MUTATIONS as MUTATIONS
errors = _validate_operation(schema, MUTATIONS["unarchive_all"])
assert not errors, f"unarchive_all mutation validation failed: {errors}"
def test_recalculate_mutation(self, schema: GraphQLSchema) -> None:
from unraid_mcp.tools.notifications import MUTATIONS
from unraid_mcp.tools.unraid import _NOTIFICATION_MUTATIONS as MUTATIONS
errors = _validate_operation(schema, MUTATIONS["recalculate"])
assert not errors, f"recalculate mutation validation failed: {errors}"
def test_all_notification_mutations_covered(self, schema: GraphQLSchema) -> None:
from unraid_mcp.tools.notifications import MUTATIONS
from unraid_mcp.tools.unraid import _NOTIFICATION_MUTATIONS as MUTATIONS
expected = {
"create",
@@ -595,19 +595,19 @@ class TestRcloneQueries:
"""Validate all queries from unraid_mcp/tools/rclone.py."""
def test_list_remotes_query(self, schema: GraphQLSchema) -> None:
from unraid_mcp.tools.rclone import QUERIES
from unraid_mcp.tools.unraid import _RCLONE_QUERIES as QUERIES
errors = _validate_operation(schema, QUERIES["list_remotes"])
assert not errors, f"list_remotes query validation failed: {errors}"
def test_config_form_query(self, schema: GraphQLSchema) -> None:
from unraid_mcp.tools.rclone import QUERIES
from unraid_mcp.tools.unraid import _RCLONE_QUERIES as QUERIES
errors = _validate_operation(schema, QUERIES["config_form"])
assert not errors, f"config_form query validation failed: {errors}"
def test_all_rclone_queries_covered(self, schema: GraphQLSchema) -> None:
from unraid_mcp.tools.rclone import QUERIES
from unraid_mcp.tools.unraid import _RCLONE_QUERIES as QUERIES
assert set(QUERIES.keys()) == {"list_remotes", "config_form"}
@@ -616,19 +616,19 @@ class TestRcloneMutations:
"""Validate all mutations from unraid_mcp/tools/rclone.py."""
def test_create_remote_mutation(self, schema: GraphQLSchema) -> None:
from unraid_mcp.tools.rclone import MUTATIONS
from unraid_mcp.tools.unraid import _RCLONE_MUTATIONS as MUTATIONS
errors = _validate_operation(schema, MUTATIONS["create_remote"])
assert not errors, f"create_remote mutation validation failed: {errors}"
def test_delete_remote_mutation(self, schema: GraphQLSchema) -> None:
from unraid_mcp.tools.rclone import MUTATIONS
from unraid_mcp.tools.unraid import _RCLONE_MUTATIONS as MUTATIONS
errors = _validate_operation(schema, MUTATIONS["delete_remote"])
assert not errors, f"delete_remote mutation validation failed: {errors}"
def test_all_rclone_mutations_covered(self, schema: GraphQLSchema) -> None:
from unraid_mcp.tools.rclone import MUTATIONS
from unraid_mcp.tools.unraid import _RCLONE_MUTATIONS as MUTATIONS
assert set(MUTATIONS.keys()) == {"create_remote", "delete_remote"}
@@ -640,13 +640,13 @@ class TestUsersQueries:
"""Validate all queries from unraid_mcp/tools/users.py."""
def test_me_query(self, schema: GraphQLSchema) -> None:
from unraid_mcp.tools.users import QUERIES
from unraid_mcp.tools.unraid import _USER_QUERIES as QUERIES
errors = _validate_operation(schema, QUERIES["me"])
assert not errors, f"me query validation failed: {errors}"
def test_all_users_queries_covered(self, schema: GraphQLSchema) -> None:
from unraid_mcp.tools.users import QUERIES
from unraid_mcp.tools.unraid import _USER_QUERIES as QUERIES
assert set(QUERIES.keys()) == {"me"}
@@ -658,19 +658,19 @@ class TestKeysQueries:
"""Validate all queries from unraid_mcp/tools/keys.py."""
def test_list_query(self, schema: GraphQLSchema) -> None:
from unraid_mcp.tools.keys import QUERIES
from unraid_mcp.tools.unraid import _KEY_QUERIES as QUERIES
errors = _validate_operation(schema, QUERIES["list"])
assert not errors, f"list query validation failed: {errors}"
def test_get_query(self, schema: GraphQLSchema) -> None:
from unraid_mcp.tools.keys import QUERIES
from unraid_mcp.tools.unraid import _KEY_QUERIES as QUERIES
errors = _validate_operation(schema, QUERIES["get"])
assert not errors, f"get query validation failed: {errors}"
def test_all_keys_queries_covered(self, schema: GraphQLSchema) -> None:
from unraid_mcp.tools.keys import QUERIES
from unraid_mcp.tools.unraid import _KEY_QUERIES as QUERIES
assert set(QUERIES.keys()) == {"list", "get"}
@@ -679,37 +679,37 @@ class TestKeysMutations:
"""Validate all mutations from unraid_mcp/tools/keys.py."""
def test_create_mutation(self, schema: GraphQLSchema) -> None:
from unraid_mcp.tools.keys import MUTATIONS
from unraid_mcp.tools.unraid import _KEY_MUTATIONS as MUTATIONS
errors = _validate_operation(schema, MUTATIONS["create"])
assert not errors, f"create mutation validation failed: {errors}"
def test_update_mutation(self, schema: GraphQLSchema) -> None:
from unraid_mcp.tools.keys import MUTATIONS
from unraid_mcp.tools.unraid import _KEY_MUTATIONS as MUTATIONS
errors = _validate_operation(schema, MUTATIONS["update"])
assert not errors, f"update mutation validation failed: {errors}"
def test_delete_mutation(self, schema: GraphQLSchema) -> None:
from unraid_mcp.tools.keys import MUTATIONS
from unraid_mcp.tools.unraid import _KEY_MUTATIONS as MUTATIONS
errors = _validate_operation(schema, MUTATIONS["delete"])
assert not errors, f"delete mutation validation failed: {errors}"
def test_add_role_mutation(self, schema: GraphQLSchema) -> None:
from unraid_mcp.tools.keys import MUTATIONS
from unraid_mcp.tools.unraid import _KEY_MUTATIONS as MUTATIONS
errors = _validate_operation(schema, MUTATIONS["add_role"])
assert not errors, f"add_role mutation validation failed: {errors}"
def test_remove_role_mutation(self, schema: GraphQLSchema) -> None:
from unraid_mcp.tools.keys import MUTATIONS
from unraid_mcp.tools.unraid import _KEY_MUTATIONS as MUTATIONS
errors = _validate_operation(schema, MUTATIONS["remove_role"])
assert not errors, f"remove_role mutation validation failed: {errors}"
def test_all_keys_mutations_covered(self, schema: GraphQLSchema) -> None:
from unraid_mcp.tools.keys import MUTATIONS
from unraid_mcp.tools.unraid import _KEY_MUTATIONS as MUTATIONS
assert set(MUTATIONS.keys()) == {"create", "update", "delete", "add_role", "remove_role"}
@@ -721,19 +721,19 @@ class TestSettingsMutations:
"""Validate all mutations from unraid_mcp/tools/settings.py."""
def test_update_mutation(self, schema: GraphQLSchema) -> None:
from unraid_mcp.tools.settings import MUTATIONS
from unraid_mcp.tools.unraid import _SETTING_MUTATIONS as MUTATIONS
errors = _validate_operation(schema, MUTATIONS["update"])
assert not errors, f"update mutation validation failed: {errors}"
def test_configure_ups_mutation(self, schema: GraphQLSchema) -> None:
from unraid_mcp.tools.settings import MUTATIONS
from unraid_mcp.tools.unraid import _SETTING_MUTATIONS as MUTATIONS
errors = _validate_operation(schema, MUTATIONS["configure_ups"])
assert not errors, f"configure_ups mutation validation failed: {errors}"
def test_all_settings_mutations_covered(self, schema: GraphQLSchema) -> None:
from unraid_mcp.tools.settings import MUTATIONS
from unraid_mcp.tools.unraid import _SETTING_MUTATIONS as MUTATIONS
expected = {
"update",
@@ -790,7 +790,7 @@ class TestCustomizationQueries:
assert not errors, f"is_initial_setup (isFreshInstall) query validation failed: {errors}"
def test_sso_enabled_query(self, schema: GraphQLSchema) -> None:
from unraid_mcp.tools.customization import QUERIES
from unraid_mcp.tools.unraid import _CUSTOMIZATION_QUERIES as QUERIES
errors = _validate_operation(schema, QUERIES["sso_enabled"])
assert not errors, f"sso_enabled query validation failed: {errors}"
@@ -805,13 +805,13 @@ class TestCustomizationMutations:
"""Validate mutations from unraid_mcp/tools/customization.py."""
def test_set_theme_mutation(self, schema: GraphQLSchema) -> None:
from unraid_mcp.tools.customization import MUTATIONS
from unraid_mcp.tools.unraid import _CUSTOMIZATION_MUTATIONS as MUTATIONS
errors = _validate_operation(schema, MUTATIONS["set_theme"])
assert not errors, f"set_theme mutation validation failed: {errors}"
def test_all_customization_mutations_covered(self, schema: GraphQLSchema) -> None:
from unraid_mcp.tools.customization import MUTATIONS
from unraid_mcp.tools.unraid import _CUSTOMIZATION_MUTATIONS as MUTATIONS
assert set(MUTATIONS.keys()) == {"set_theme"}
@@ -823,13 +823,13 @@ class TestPluginsQueries:
"""Validate all queries from unraid_mcp/tools/plugins.py."""
def test_list_query(self, schema: GraphQLSchema) -> None:
from unraid_mcp.tools.plugins import QUERIES
from unraid_mcp.tools.unraid import _PLUGIN_QUERIES as QUERIES
errors = _validate_operation(schema, QUERIES["list"])
assert not errors, f"plugins list query validation failed: {errors}"
def test_all_plugins_queries_covered(self, schema: GraphQLSchema) -> None:
from unraid_mcp.tools.plugins import QUERIES
from unraid_mcp.tools.unraid import _PLUGIN_QUERIES as QUERIES
assert set(QUERIES.keys()) == {"list"}
@@ -838,19 +838,19 @@ class TestPluginsMutations:
"""Validate all mutations from unraid_mcp/tools/plugins.py."""
def test_add_mutation(self, schema: GraphQLSchema) -> None:
from unraid_mcp.tools.plugins import MUTATIONS
from unraid_mcp.tools.unraid import _PLUGIN_MUTATIONS as MUTATIONS
errors = _validate_operation(schema, MUTATIONS["add"])
assert not errors, f"plugins add mutation validation failed: {errors}"
def test_remove_mutation(self, schema: GraphQLSchema) -> None:
from unraid_mcp.tools.plugins import MUTATIONS
from unraid_mcp.tools.unraid import _PLUGIN_MUTATIONS as MUTATIONS
errors = _validate_operation(schema, MUTATIONS["remove"])
assert not errors, f"plugins remove mutation validation failed: {errors}"
def test_all_plugins_mutations_covered(self, schema: GraphQLSchema) -> None:
from unraid_mcp.tools.plugins import MUTATIONS
from unraid_mcp.tools.unraid import _PLUGIN_MUTATIONS as MUTATIONS
assert set(MUTATIONS.keys()) == {"add", "remove"}
@@ -862,37 +862,37 @@ class TestOidcQueries:
"""Validate all queries from unraid_mcp/tools/oidc.py."""
def test_providers_query(self, schema: GraphQLSchema) -> None:
from unraid_mcp.tools.oidc import QUERIES
from unraid_mcp.tools.unraid import _OIDC_QUERIES as QUERIES
errors = _validate_operation(schema, QUERIES["providers"])
assert not errors, f"oidc providers query validation failed: {errors}"
def test_provider_query(self, schema: GraphQLSchema) -> None:
from unraid_mcp.tools.oidc import QUERIES
from unraid_mcp.tools.unraid import _OIDC_QUERIES as QUERIES
errors = _validate_operation(schema, QUERIES["provider"])
assert not errors, f"oidc provider query validation failed: {errors}"
def test_configuration_query(self, schema: GraphQLSchema) -> None:
from unraid_mcp.tools.oidc import QUERIES
from unraid_mcp.tools.unraid import _OIDC_QUERIES as QUERIES
errors = _validate_operation(schema, QUERIES["configuration"])
assert not errors, f"oidc configuration query validation failed: {errors}"
def test_public_providers_query(self, schema: GraphQLSchema) -> None:
from unraid_mcp.tools.oidc import QUERIES
from unraid_mcp.tools.unraid import _OIDC_QUERIES as QUERIES
errors = _validate_operation(schema, QUERIES["public_providers"])
assert not errors, f"oidc public_providers query validation failed: {errors}"
def test_validate_session_query(self, schema: GraphQLSchema) -> None:
from unraid_mcp.tools.oidc import QUERIES
from unraid_mcp.tools.unraid import _OIDC_QUERIES as QUERIES
errors = _validate_operation(schema, QUERIES["validate_session"])
assert not errors, f"oidc validate_session query validation failed: {errors}"
def test_all_oidc_queries_covered(self, schema: GraphQLSchema) -> None:
from unraid_mcp.tools.oidc import QUERIES
from unraid_mcp.tools.unraid import _OIDC_QUERIES as QUERIES
expected = {
"providers",
@@ -911,36 +911,43 @@ class TestSchemaCompleteness:
"""Validate that all tool operations are covered by the schema."""
def test_all_tool_queries_validate(self, schema: GraphQLSchema) -> None:
"""Bulk-validate every query across all tools.
"""Bulk-validate every query/mutation across all domains in the consolidated unraid module.
Known schema mismatches are tracked in KNOWN_SCHEMA_ISSUES and excluded
from the assertion so the test suite stays green while the underlying
tool queries are fixed incrementally.
"""
import importlib
import unraid_mcp.tools.unraid as unraid_mod
tool_modules = [
"unraid_mcp.tools.info",
"unraid_mcp.tools.array",
"unraid_mcp.tools.storage",
"unraid_mcp.tools.docker",
"unraid_mcp.tools.virtualization",
"unraid_mcp.tools.notifications",
"unraid_mcp.tools.rclone",
"unraid_mcp.tools.users",
"unraid_mcp.tools.keys",
"unraid_mcp.tools.settings",
"unraid_mcp.tools.customization",
"unraid_mcp.tools.plugins",
"unraid_mcp.tools.oidc",
# All query/mutation dicts in the consolidated module, keyed by domain/type label
all_operation_dicts: list[tuple[str, dict[str, str]]] = [
("system/QUERIES", unraid_mod._SYSTEM_QUERIES),
("array/QUERIES", unraid_mod._ARRAY_QUERIES),
("array/MUTATIONS", unraid_mod._ARRAY_MUTATIONS),
("disk/QUERIES", unraid_mod._DISK_QUERIES),
("disk/MUTATIONS", unraid_mod._DISK_MUTATIONS),
("docker/QUERIES", unraid_mod._DOCKER_QUERIES),
("docker/MUTATIONS", unraid_mod._DOCKER_MUTATIONS),
("vm/QUERIES", unraid_mod._VM_QUERIES),
("vm/MUTATIONS", unraid_mod._VM_MUTATIONS),
("notification/QUERIES", unraid_mod._NOTIFICATION_QUERIES),
("notification/MUTATIONS", unraid_mod._NOTIFICATION_MUTATIONS),
("rclone/QUERIES", unraid_mod._RCLONE_QUERIES),
("rclone/MUTATIONS", unraid_mod._RCLONE_MUTATIONS),
("user/QUERIES", unraid_mod._USER_QUERIES),
("key/QUERIES", unraid_mod._KEY_QUERIES),
("key/MUTATIONS", unraid_mod._KEY_MUTATIONS),
("setting/MUTATIONS", unraid_mod._SETTING_MUTATIONS),
("customization/QUERIES", unraid_mod._CUSTOMIZATION_QUERIES),
("customization/MUTATIONS", unraid_mod._CUSTOMIZATION_MUTATIONS),
("plugin/QUERIES", unraid_mod._PLUGIN_QUERIES),
("plugin/MUTATIONS", unraid_mod._PLUGIN_MUTATIONS),
("oidc/QUERIES", unraid_mod._OIDC_QUERIES),
]
# Known schema mismatches in tool QUERIES/MUTATIONS dicts.
# These represent bugs in the tool implementation, not in the tests.
# Remove entries from this set as they are fixed.
# Known schema mismatches — bugs in tool implementation, not in tests.
# Remove entries as they are fixed.
KNOWN_SCHEMA_ISSUES: set[str] = {
# storage: unassignedDevices not in Query type
"storage/QUERIES/unassigned",
# customization: Customization.theme field does not exist
"customization/QUERIES/theme",
# customization: publicPartnerInfo not in Query type
@@ -953,26 +960,10 @@ class TestSchemaCompleteness:
unexpected_passes: list[str] = []
total = 0
for module_path in tool_modules:
mod = importlib.import_module(module_path)
tool_name = module_path.split(".")[-1]
queries = getattr(mod, "QUERIES", {})
for action, query_str in queries.items():
for label, ops_dict in all_operation_dicts:
for action, query_str in ops_dict.items():
total += 1
key = f"{tool_name}/QUERIES/{action}"
errors = _validate_operation(schema, query_str)
if errors:
if key not in KNOWN_SCHEMA_ISSUES:
failures.append(f"{key}: {errors[0]}")
else:
if key in KNOWN_SCHEMA_ISSUES:
unexpected_passes.append(key)
mutations = getattr(mod, "MUTATIONS", {})
for action, query_str in mutations.items():
total += 1
key = f"{tool_name}/MUTATIONS/{action}"
key = f"{label}/{action}"
errors = _validate_operation(schema, query_str)
if errors:
if key not in KNOWN_SCHEMA_ISSUES:
@@ -982,7 +973,6 @@ class TestSchemaCompleteness:
unexpected_passes.append(key)
if unexpected_passes:
# A known issue was fixed — remove it from KNOWN_SCHEMA_ISSUES
raise AssertionError(
"The following operations are listed in KNOWN_SCHEMA_ISSUES but now pass — "
"remove them from the set:\n" + "\n".join(unexpected_passes)
@@ -1003,29 +993,32 @@ class TestSchemaCompleteness:
def test_total_operations_count(self, schema: GraphQLSchema) -> None:
"""Verify the expected number of tool operations exist."""
import importlib
import unraid_mcp.tools.unraid as unraid_mod
tool_modules = [
"unraid_mcp.tools.info",
"unraid_mcp.tools.array",
"unraid_mcp.tools.storage",
"unraid_mcp.tools.docker",
"unraid_mcp.tools.virtualization",
"unraid_mcp.tools.notifications",
"unraid_mcp.tools.rclone",
"unraid_mcp.tools.users",
"unraid_mcp.tools.keys",
"unraid_mcp.tools.settings",
"unraid_mcp.tools.customization",
"unraid_mcp.tools.plugins",
"unraid_mcp.tools.oidc",
all_dicts = [
unraid_mod._SYSTEM_QUERIES,
unraid_mod._ARRAY_QUERIES,
unraid_mod._ARRAY_MUTATIONS,
unraid_mod._DISK_QUERIES,
unraid_mod._DISK_MUTATIONS,
unraid_mod._DOCKER_QUERIES,
unraid_mod._DOCKER_MUTATIONS,
unraid_mod._VM_QUERIES,
unraid_mod._VM_MUTATIONS,
unraid_mod._NOTIFICATION_QUERIES,
unraid_mod._NOTIFICATION_MUTATIONS,
unraid_mod._RCLONE_QUERIES,
unraid_mod._RCLONE_MUTATIONS,
unraid_mod._USER_QUERIES,
unraid_mod._KEY_QUERIES,
unraid_mod._KEY_MUTATIONS,
unraid_mod._SETTING_MUTATIONS,
unraid_mod._CUSTOMIZATION_QUERIES,
unraid_mod._CUSTOMIZATION_MUTATIONS,
unraid_mod._PLUGIN_QUERIES,
unraid_mod._PLUGIN_MUTATIONS,
unraid_mod._OIDC_QUERIES,
]
total = 0
for module_path in tool_modules:
mod = importlib.import_module(module_path)
total += len(getattr(mod, "QUERIES", {}))
total += len(getattr(mod, "MUTATIONS", {}))
# Operations across all tools (queries + mutations in dicts)
total = sum(len(d) for d in all_dicts)
assert total >= 50, f"Expected at least 50 operations, found {total}"