9.0 KiB
Plan: Complete MCP tool surface — Hermes as the primary operator interface
Status: Planned (2026-07-07, rev 2) — supersedes rev 1 (CLI port was the wrong paradigm)
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 <service> -n <lines> |
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 <vmid> |
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 <service> on target host |
logs |
"show me caddy logs" | Already handled by tail_log tool |
pct_exec |
"run apt update on LXC 121" | pct exec <vmid> -- <cmd> 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 <verb> <service> (gated: enable = config_mutation) |
Each action type needs:
- A clear input schema (what params Hermes must provide)
- Policy classification (risk class, blast radius)
- Verification step (how to confirm it worked)
- 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:
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_logreturns 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.