feat(mcp): understand and judge a metric anomaly: what happened, what was usual, what else went on (#5837)
get_anomaly gives each occurrence against what is usual at that hour, the metric around the newest one with its normal, what else happened on the resource's host around each occurrence (alert transitions, Ansible runs, other anomalies there and a count elsewhere, plus plugins' own events through a new mcp_events hook: the host agent's maintenance jobs and downtime, Docker's container starts, deaths and OOM kills), and how earlier anomalies on the same metric were judged. get_metric_normal reads the learned slots and the level that would be flagged. review_anomaly takes one id or several, all or nothing, with a note. get_anomaly_settings and (admin) set_anomaly_settings read and change the review window and each normal's floor, as Settings → Thresholds does. metric_series.points takes an optional `until`; core.alerts gains host_of_resource; floor_setting names its floor. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
+10
-1
@@ -79,6 +79,9 @@ class StewardMCPServer(MCPServer):
|
||||
self.tool_plugins: dict[str, str] = {}
|
||||
# plugin name -> its `mcp_overview(db)`: findings it adds to get_overview.
|
||||
self.overview_sources: dict[str, object] = {}
|
||||
# plugin name -> its `mcp_events(db, host_id, host_name, since, until)`:
|
||||
# what it knows happened on a host, for get_anomaly.
|
||||
self.event_sources: dict[str, object] = {}
|
||||
|
||||
# ── registration ─────────────────────────────────────────────────────────
|
||||
def register_tool(self, fn, *, role: UserRole, read_only: bool, plugin: str | None = None) -> None:
|
||||
@@ -103,7 +106,9 @@ class StewardMCPServer(MCPServer):
|
||||
|
||||
def register_plugin(self, name: str, module) -> None:
|
||||
"""Add a plugin's tools via its optional `register_mcp_tools(register)`,
|
||||
and its optional `mcp_overview(db)` as a get_overview findings source.
|
||||
its optional `mcp_overview(db)` as a get_overview findings source, and
|
||||
its optional `mcp_events(db, host_id, host_name, since, until)` as a
|
||||
source of what happened on a host (get_anomaly).
|
||||
|
||||
Idempotent per plugin (hot-reload drops its previous tools first) and
|
||||
tolerant: a raising hook is logged and leaves the plugin with no tools,
|
||||
@@ -114,6 +119,10 @@ class StewardMCPServer(MCPServer):
|
||||
overview = getattr(module, "mcp_overview", None)
|
||||
if callable(overview):
|
||||
self.overview_sources[name] = overview
|
||||
self.event_sources.pop(name, None)
|
||||
events = getattr(module, "mcp_events", None)
|
||||
if callable(events):
|
||||
self.event_sources[name] = events
|
||||
hook = getattr(module, "register_mcp_tools", None)
|
||||
if hook is None:
|
||||
return
|
||||
|
||||
Reference in New Issue
Block a user