# Plan: Complete MCP tool surface — Hermes as the primary operator interface **Status:** Done (2026-07-08) — all 4 phases complete. Matrix notification + approve→execute chain wired. ## Goal The operator (dtoro) interacts with the homelab through Hermes, the AI agent. Hermes talks to `oikos api` via MCP protocol at `:8090`. The command-line `homelab` CLI is an implementation detail — the agent is the interface. This plan completes the MCP tool surface so Hermes can **observe, orient, decide, and act** on the full homelab without the operator touching a shell. Once complete, `bin/homelab` (Python) and `bin/oikos` (stale binary) are deleted. ## Architecture ``` ┌──────────────────────────────────────────────────────────┐ │ dtoro (operator) │ │ Interface: Hermes (natural language via chat/matrix) │ │ Also: Oikos Console (web UI, future) │ └──────────────────────┬───────────────────────────────────┘ │ delegates tasks ▼ ┌──────────────────────────────────────────────────────────┐ │ Hermes (AI agent, Docker :8092) │ │ │ │ Skills: health triage, signal response, execution │ │ tracking, pattern learning, homelab ops, knowledge │ │ │ │ Protocol: MCP streamable HTTP → oikos api :8090 │ │ Authority: auto-act on reversible_low only; escalates │ │ config_mutation/destructive to operator via Matrix │ └──────────────────────┬───────────────────────────────────┘ │ MCP tools ▼ ┌──────────────────────────────────────────────────────────┐ │ oikos api (Go, Docker :8090) │ │ │ │ REST API + MCP server + SSE event stream │ │ Same DB, auth middleware, audit log, policy engine │ │ │ │ Actuator (separate container, restricted SSH key) │ │ Notifier (Matrix alerts) │ │ Scheduler (OODA loop: probes, signals, patterns) │ └──────────────────────────────────────────────────────────┘ ``` ## Current MCP tool surface (15 tools) ### Observe — ✅ complete | Tool | What it does | |------|-------------| | `get_entity` | Get entity by slug or UUID | | `list_entities` | List entities by type, state, search | | `get_relations` | Get relationships for an entity | | `get_blast_radius` | Entities affected if this entity goes down | | `get_health_summary` | Current fleet health summary | | `get_signal_history` | Open and recent signals | | `get_trend` | Metric trends for an entity | | `get_event_timeline` | Recent events | | `query_metrics` | Time-series metric queries | | `search_knowledge` | Full-text search across docs/runbooks | ### Orient — ✅ complete | Tool | What it does | |------|-------------| | `get_patterns` | Learned action patterns | | `get_skills` | Available automation skills | | `get_audit_trail` | Audit log queries | ### Act — ⚠️ exists, limited scope | Tool | What it does | |------|-------------| | `request_execution` | Hermes-only mutation path. Takes `target` (entity slug) + `action` (verb). Policy-gated. | ### Self — ✅ complete | Tool | What it does | |------|-------------| | `get_agent_activity` | Agent self-inspection | ## What's missing — MCP tools to add ### Phase 1 — Operational visibility (P0, this week) Tools Hermes needs to answer "what's happening right now?" | Tool | Input | Output | Implementation | |------|-------|--------|----------------| | `tail_log` | `service_slug`, `lines` | Last N log lines via `journalctl` | SSH to host, `journalctl -u -n ` | | `get_service_status` | `service_slug` | `systemctl is-active` / `is-enabled` + uptime | SSH to host, `systemctl show` | | `ping_service` | `service_slug` | HTTP/TCP reachability check | HTTP GET health endpoint from oikos scheduler probe data | | `list_lxcs` | none | All LXCs with ID, host, IP, state | DB query from `entity_status` table | | `get_lxc_state` | `lxc_slug` | RAM, CPU, disk, uptime | SSH to Proxmox host, `pct status ` | ### Phase 2 — Execution expansion (P1, next 2 weeks) Expand `request_execution` to handle the full operational surface. Currently it writes a generic execution record. It needs to understand action types and route them: | Action type | What Hermes says | What actuator does | |-------------|-----------------|--------------------| | `restart` | "restart lxc:caddy" | `systemctl restart ` on target host | | `logs` | "show me caddy logs" | Already handled by `tail_log` tool | | `pct_exec` | "run apt update on LXC 121" | `pct exec -- ` on Proxmox host | | `apt_upgrade` | "upgrade packages on lxc:caddy" | `apt update && apt upgrade -y` in detached screen | | `apt_audit` | "audit packages on all LXCs" | `dpkg -l` + upgradable count per host | | `systemctl` | "enable service on lxc:foo" | `systemctl ` (gated: enable = config_mutation) | Each action type needs: 1. A clear input schema (what params Hermes must provide) 2. Policy classification (risk class, blast radius) 3. Verification step (how to confirm it worked) 4. Ledger entry (what happened, when, by whom) ### Phase 3 — Agent-to-operator escalation (P1, next 2 weeks) When Hermes hits a `config_mutation` or `destructive` action, it escalates to the operator. This path needs to be solid: | Component | Current state | Target | |-----------|--------------|--------| | Matrix alert | ⚠️ Notifier polls DB, but Hermes doesn't trigger it | Hermes calls `request_execution` → policy gate rejects → notifier sends Matrix alert with approval token | | Approval flow | ⚠️ `oikos homelab approval` exists in Go but agent can't use it | Operator reacts ✅/❌ on Matrix → webhook → approval token consumed → actuator proceeds | | Execution tracking | ✅ `request_execution` writes to `executions` table | Add `get_execution_status` MCP tool so Hermes can poll for results | ### Phase 4 — Cleanup (P2, after all tools work) | Action | Notes | |--------|-------| | Delete `bin/homelab` | Python CLI. Disconnected from DB, imports deleted modules. Agent handles everything. | | Delete `bin/oikos` | Stale pre-built arm64 binary. Docker image is the canonical build. | | Remove `oikos homelab` Go subcommand | The `homelab` CLI role in the Go binary is a dead end. Remove it or keep as debug-only. | | Update AGENTS.md | Document the complete MCP tool surface. Remove CLI references. | ## Implementation approach Each new MCP tool is registered in `internal/mcp/server.go` following the existing pattern: ```go register(&mcp.Tool{Name: "tail_log", Description: "...", InputSchema: ...}, func(ctx context.Context, req *mcp.CallToolRequest) (*mcp.CallToolResult, error) { // ... }) ``` SSH-based tools (`tail_log`, `get_service_status`, `list_lxcs`, `get_lxc_state`) reuse the mesh-address resolution already working for `request_execution`: entity slug → DB lookup → inventory attributes → mesh IP → `ssh user@ip`. The actuator container holds the SSH key. MCP tools that need SSH should route through the actuator's execution queue rather than holding their own SSH key, maintaining the security boundary: Hermes → MCP → API → execution queue → actuator → SSH. ## Verification - Hermes can answer "show me caddy logs" → `tail_log` returns journal lines - Hermes can answer "what's the fleet health?" → `get_health_summary` + `list_lxcs` - Hermes can answer "restart caddy" → `request_execution(action="restart", target="lxc:caddy")` → actuator SSHs → caddy restarts → Hermes confirms - Hermes can answer "upgrade packages on dns LXC" → `request_execution(action="apt_upgrade", target="lxc:dns")` → gated → actuator runs in screen → Hermes reports result - `bin/` directory is empty or contains only Docker-related scripts ## Changelog - 2026-07-07 rev 2 — flipped paradigm. Operator interface is Hermes via MCP, not CLI. Replaced 4-phase CLI port plan with 4-phase MCP completion plan. - 2026-07-07 rev 1 — cataloged 27 Python CLI subcommands vs 4 Go subcommands.