feat(elicitation): add setup action to unraid_health

This commit is contained in:
Jacob Magar
2026-03-14 04:02:15 -04:00
parent 61604b313f
commit 9be46750b8
2 changed files with 53 additions and 6 deletions

View File

@@ -194,3 +194,36 @@ class TestSafeDisplayUrl:
# raises ValueError: Invalid IPv6 URL — this triggers the except branch. # raises ValueError: Invalid IPv6 URL — this triggers the except branch.
result = safe_display_url("https://[invalid") result = safe_display_url("https://[invalid")
assert result == "<unparseable>" assert result == "<unparseable>"
@pytest.mark.asyncio
async def test_health_setup_action_calls_elicitation() -> None:
"""setup action triggers elicit_and_configure and returns success message."""
from unittest.mock import AsyncMock, MagicMock
tool_fn = _make_tool()
with patch(
"unraid_mcp.tools.health.elicit_and_configure", new=AsyncMock(return_value=True)
) as mock_elicit:
result = await tool_fn(action="setup", ctx=MagicMock())
assert mock_elicit.called
assert "configured" in result.lower() or "success" in result.lower()
@pytest.mark.asyncio
async def test_health_setup_action_returns_declined_message() -> None:
"""setup action with declined elicitation returns appropriate message."""
from unittest.mock import AsyncMock, MagicMock
tool_fn = _make_tool()
with patch("unraid_mcp.tools.health.elicit_and_configure", new=AsyncMock(return_value=False)):
result = await tool_fn(action="setup", ctx=MagicMock())
assert (
"not configured" in result.lower()
or "declined" in result.lower()
or "cancel" in result.lower()
)

View File

@@ -1,14 +1,14 @@
"""Health monitoring and diagnostics. """Health monitoring and diagnostics.
Provides the `unraid_health` tool with 3 actions for system health checks, Provides the `unraid_health` tool with 4 actions for system health checks,
connection testing, and subscription diagnostics. connection testing, subscription diagnostics, and credential setup.
""" """
import datetime import datetime
import time import time
from typing import Any, Literal, get_args from typing import Any, Literal, get_args
from fastmcp import FastMCP from fastmcp import Context, FastMCP
from ..config.logging import logger from ..config.logging import logger
from ..config.settings import ( from ..config.settings import (
@@ -20,13 +20,14 @@ from ..config.settings import (
) )
from ..core.client import make_graphql_request from ..core.client import make_graphql_request
from ..core.exceptions import ToolError, tool_error_handler from ..core.exceptions import ToolError, tool_error_handler
from ..core.setup import elicit_and_configure
from ..core.utils import safe_display_url from ..core.utils import safe_display_url
from ..subscriptions.utils import _analyze_subscription_status from ..subscriptions.utils import _analyze_subscription_status
ALL_ACTIONS = {"check", "test_connection", "diagnose"} ALL_ACTIONS = {"check", "test_connection", "diagnose", "setup"}
HEALTH_ACTIONS = Literal["check", "test_connection", "diagnose"] HEALTH_ACTIONS = Literal["check", "test_connection", "diagnose", "setup"]
if set(get_args(HEALTH_ACTIONS)) != ALL_ACTIONS: if set(get_args(HEALTH_ACTIONS)) != ALL_ACTIONS:
_missing = ALL_ACTIONS - set(get_args(HEALTH_ACTIONS)) _missing = ALL_ACTIONS - set(get_args(HEALTH_ACTIONS))
@@ -57,10 +58,12 @@ def register_health_tool(mcp: FastMCP) -> None:
@mcp.tool() @mcp.tool()
async def unraid_health( async def unraid_health(
action: HEALTH_ACTIONS, action: HEALTH_ACTIONS,
) -> dict[str, Any]: ctx: Context | None = None,
) -> dict[str, Any] | str:
"""Monitor Unraid MCP server and system health. """Monitor Unraid MCP server and system health.
Actions: Actions:
setup - Configure Unraid credentials via interactive elicitation
check - Comprehensive health check (API latency, array, notifications, Docker) check - Comprehensive health check (API latency, array, notifications, Docker)
test_connection - Quick connectivity test (just checks { online }) test_connection - Quick connectivity test (just checks { online })
diagnose - Subscription system diagnostics diagnose - Subscription system diagnostics
@@ -68,6 +71,17 @@ def register_health_tool(mcp: FastMCP) -> None:
if action not in ALL_ACTIONS: if action not in ALL_ACTIONS:
raise ToolError(f"Invalid action '{action}'. Must be one of: {sorted(ALL_ACTIONS)}") raise ToolError(f"Invalid action '{action}'. Must be one of: {sorted(ALL_ACTIONS)}")
if action == "setup":
configured = await elicit_and_configure(ctx)
if configured:
return (
"✅ Credentials configured successfully. You can now use all Unraid MCP tools."
)
return (
"⚠️ Credentials not configured. "
"Run `unraid_health action=setup` again to provide credentials."
)
with tool_error_handler("health", action, logger): with tool_error_handler("health", action, logger):
logger.info(f"Executing unraid_health action={action}") logger.info(f"Executing unraid_health action={action}")