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.
result = safe_display_url("https://[invalid")
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.
Provides the `unraid_health` tool with 3 actions for system health checks,
connection testing, and subscription diagnostics.
Provides the `unraid_health` tool with 4 actions for system health checks,
connection testing, subscription diagnostics, and credential setup.
"""
import datetime
import time
from typing import Any, Literal, get_args
from fastmcp import FastMCP
from fastmcp import Context, FastMCP
from ..config.logging import logger
from ..config.settings import (
@@ -20,13 +20,14 @@ from ..config.settings import (
)
from ..core.client import make_graphql_request
from ..core.exceptions import ToolError, tool_error_handler
from ..core.setup import elicit_and_configure
from ..core.utils import safe_display_url
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:
_missing = ALL_ACTIONS - set(get_args(HEALTH_ACTIONS))
@@ -57,10 +58,12 @@ def register_health_tool(mcp: FastMCP) -> None:
@mcp.tool()
async def unraid_health(
action: HEALTH_ACTIONS,
) -> dict[str, Any]:
ctx: Context | None = None,
) -> dict[str, Any] | str:
"""Monitor Unraid MCP server and system health.
Actions:
setup - Configure Unraid credentials via interactive elicitation
check - Comprehensive health check (API latency, array, notifications, Docker)
test_connection - Quick connectivity test (just checks { online })
diagnose - Subscription system diagnostics
@@ -68,6 +71,17 @@ def register_health_tool(mcp: FastMCP) -> None:
if action not in 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):
logger.info(f"Executing unraid_health action={action}")