You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
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 excludesmemory, 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:
Hermes profile hermes-codex with ~/.hermes/profiles/hermes-codex/memories/MEMORY.md and USER.md populated.
~/.codex/config.toml has [mcp_servers.hermes-tools] pointing at agent/transports/hermes_tools_mcp_server.py.
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:
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.
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.
codex_app_server does not receive Hermes SOUL/system prompt context #26035 — codex_app_server does not receive Hermes SOUL / system prompt / assembled memory context. Different problem (input context injection vs. tool exposure) but the same runtime and same impact on persona-sensitive profiles. Worth coordinating fixes.
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.
Summary
The Hermes-tools MCP server that backs the
codex_app_serverruntime(
agent/transports/hermes_tools_mcp_server.py) claims to exposememory,session_search,delegate_task, andtext_to_speechin itstop-of-file docstring, but the
EXPOSED_TOOLStuple a few lines laterdeliberately 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.mdare 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 sameprofile run with
openai_runtime: codex_app_servercannot.Where it is
agent/transports/hermes_tools_mcp_server.pyDocstring (lines ~1–34) — "Scope (what we expose)" lists:
Code, immediately below (lines ~44–58 of the inline comment, then
EXPOSED_TOOLS~60–97):model_tools.py:484confirms_AGENT_LOOP_TOOLS = {"todo", "memory", "session_search", "delegate_task"}— the dispatcher returns"<tool> must be handled by the agent loop"if anything routes them throughhandle_function_call()from outsideAIAgent.text_to_speechis also docstring-listed but IS inEXPOSED_TOOLS— sothat one is correct, just stale in the prose.
Why it matters
Repro pattern that surfaced this:
hermes-codexwith~/.hermes/profiles/hermes-codex/memories/MEMORY.mdandUSER.mdpopulated.~/.codex/config.tomlhas[mcp_servers.hermes-tools]pointing atagent/transports/hermes_tools_mcp_server.py.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_TOOLSshould 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:
Doc fix (minimum): correct the top-of-file scope docstring to match
EXPOSED_TOOLS. Removedelegate_task/memory/session_searchfrom the "what we expose" list; the inline comment block already explains why. This stops misleading future contributors.Shim memory + session_search through a callback into the parent AIAgent.
memoryandsession_searchboth have legitimate stateless implementations —session_searchreadsSessionDB(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_dbonly needs to be passed in), andmemoryreads/writes the profile's MEMORY.md/USER.md files under a lock. The "needs agent-loop state" rationale applies most strongly todelegate_task(which actually spawns nested AIAgent loops) andtodo(which is per-loop UX state). The other two could plausibly be exposed via a thin wrapper that takesHERMES_HOMEfrom env (already set on the MCP subprocess) and routes throughMemoryStore/SessionDBdirectly, without an AIAgent.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
hermes mcp serve(themcp_serve.pyfor Claude Code / Cursor) also doesn't expose memory, with implementation proposed in feat(mcp): add hermes-memory MCP server for bidirectional memory access #10833. That server is separate from thehermes-toolsMCP this issue covers, but a unifiedMemoryStore-over-MCP wrapper could potentially serve both.session_searchwas unreachable because the gateway didn't passsession_dbtoAIAgent. Same shape of fix may apply here.Caveats
I haven't audited whether the
MemoryStore/SessionDBinterfaces aregenuinely 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.