Files
oikos/plans/done/2026-08-04-hermes-mcp-client-integration.md
dtoro 0dd8c28815
Some checks failed
ci / build-test (push) Has been cancelled
ci / docker-build (push) Has been cancelled
ci / web (push) Has been cancelled
Desktop App / Build Linux (amd64) (push) Has been cancelled
Desktop App / Attach to Release (push) Has been cancelled
feat: MCP ping tool, tightened descriptions, and Hermes client docs
- Add  MCP tool — lightweight connectivity check returning server
  identity, no DB hit (resolves agent connection-test friction)
- Tighten 6 tool descriptions (get_relations, get_health_summary,
  query_metrics, get_trend, get_event_timeline, ping) to be searchable
  in the first 8-12 words
- Document Hermes MCP client setup in ADR-0012 with token security caveat
- Move completed plan to plans/done/
2026-08-05 00:21:56 +02:00

145 lines
6.6 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 2026-08-04 — Hermes MCP client integration: native tool surface for oikos
**Status:** Plan.
**Context:** Hermes Agent (mac-mini workstation) now connects to oikos's MCP server
as a native MCP client (`mcp_servers.oikos` in `~/.hermes/config.yaml`). All 37+ MCP
tools are available as `mcp__oikos__*` first-class Hermes tool calls — no more raw
curl with batch-initialize SSE parsing. The integration works; this plan tightens the
remaining seams.
**Trigger:** First-use retrospective identified three areas that make the integration
harder to use than it should be.
---
## 1. Motivation
The oikos MCP server (`internal/mcp/`) speaks Streamable HTTP at
`https://mcp.hubris.network/mcp`. Hermes Agent's native MCP client connects to it on
startup, discovers tools, and registers them as callable functions. This replaces the
previous pattern where agents fired raw curl requests with batch `initialize` +
`tools/call` envelopes.
Three friction points observed:
- **No lightweight connectivity check.** The `/healthz` HTTP endpoint exists but isn't
exposed at the MCP protocol layer. An agent that wants to verify the MCP server is
reachable must call a real tool (e.g. `list_entities` with a limit of 1) — every call
carries the Streamable HTTP session-initialization overhead.
- **Bearer token in plaintext.** `~/.hermes/config.yaml` stores the token directly in the
`mcp_servers.oikos.headers.Authorization` value. Hermes does not support env-var
interpolation in MCP server configs, so the token can't live only in `.env`.
- **Zero-visibility streaming overhead.** Streamable HTTP batches `initialize` +
`tools/call` per request. This adds ~2KB of transport per tool call that the agent
never sees. For a single `get_health_summary` call this is negligible; for a 10-tool
exploration pass it's 20KB of invisible overhead.
---
## 2. Changes
### I — MCP health/ping tool (`mcp__oikos__ping`)
**Why:** Agents need a zero-cost connectivity check before calling production tools.
Currently every check incurs the full Streamable HTTP initialize + tools/call round-trip.
**What:**
Add a `ping` tool that returns `{"ok": true, "server": "oikos", "version": "dev"}`.
No arguments. No DB hit. No auth check (already protected by the MCP transport's auth
layer — the request won't arrive if the bearer token is missing).
```go
// internal/mcp/tools.go
{
Name: "ping",
Description: "Lightweight connectivity check. Returns immediately with server identity, no DB hit.",
InputSchema: jsonschema.Must(nil), // no params
Handler: func(ctx context.Context, args json.RawMessage, caller CallerInfo) (json.RawMessage, error) {
return json.RawMessage(`{"ok":true,"server":"oikos","version":"` + version.Version + `"}`), nil
},
}
```
**Risk class:** read-only. No auth, no DB, no state. Auto-approves.
**Test:** `hermes mcp test oikos` (from the Hermes CLI) verifies MCP server reachability
independently; the `ping` tool gives agent code the same signal programmatically.
### II — Tool name documentation in server metadata
**Why:** Hermes prefixes MCP tools as `mcp_{server}_{tool}`, so `get_health_summary`
becomes `mcp__oikos__get_health_summary`. Agents discover tool names at runtime via
`tools/list`, but there's no short summary of what each tool group does that survives
into the MCP tool description.
**What:**
Audit and tighten every tool's `Description` field in `internal/mcp/tools.go` so the
first 812 words are a searchable one-liner an agent can pattern-match against.
Current descriptions that are vague or redundant get a prefix rewrite:
| Tool | Current description | Revised |
|------|-------------------|---------|
| `get_entity` | "Get entity metadata" | "Look up one entity by slug or UUID — type, state, attributes, health" |
| `list_entities` | "List entities" | "Browse entities by type, state, or name substring — paginated" |
| `upsert_knowledge` | "Record what you learned" | "Write a document/investigation/runbook to the knowledge graph — idempotent" |
| `run` | "Run ANY shell command" | "Execute a shell command on any host/LXC/VM — auto-classified by risk" |
Existing tools pass through unchanged if their description is already crisp. ~15 tools
get description rewrites.
**Risk class:** read-only (config change). No runtime effect.
### III — Env-var interpolation docs for Hermes config (oikos-side documentation)
**Why:** The bearer token lives in `~/.hermes/config.yaml` in plaintext because Hermes
does not support `${VAR}` interpolation in MCP server configs. This is a Hermes
upstream feature request, not an oikos change — but oikos should document the
workaround and track the upstream ask.
**What:**
Add a `### Hermes MCP client` subsection to `docs/infrastructure/mcp-server.md` (or
create it if it doesn't exist) that covers:
1. The config block to add to `~/.hermes/config.yaml` (already done — record it
for the next person).
2. The token exposure caveat: Hermes doesn't support env-var interpolation in
`mcp_servers` `headers` yet (upstream issue nousresearch/hermes-agent#TODO — file
once).
3. Workaround: `hermes config set security.redact_secrets true` (already default) so
the token value is stripped from tool output and logs even if it appears in
diagnostic text.
4. How to verify the connection: `hermes mcp list``hermes mcp test oikos`.
**Risk class:** docs-only.
---
## 3. Open questions
| Question | Decision |
|----------|----------|
| Should `ping` bypass auth entirely or still require a valid bearer token? | **Still requires auth.** The MCP transport layer validates the token before routing to `ping` — no special treatment needed. If the token is missing, the request never reaches the handler. |
| Who files the Hermes upstream feature request for `${VAR}` interpolation? | **Oikos operator** (dtoro). The need is specific to this deployment. File at https://github.com/NousResearch/hermes-agent/issues. |
---
## 4. Not doing (yet)
- **Persistent MCP sessions** — Streamable HTTP stateless mode is fine for the
current tool-call volume (~15 calls per agent turn). Persistent sessions would
save ~2KB per call but add connection lifecycle complexity. Revisit if per-turn
tool calls exceed 20.
- **`tools/list` caching** — Hermes already caches tool discovery at session start.
The 37-tool list is ~4KB; caching adds complexity for negligible savings.
---
## 5. Verification
1. `curl -X POST https://mcp.hubris.network/mcp ... -d '...ping...'` returns `{"ok":true,"server":"oikos","version":"dev"}`
2. `hermes mcp list` shows `ping` among oikos tools
3. `hermes doctor` passes
4. Tool descriptions are crisp: `hermes mcp list` output for oikos shows prefixed summaries