Version: v1.0.0 (binding under docs/compatibility_policy.md §3 from this release)
Status: Stable surface
Module: mythic_vibe_cli.agent_api
CLI surface: mythic-vibe surface hermes (HTTP) and mythic-vibe hermes (CLI introspection)
Hermes is the Greek messenger-god — he crosses boundaries. The Hermes Agent surface gives any external AI agent — Claude, GPT, Gemini, Aider, Goose, custom tooling — the power to see and do anything Mythic Vibe CLI exposes, through one of two access modes:
| Mode | For | Shape |
|---|---|---|
| TCL (Tool Calling Library) | In-process Python agents | from mythic_vibe_cli.agent_api import HermesAgent — every operation is a method, every artifact readable, every state introspectable |
| HTTP API | Any agent that speaks HTTP — non-Python clients, multi-language tooling, network-isolated agents | Token-protected JSON endpoints over a stdlib HTTP server |
Both modes share one core (agent_api/core.py) so behavior is identical regardless of access mode. The curated tool surface is the contract; the implementation can change without breaking callers.
The Pythonic face. Drop straight into an agent loop:
from mythic_vibe_cli.agent_api import HermesAgent, build_default_agent
agent = build_default_agent(root="/path/to/project")
# Convenience methods for common queries
status = agent.status()
state = agent.state()
diagnostic = agent.doctor()
# Generic invocation by tool name + kwargs
result = agent.invoke("checkin", phase="build", update="Implemented login audit")
print(result.ok, result.value)
# Agent introspection — JSON Schema list suitable for direct
# use as the `tools` argument in Anthropic Tool Use or OpenAI
# function calling
tools = agent.list_tools()The HermesAgent.invoke(tool, **args) method returns an InvocationResult with status (ok / error / unknown_tool / validation_error), value, error, elapsed_ms. The convenience accessors (agent.status() etc.) return the value directly for the most common queries.
Launch the surface:
mythic-vibe surface hermes
# ...prints:
# URL: http://127.0.0.1:8770/api/health
# Token: <auto-generated 32-byte URL-safe token>
# Tool count: 18Or with explicit settings:
mythic-vibe surface hermes --bind 127.0.0.1 --port 8770 --token "$MY_TOKEN"Endpoints (auth via X-Hermes-Token header OR a token field in POST bodies):
| Method | Path | Purpose |
|---|---|---|
GET |
/api/health |
No-auth health check (for reverse-proxy probes). |
GET |
/api/tools |
List registered tools as JSON Schema. |
POST |
/api/invoke |
Invoke a tool by name. Body: {"tool":"<name>","args":{...},"request_id":"<optional>"}. |
GET |
/api/state |
Convenience for state_show. |
GET |
/api/artifacts?under=…&glob=…&limit=… |
List project artifacts. |
GET |
/api/artifacts/<relpath>?max_bytes=… |
Read one artifact (refuses path escapes). |
GET |
/api/events?limit=… |
Recent event-log entries. |
Example end-to-end:
TOKEN="<from launch output>"
# List tools
curl -H "X-Hermes-Token: $TOKEN" http://127.0.0.1:8770/api/tools
# Invoke a tool
curl -X POST -H "Content-Type: application/json" \
-d '{"token":"'$TOKEN'","tool":"checkin","args":{"phase":"build","update":"…"}}' \
http://127.0.0.1:8770/api/invoke
# Read an artifact
curl -H "X-Hermes-Token: $TOKEN" \
"http://127.0.0.1:8770/api/artifacts/mythic/status.json"Eighteen tools ship in build_default_agent(). Each declares its capabilities (using the PH-20.3 plugin-capability vocabulary) and side effects so operators auditing via mythic-vibe hermes tools can see what an agent can actually touch.
| Tool | Capabilities | Side effects | Purpose |
|---|---|---|---|
status |
read |
— | Project status summary |
doctor |
read |
— | Project diagnostics |
drift |
read |
— | docs↔code drift scan (dashboard: true for the rolled-up scorecard) |
state_show |
read |
— | Read schema-versioned project state |
checkin |
read, file-write |
writes status.json + DEVLOG.md | Phase update + tracking |
packet_create |
read, file-write |
writes mythic/packets/ | Create reusable packet |
packet_lint |
read |
— | Heuristic packet quality lint |
verify |
read, subprocess, file-write |
runs pytest/ruff/mypy; writes VER-* | Verification gates |
reflect |
read, file-write |
writes mythic/handoffs/ | Reflection handoff |
review_architecture |
read |
— | Quarterly governance checklist |
ai_recommend |
read |
— | Pure-policy model picker (zero provider calls) |
provenance_verify |
read |
— | SHA-256 verify of plunder imports |
workflow_lineage |
read |
— | Mermaid graph of one workflow |
persona_show |
read |
— | Active persona (or none) |
plugin_doctor |
read |
— | Plugin capability + breaker audit |
read_artifact |
read |
— | Read a project-relative file (refuses path escapes) |
list_artifacts |
read |
— | List files under a project-relative dir |
recent_events |
read |
— | Read mythic/events.jsonl tail |
The full, machine-readable list is what agent.list_tools() and GET /api/tools return — that's the source of truth.
The Hermes surface is asset A6 in docs/security/threat_model.md. Read both before running it.
- A token-protected programmatic surface that exposes a curated subset of the CLI to an agent.
- Defaulted to loopback bind (
127.0.0.1). External exposure requires an explicit--bind 0.0.0.0and a TLS reverse proxy you provide. - Audited: every invocation appends one line to
mythic/events.jsonlvia the existing PH-09 event-log primitive. Operators see exactly what an agent did.
- It is not a sandbox. Agents calling Hermes can do everything the curated tools allow — including
verify(which runs subprocesses) andcheckin/packet_create/reflect(which mutate project state). - It does not enforce capability declarations at the OS level. The declarations are operator-visible documentation today (PH-20.3 vocabulary); a future subprocess sandbox could promote them to enforcement.
- 32-byte URL-safe token compared via
secrets.compare_digest(constant-time). MAX_REQUEST_BODY_BYTES = 65_536cap on POST bodies.- 30-second per-connection socket timeout.
- Path-escape refusal in
read_artifactandlist_artifacts(refuses any path that resolves outside the project root). - Validation gate on every invocation: required-args + type + enum checks against the JSON Schema before the tool implementation runs.
| Scenario | Bind | TLS / Proxy | Notes |
|---|---|---|---|
| Local dev with an agent in the same machine | 127.0.0.1 (default) |
none | Token is sufficient; loopback only |
| Multi-process on the same host (agent in a sibling process) | 127.0.0.1 (default) |
none | Token in POST body or header |
| Remote operator → server | 0.0.0.0 (explicit) |
required TLS reverse proxy you provide | Token is the only auth — HTTPS is mandatory in production |
| CI / automated agent | loopback inside the runner; tear down per job | none | Job-scoped token |
For agents that prefer "ask the CLI" over the HTTP surface:
mythic-vibe hermes tools # list registered tools (text or --json)
mythic-vibe hermes inspect --tool packet_create # show one tool's full spec
mythic-vibe hermes invoke --tool status # invoke directly without HTTP
mythic-vibe hermes invoke --tool checkin \
--args '{"phase":"build","update":"…"}' --jsonThe invoke subcommand is useful for shell-script automation and operator debugging without spinning up the HTTP server. Its exit code matches the invocation result: 0 on success, 1 on operational failure / unknown tool, 2 on user input error.
The default registry is built by build_default_agent(). Advanced callers can construct a HermesCore directly and register custom tools:
from mythic_vibe_cli.agent_api import (
HermesCore, ToolSpec, Invocation, InvocationResult, HermesAgent,
)
def my_tool(core, args):
payload = args["payload"]
return InvocationResult(status="ok", value={"echo": payload})
core = HermesCore(root="/path/to/project")
core.register(
ToolSpec(
name="echo",
description="Echo back the payload.",
input_schema={
"type": "object",
"properties": {"payload": {"type": "string"}},
"required": ["payload"],
},
capabilities=("read",),
),
my_tool,
)
agent = HermesAgent(core)
print(agent.invoke("echo", payload="hi").value) # {"echo": "hi"}Custom tools can also be packaged as a Mythic plugin (see docs/PLUGIN_AUTHORING_GUIDE.md) and registered at agent-construction time by the operator's plugin loader.
- Always declare capabilities. Operators auditing via
mythic-vibe hermes toolsshould see what your tool needs —readat minimum,network/subprocess/file-writeif applicable. - Use side-effect tags for any tool that writes files or runs subprocesses. They surface in
mythic-vibe hermes toolsoutput and inGET /api/tools. - Don't raise into the invoker.
HermesCore.invokecatches everything anyway, but a tool that returns a structuredInvocationResultwithstatus="error"and a usefulerrorfield gives the agent a much better signal than a stringified traceback. - Validate inputs in the JSON Schema rather than the tool body. The minimal validator in
core.pyenforcesrequired+type+enum; richer validation can happen in the tool implementation.
Hermes tool specs are JSON-Schema-compatible. They can be passed directly to Anthropic's tools parameter:
import anthropic
from mythic_vibe_cli.agent_api import build_default_agent
agent = build_default_agent(root=".")
client = anthropic.Anthropic()
response = client.messages.create(
model="claude-opus-4-7",
max_tokens=4096,
tools=agent.list_tools(), # JSON Schema for every Hermes tool
messages=[{
"role": "user",
"content": "Read the project status and write a one-line summary.",
}],
)
# When Claude calls a tool, route the call through Hermes:
for block in response.content:
if block.type == "tool_use":
result = agent.invoke(block.name, **block.input)
# Send result.value back to Claude as a tool_result blockSame pattern works for OpenAI function calling (the JSON Schema shape is compatible), Gemini tool calling, etc.
Per docs/compatibility_policy.md §3, the Hermes surface lands in the Stable tier. From v1.0.0 onward:
- The 18 default tool names + their input-schema shapes are SemVer-stable. New tools may be added (MINOR); existing tool fields may be added (MINOR); removals or type changes require a MAJOR bump with the documented deprecation cadence.
- The HTTP endpoint paths + auth contract are SemVer-stable.
- The
HermesAgent/HermesCorePython class names + public method signatures are SemVer-stable. - The
ToolSpec+Invocation+InvocationResultdataclass shapes are SemVer-stable. - Internal helpers (
_safe_serialise,_validate_against_schema, etc.) are NOT public — they may change in any release.
Every Hermes invocation appends one line to mythic/events.jsonl via the existing PH-09 runtime/event_log primitive. Operators inspect via:
mythic-vibe hermes invoke --tool recent_events --json
# or
curl -H "X-Hermes-Token: $TOKEN" "http://127.0.0.1:8770/api/events?limit=20"
# or read the file directly:
tail -20 mythic/events.jsonlEach audit event records tool, request_id (if supplied), status, elapsed_ms. The same event-log file is what the TUI's "Recent Events" panel reads, so Hermes invocations show up there too.
mythic_vibe_cli/agent_api/core.py— shared core (HermesCore, ToolSpec, Invocation, InvocationResult, audit emit).mythic_vibe_cli/agent_api/tcl.py— Python in-process surface (HermesAgent, build_default_agent, default tool implementations).mythic_vibe_cli/agent_api/http_api.py— HTTP server (HermesHttpConfig, HermesHttpServer, build_default_http_server, pure routing handlers).mythic_vibe_cli/commands.py—cmd_surface_hermes,cmd_hermes,cmd_hermes_tools,cmd_hermes_inspect,cmd_hermes_invoke.tests/test_hermes_agent.py— 34 tests covering core + TCL.tests/test_hermes_http.py— 24 tests covering HTTP API (incl. live server).tests/test_hermes_cli.py— 16 tests covering CLI integration + registration.docs/security/threat_model.md— asset A6 + threat rows.docs/compatibility_policy.md— v1.0 SemVer rules for the Hermes surface.docs/plugins.md§9a — capability-declaration vocabulary shared with Hermes ToolSpec.