Skip to content

hermes_tools_mcp_server: scope docstring claims memory/session_search/delegate_task but EXPOSED_TOOLS excludes them #26567

@simpolism

Description

@simpolism

Summary

The Hermes-tools MCP server that backs the codex_app_server runtime
(agent/transports/hermes_tools_mcp_server.py) claims to expose
memory, session_search, delegate_task, and text_to_speech in its
top-of-file docstring, but the EXPOSED_TOOLS tuple a few lines later
deliberately excludes memory, session_search, delegate_task,
and todo.

A Codex session running under this runtime therefore sees Hermes' web,
browser, vision, image-gen, skills, kanban, and TTS tools, but has no
memory save/recall API, no cross-session search, no subagent delegation,
and no shared todo state — even when running under a Hermes profile
whose stored MEMORY.md / USER.md are accessible on disk.

This is a documentation/implementation contradiction at minimum, and a
real capability gap at most: in the default Hermes runtime an agent can
save a fact (memory.add(...)) and recall it next session. The same
profile run with openai_runtime: codex_app_server cannot.

Where it is

agent/transports/hermes_tools_mcp_server.py

Docstring (lines ~1–34) — "Scope (what we expose)" lists:

  • delegate_task — Hermes subagents
  • memory — Hermes' persistent memory store
  • session_search — cross-session search
  • text_to_speech — TTS

Code, immediately below (lines ~44–58 of the inline comment, then
EXPOSED_TOOLS ~60–97):

#   - delegate_task / memory / session_search / todo — these are
#     `_AGENT_LOOP_TOOLS` in Hermes (model_tools.py:493). They require
#     the running AIAgent context to dispatch (mid-loop state), so a
#     stateless MCP callback can't drive them. Hermes' default runtime
#     keeps these working; the codex_app_server runtime cannot.
EXPOSED_TOOLS: tuple[str, ...] = (
    "web_search",
    "web_extract",
    # browser / vision / image / skills / TTS / kanban only
    ...
)

model_tools.py:484 confirms _AGENT_LOOP_TOOLS = {"todo", "memory", "session_search", "delegate_task"} — the dispatcher returns
"<tool> must be handled by the agent loop" if anything routes them through handle_function_call() from outside AIAgent.

text_to_speech is also docstring-listed but IS in EXPOSED_TOOLS — so
that one is correct, just stale in the prose.

Why it matters

Repro pattern that surfaced this:

  1. Hermes profile hermes-codex with ~/.hermes/profiles/hermes-codex/memories/MEMORY.md and USER.md populated.
  2. ~/.codex/config.toml has [mcp_servers.hermes-tools] pointing at agent/transports/hermes_tools_mcp_server.py.
  3. A Codex session in that profile (Codex-running-as-Hermes-Codex-bot in our Discord deployment) is asked whether it has memory access. It can read memory files via filesystem tools and lists what the MCP server actually advertises, and reports back: "no callable memory tool exposed."

That answer is correct given the code, but it contradicts what the docstring tells contributors the runtime should provide, and contradicts the cross-runtime parity expectation set by PR #24182 ("memory + skill nudges: yes (via item projection)" in the trade-off table). Whether _AGENT_LOOP_TOOLS should remain agent-loop-only is a separate architectural question; the immediate problem is that the file claims the tools are exposed when they are not.

What I'd expect

One of the following, in order of escalating effort:

  1. Doc fix (minimum): correct the top-of-file scope docstring to match EXPOSED_TOOLS. Remove delegate_task / memory / session_search from the "what we expose" list; the inline comment block already explains why. This stops misleading future contributors.

  2. Shim memory + session_search through a callback into the parent AIAgent. memory and session_search both have legitimate stateless implementations — session_search reads SessionDB (see Gateway doesn't pass session_db to AIAgent, causing "session_search must be handled by the agent loop" error #105 for the parallel gateway fix; session_db only needs to be passed in), and memory reads/writes the profile's MEMORY.md/USER.md files under a lock. The "needs agent-loop state" rationale applies most strongly to delegate_task (which actually spawns nested AIAgent loops) and todo (which is per-loop UX state). The other two could plausibly be exposed via a thin wrapper that takes HERMES_HOME from env (already set on the MCP subprocess) and routes through MemoryStore / SessionDB directly, without an AIAgent.

  3. Full parity (most work): keep memory/session_search agent-loop-only, but plumb the parent AIAgent's memory store and session DB through the MCP callback as a side channel so the tools work transparently from the codex runtime. Similar in spirit to Gateway doesn't pass session_db to AIAgent, causing "session_search must be handled by the agent loop" error #105's gateway fix.

Related

Caveats

I haven't audited whether the MemoryStore / SessionDB interfaces are
genuinely safe to invoke from a forked MCP subprocess (file locking
across processes, plugin-provided memory backends like Honcho/mem0 that
may assume in-process state, etc.). The doc-only fix (option 1) is
safe; options 2 and 3 want a careful look at the plugin memory paths in
plugins/memory/ first.

Metadata

Metadata

Assignees

No one assigned

    Labels

    P3Low — cosmetic, nice to havecomp/agentCore agent loop, run_agent.py, prompt buildertool/mcpMCP client and OAuthtype/docsDocumentation improvements

    Type

    No type
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions