- 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/
145 lines
6.6 KiB
Markdown
145 lines
6.6 KiB
Markdown
# 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 8–12 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 (~1–5 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 |