Summary
In v2026.5.7 ("Tenacity"), tools/approval.py is only consulted from tools/terminal_tool.py:1827. MCP wrappers (ssh, docker, etc.) call subprocess.run directly with no gate — the agent can route any destructive command through an MCP and the approval system never sees it (no regex check, no Smart-mode prompt, no audit entry).
Repro
- Set
approvals.mode: smart (or manual) on the active profile.
- Configure any shell-wrapping MCP (
ssh, docker, the official @modelcontextprotocol/server-filesystem, etc.).
- Issue a chat request that exercises a destructive command via the MCP path, e.g. "Run
docker compose down -v in /path/to/stack on host X via ssh."
- Observe: command executes, no approval prompt, zero audit entries. The equivalent command via
terminal_tool correctly fires the gate.
Root cause
grep -rn "detect_dangerous_command\|check_approval" hermes-agent/tools/ shows only terminal_tool.py as the consumer. No shared interception layer for MCP-spawned subprocesses.
Local workaround
Patched our 4 highest-risk MCPs (ssh, docker, truenas, kopia) with a Python shim that imports detect_hardline_command + detect_dangerous_command via sys.path injection (MCPs run in a separate venv) and calls them before subprocess.run. Tier-1 only — regex gate + hardline floor + per-profile mode resolution. Smart-mode aux-LLM is not invoked (would require gateway-side IPC we don't have), so the shim hard-denies on regex hit in smart/manual; UX is a downgrade vs. the terminal_tool prompt-and-confirm.
Suggested fix
Either (A) move the gate into a transport layer wrapping every MCP's subprocess spawn so wrappers inherit the check automatically, or (B) expose /api/approval/check from the gateway so MCPs can call it synchronously over local IPC. Option B preserves the prompt UX for MCP-routed dangerous commands.
Environment
- Hermes Agent v2026.5.7 ("Tenacity")
- Linux (Ubuntu 24.04 ARM, DGX OS)
- Affected MCPs in our deployment: ssh, docker, truenas, kopia (all Python, share a separate venv from hermes-agent)
Happy to share shim source + threat-model notes for the filesystem MCP if useful.
Summary
In v2026.5.7 ("Tenacity"),
tools/approval.pyis only consulted fromtools/terminal_tool.py:1827. MCP wrappers (ssh,docker, etc.) callsubprocess.rundirectly with no gate — the agent can route any destructive command through an MCP and the approval system never sees it (no regex check, no Smart-mode prompt, no audit entry).Repro
approvals.mode: smart(ormanual) on the active profile.ssh,docker, the official@modelcontextprotocol/server-filesystem, etc.).docker compose down -vin /path/to/stack on host X via ssh."terminal_toolcorrectly fires the gate.Root cause
grep -rn "detect_dangerous_command\|check_approval" hermes-agent/tools/shows onlyterminal_tool.pyas the consumer. No shared interception layer for MCP-spawned subprocesses.Local workaround
Patched our 4 highest-risk MCPs (ssh, docker, truenas, kopia) with a Python shim that imports
detect_hardline_command+detect_dangerous_commandviasys.pathinjection (MCPs run in a separate venv) and calls them beforesubprocess.run. Tier-1 only — regex gate + hardline floor + per-profile mode resolution. Smart-mode aux-LLM is not invoked (would require gateway-side IPC we don't have), so the shim hard-denies on regex hit in smart/manual; UX is a downgrade vs. the terminal_tool prompt-and-confirm.Suggested fix
Either (A) move the gate into a transport layer wrapping every MCP's subprocess spawn so wrappers inherit the check automatically, or (B) expose
/api/approval/checkfrom the gateway so MCPs can call it synchronously over local IPC. Option B preserves the prompt UX for MCP-routed dangerous commands.Environment
Happy to share shim source + threat-model notes for the filesystem MCP if useful.