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.
This commit is contained in:
Jacob Magar
2026-02-15 17:09:31 -05:00
parent 6bbe46879e
commit 37e9424a5c
58 changed files with 1333 additions and 1175 deletions

View File

@@ -12,17 +12,13 @@ from ..config.logging import logger
from ..core.client import make_graphql_request
from ..core.exceptions import ToolError
QUERIES: dict[str, str] = {
"list": """
query ListVMs {
vms { id domains { id name state uuid } }
}
""",
"details": """
query GetVmDetails {
vms { domains { id name state uuid } }
}
""",
}
MUTATIONS: dict[str, str] = {
@@ -49,15 +45,9 @@ MUTATIONS: dict[str, str] = {
""",
}
# Map action names to their GraphQL field names
# Map action names to GraphQL field names (only where they differ)
_MUTATION_FIELDS: dict[str, str] = {
"start": "start",
"stop": "stop",
"pause": "pause",
"resume": "resume",
"force_stop": "forceStop",
"reboot": "reboot",
"reset": "reset",
}
DESTRUCTIVE_ACTIONS = {"force_stop", "reset"}
@@ -90,7 +80,7 @@ def register_vm_tool(mcp: FastMCP) -> None:
reboot - Reboot a VM (requires vm_id)
reset - Reset a VM (requires vm_id, confirm=True)
"""
all_actions = set(QUERIES) | set(MUTATIONS)
all_actions = set(QUERIES) | set(MUTATIONS) | {"details"}
if action not in all_actions:
raise ToolError(f"Invalid action '{action}'. Must be one of: {sorted(all_actions)}")
@@ -103,39 +93,40 @@ def register_vm_tool(mcp: FastMCP) -> None:
try:
logger.info(f"Executing unraid_vm action={action}")
if action == "list":
if action in ("list", "details"):
data = await make_graphql_request(QUERIES["list"])
if data.get("vms"):
vms = data["vms"].get("domains") or data["vms"].get("domain")
if vms:
return {"vms": list(vms) if isinstance(vms, list) else []}
return {"vms": []}
if action == "details":
data = await make_graphql_request(QUERIES["details"])
if data.get("vms"):
vms = data["vms"].get("domains") or data["vms"].get("domain") or []
if isinstance(vms, dict):
vms = [vms]
if action == "list":
return {"vms": vms}
# details: find specific VM
for vm in vms:
if (
vm.get("uuid") == vm_id
or vm.get("id") == vm_id
or vm.get("name") == vm_id
):
return dict(vm) if isinstance(vm, dict) else {}
return dict(vm)
available = [
f"{v.get('name')} (UUID: {v.get('uuid')})" for v in vms
]
raise ToolError(
f"VM '{vm_id}' not found. Available: {', '.join(available)}"
)
raise ToolError("No VM data returned from server")
if action == "details":
raise ToolError("No VM data returned from server")
return {"vms": []}
# Mutations
if action in MUTATIONS:
data = await make_graphql_request(
MUTATIONS[action], {"id": vm_id}
)
field = _MUTATION_FIELDS[action]
field = _MUTATION_FIELDS.get(action, action)
if data.get("vm") and field in data["vm"]:
return {
"success": data["vm"][field],