feat(mcp): docker and host-agent tools; TLS and crash-loop cutoffs in Settings
MCP-5 of milestone 461 "MCP server: Steward as an LLM-inspectable, LLM-operable hub". Contributed through each plugin's register_mcp_tools hook, so they exist only while the plugin is enabled. docker (viewer, read-only): - list_containers: filter by host, status, compose project, or problem (restarting, crash_looping, unhealthy, oom_killed, exited_nonzero); problems sort first and each row names what is wrong. - get_container: state, CPU/memory history across the raw/hourly seam, lifecycle events. A unique part of a swarm task name is enough. - list_swarm_services: replicas per node (agent-less placements as ghosts) and the swarm's nodes. - search_container_logs: text (% and _ literal), stream, range, context lines, paging by a full-precision `before` cursor; over-long lines keep both ends and say how much of the middle was cut. - get_docker_disk_usage: bytes per host, reclaimable first. - mcp_overview: failing containers and under-replicated swarm services join get_overview. host_agent: list_agents (reporting / stale / pending, version, OS), and an overview finding for agents registered over 30 minutes ago that never checked in. Settings > Thresholds gains the TLS-expiry warn/crit days (were 14/3 in code) and the crash-loop deaths-per-hour count (3), which get_overview and the docker tools now read. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,22 @@
|
||||
"""The host_agent plugin's MCP tools."""
|
||||
from datetime import datetime, timedelta, timezone
|
||||
from types import SimpleNamespace as NS
|
||||
from unittest.mock import MagicMock
|
||||
|
||||
import plugins.host_agent as agent_plugin
|
||||
from plugins.host_agent.mcp_tools import agent_state
|
||||
|
||||
NOW = datetime(2026, 10, 6, 12, 0, tzinfo=timezone.utc)
|
||||
|
||||
|
||||
def test_agent_state():
|
||||
assert agent_state(NS(last_seen_at=None), NOW, 180) == "pending"
|
||||
assert agent_state(NS(last_seen_at=NOW - timedelta(seconds=60)), NOW, 180) == "reporting"
|
||||
assert agent_state(NS(last_seen_at=NOW - timedelta(minutes=10)), NOW, 180) == "stale"
|
||||
|
||||
|
||||
def test_plugin_exposes_the_mcp_hooks():
|
||||
reg = MagicMock()
|
||||
agent_plugin.register_mcp_tools(reg)
|
||||
assert [c.args[0].__name__ for c in reg.call_args_list] == ["list_agents"]
|
||||
assert callable(agent_plugin.mcp_overview)
|
||||
Reference in New Issue
Block a user