- Consolidate Oikos on mac-mini (2026-07-06) - Client lifecycle in Go (2026-07-07) - Comprehensive audit & next steps (2026-07-07) - DB as source of truth (2026-07-07) - MCP tool completion (2026-07-07) Paths fixed in index.md to reflect planes/done/ locations. Active plans remaining: Prometheus LXC (Planned), implementation audit (Active).
169 lines
9.0 KiB
Markdown
169 lines
9.0 KiB
Markdown
# 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 <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:
|
|
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. |