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

6.6 KiB
Raw Permalink Blame History

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).

// 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 listhermes 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