One-Sentence Summary
Hermes (a Telegram bot gateway) controlling Claude Code (Anthropic’s CLI coding agent) has 7 technical routes: direct terminal invocation, SDK programmatic API, MCP bidirectional communication, A2A Agent-to-Agent protocol, ACP Agent Communication protocol, Hooks system, and Cloud sessions. Hermes already has codex_app_server and copilot_acp subprocess runtime templates — adding a Claude Code runtime is essentially copying an existing pattern. For Telegram bot scenarios, stream-json pipeline + canUseTool external permission approval + existing codex_app_server template is the most pragmatic route today.
Why Control Claude Code From External Systems
Claude Code is a terminal-based coding agent. You type claude, it enters an interactive session, reads your codebase, edits files, runs tests, commits git — all inside a TTY.
But often you don’t want to sit at the terminal:
- You want to send a message from Telegram and have Claude Code fix a bug
- You want a bot gateway (Hermes) to route multiple users’ messages to different Claude Code sessions
- You want to trigger Claude Code in CI/CD pipelines for code review
- You want one agent to invoke another (Agent-to-Agent collaboration)
The common thread: the control plane is not in a TTY — it needs a programmatic interface to drive Claude Code.
Hermes is NousResearch’s open-source agent framework — a Telegram bot gateway written in Python, routing messages through gateway/run.py to various LLMs. It already calls OpenAI, Anthropic, DeepSeek, and even has a codex_app_server runtime that hands entire conversation turns to the Codex CLI subprocess. The question: how to bring Claude Code in?
Claude Code v2.1.267 (the version at time of writing) offers more than one path. Let’s break them down.
Route 1: Direct Terminal Invocation
Principle: Claude Code is fundamentally a CLI binary. Any language that can spawn subprocesses can call it directly, communicating via stdin/stdout. This is the lowest-level route, and the foundation of all others — the SDK does essentially the same thing under the hood.
Hermes precedent: Hermes’s codex_app_server runtime already does this — spawning codex app-server as a subprocess, driving the entire conversation turn via JSON-RPC over stdio. The file agent/transports/codex_app_server.py fully implements this pattern. Adding a Claude Code runtime is essentially copying this template.
1.1 Headless Mode: -p / --print
| |
--output-format has three options (only in --print mode):
| Format | Use case | Characteristics |
|---|---|---|
text | Simple text | One-shot return, for short tasks |
json | Structured result | Returns one JSON object with the complete result |
stream-json | Real-time streaming | Each message (thinking, tool calls, results) output incrementally, for long tasks and live monitoring |
stream-json is critical for Telegram bot scenarios — you don’t want to wait 5 minutes for a complete result. You want to see “Claude is reading a file,” “Claude is editing code,” and push those progress updates to Telegram in real time.
1.2 Streaming Input: --input-format stream-json
| |
This lets external systems send prompts as JSON messages via stdin incrementally, rather than all at once. You can have a continuous conversation within one Claude Code session — send a message, get a reply, send another.
With --include-partial-messages, you even get token-by-token partial messages — you can see Claude typing in real time.
1.3 Session Management
| |
Session persistence is built in. Each session has a unique ID that can be resumed. This means Hermes can maintain independent Claude Code sessions per Telegram user — user A’s conversation doesn’t leak to user B.
1.4 Background Session Management
| |
--bg is a killer feature for Telegram bots. Hermes can: receive a user message, claude --bg to start a session, store the session ID, poll with claude logs or stream with --output-format stream-json back to Telegram, and stop to preserve the session when the user leaves.
1.5 Permission Control
This is the most critical issue for Telegram bot scenarios: Claude Code prompts for permission by default. In unattended bot scenarios, nobody is at the terminal to click “yes.”
Claude Code offers 6 permission modes:
| Mode | Behavior | Use case |
|---|---|---|
manual | Ask every time | Interactive, human at terminal |
acceptEdits | Auto-accept file edits, still ask for others | Semi-auto |
auto | Smart judgment on what’s safe to auto-execute | Semi-auto |
plan | Read-only, no modifications | Code review, security analysis |
bypassPermissions | Skip all permission checks | Full auto (high risk) |
dontAsk | Don’t ask; deny anything that would prompt | Safest auto mode |
| |
--permission-prompts host is the bridge — it allows an external system (via SDK) to answer permission questions. This is exactly what Telegram bot scenarios need: Hermes receives Claude Code’s permission request, forwards it to the user in Telegram to click “allow” or “deny.”
1.6 Subprocess Call Example (Python, Hermes-style)
| |
Route 2: Claude Code SDK (Programmatic API)
Principle: Anthropic wraps a query() function in the @anthropic-ai/claude-code npm package, encapsulating process management, message parsing, streaming output, and permission approval into an API layer. The SDK and direct terminal invocation share the same foundation — both spawn the claude subprocess — but the SDK wraps all the plumbing.
2.1 TypeScript/JavaScript SDK
| |
2.2 Python SDK
| |
2.3 canUseTool Callback — External Permission Approval
This is the SDK’s biggest advantage over raw subprocess calls:
| |
canUseTool turns Hermes into a permission approval proxy: every time Claude Code wants to use a tool, it asks Hermes first, and Hermes asks the Telegram user. This is far more granular than bypassPermissions (allow all) or dontAsk (deny all).
2.4 Complete Type Definitions
The SDK ships with sdk-tools.d.ts, covering all built-in tool types — AgentInput, BashInput, FileEditInput, McpInput, WorkflowInput, CronCreateInput, and 30+ more. External systems can construct and parse tool calls type-safely.
2.5 SDK vs Direct Terminal Invocation
| Dimension | Direct terminal | SDK |
|---|---|---|
| Process management | Manual spawn/wait | Automatic |
| Message parsing | Manual JSON.parse | Typed objects |
| Permission approval | No canUseTool | ✅ Callback-based |
| Error handling | Manual exit code checks | Built-in retry and error types |
| Language | Any | TS/JS, Python |
Conclusion: If Hermes’s component is Node or Python, use the SDK. To embed in Hermes’s Python gateway, you can follow the codex_app_server subprocess pattern and wrap it yourself — because Hermes’s transport layer already has mature subprocess JSON-RPC infrastructure.
Route 3: MCP (Model Context Protocol)
Principle: MCP is Anthropic’s open standard — letting AI models connect to external tools and data sources. Claude Code can be both an MCP client (connecting to others’ servers) and an MCP server (exposing its own capabilities).
Hermes already has this: Hermes has mcp_serve.py — an MCP server exposing Telegram conversations as tools, letting any MCP client (Claude Code, Cursor, Codex) read message history, send messages, and manage approvals. Meanwhile, tools/mcp_tool.py can already spawn claude mcp serve as a subprocess.
3.1 Claude Code as MCP Server
| |
This turns Claude Code into an MCP server, exposing code editing, command execution, and file I/O via stdio. Any MCP client can connect.
3.2 Claude Code as MCP Client
| |
3.3 Four Combinations
MCP is the only bidirectional peer-to-peer route — both Hermes and Claude Code can be server or client:
Combination A: Hermes as Server, Claude Code as Client
Hermes implements an MCP server exposing Telegram tools (send/read messages). Claude Code connects via claude mcp add and can directly send/receive Telegram messages in code. This is the direction Hermes’s mcp_serve.py already implements.
Combination B: Claude Code as Server, Hermes as Client
Claude Code runs claude mcp serve to expose coding capabilities. Hermes connects as an MCP client, calling Claude Code’s tools like any other tool. This is what Hermes’s tools/mcp_tool.py already supports.
Combination C: Both as Servers Both run MCP servers, interconnected. For complex collaboration scenarios.
3.4 Isolation Control
| |
--strict-mcp-config ensures Claude Code only connects to your specified servers — preventing session cross-contamination in multi-tenant scenarios.
3.5 MCP’s Position
MCP is suited for capability bridging (letting Claude Code use Hermes’s Telegram tools, or letting Hermes use Claude Code’s coding tools), but not for complete turn handoff — MCP operates at the tool-call level, not the session management level. For full turn handoff, use Route 1’s subprocess mode or Route 5’s ACP.
Route 4: A2A (Agent-to-Agent Protocol)
Principle: A2A is an open protocol announced by Google in April 2025, designed to let AI agents built on different frameworks (LangChain, CrewAI, AutoGen, etc.) discover each other and collaborate. It is now governed by the Linux Foundation, with 50+ tech companies backing it (Salesforce, SAP, Atlassian, Microsoft).
Think of it as HTTP for agents: just as HTTP lets browsers and servers from different vendors talk to each other, A2A lets agents from different vendors talk to each other.
4.1 Three-Layer Architecture
| Layer | Content |
|---|---|
| Data Model | Task, Message, AgentCard, Part, Artifact |
| Operations | Send Message, Send Streaming Message, Get/Cancel Task, Get Agent Card |
| Protocol Bindings | JSON-RPC 2.0 (primary), gRPC, HTTP/REST |
4.2 Core Mechanisms
Agent Card discovery: Each agent publishes a JSON metadata document at /.well-known/agent.json, describing its identity, capabilities, skills, supported I/O modalities, and authentication.
| |
JSON-RPC over HTTPS: All communication uses JSON-RPC 2.0. Send Streaming Message uses SSE for real-time streaming.
Task lifecycle: submitted → working → input-required → completed/canceled/failed. An agent delegates a task to another agent, then polls or streams for results.
| |
4.3 Claude Code Support Status
Not natively supported. Claude Code does not implement an A2A server or client. But three viable paths exist:
Anthropic has demonstrated it: A webinar (“Deploying Multi-Agent Systems using MCP and A2A with Claude on Vertex AI”) shows MCP (agent-to-tools) + A2A (agent-to-agent) + Claude combined.
A2A-MCP Bridge (community): GitHub’s
GongRzhe/A2A-MCP-Serveris an MCP server bridging A2A and MCP. Since Claude Code supports MCP, this bridge lets Claude Code participate in A2A workflows indirectly — Claude Code can call A2A-exposed agents as MCP tools, and vice versa.python-a2a library: PyPI’s
python-a2ais a complete A2A client/server library that can wrap Claude Code’s CLI as an A2A server.
4.4 How Hermes Could Use A2A to Control Claude Code
Approach A: Wrap Claude Code as an A2A server
| |
Hermes sends A2A tasks/send → server spawns Claude Code → returns result as task artifact. Standardized discovery, streaming (SSE), and task lifecycle all free.
Approach B: Use the A2A-MCP Bridge
Deploy the A2A-MCP Bridge as an MCP server that Claude Code connects to. Hermes acts as an A2A client, delegating tasks to the bridge, which translates them to MCP tool calls that Claude Code processes. Indirect but requires no custom server code.
4.5 A2A’s Position
A2A is suited for cross-vendor multi-agent orchestration — if Hermes needs to control not just Claude Code but also Gemini CLI, Copilot CLI, and other agents, A2A provides a unified interface. But for controlling just Claude Code alone, A2A’s protocol overhead isn’t worth it — direct terminal invocation is simpler.
References:
- A2A spec: https://github.com/a2aproject/A2A/blob/main/docs/specification.md
- Google blog: https://developers.googleblog.com/en/a2a-a-new-era-of-agent-interoperability
- A2A-MCP Bridge: https://github.com/GongRzhe/A2A-MCP-Server
- python-a2a: https://pypi.org/project/python-a2a/
Route 5: ACP (Agent Communication Protocol)
Principle: ACP (Agent Client Protocol / Agent Communication Protocol) is a standardized inter-agent communication protocol, using JSON-RPC over stdio to let one agent hand a conversation turn to another.
Hermes already has a complete implementation — this route’s biggest differentiator: it’s not theoretical, it’s code already running in Hermes:
agent/copilot_acp_client.py: spawnscopilot --acpsubprocess, ACP JSON-RPC, “forwards Hermes requests to copilot –acp”acp_adapter/: exposes Hermes itself as an ACP agent, letting editors like Zed connect viahermes acpagent/transports/codex_app_server.py: a similar subprocess JSON-RPC pattern (the codex version), serving as a sibling template for the ACP runtime
In other words, adding a Claude Code ACP runtime is essentially copying the copilot_acp_client.py pattern — replacing copilot --acp with claude (if Claude Code supports ACP), or with claude -p --input-format stream-json --output-format stream-json (using stream-json instead of ACP JSON-RPC).
5.0 Two ACPs
“ACP” has two meanings in the AI agent ecosystem, both relevant to Claude Code:
ACP #1: Agent Communication Protocol (IBM)
An open standard by IBM Research (2025), originally built for the BeeAI platform. REST-native design (not JSON-RPC), supporting multi-modal messages (text, files, structured data), async streaming, and Agent Identity (AID). Merged into A2A in late 2025 under the Linux Foundation — the official site agentcommunicationprotocol.dev states “ACP is now part of A2A.”
So if you hear “ACP protocol” in the context of agent interoperability, it’s basically part of A2A now.
ACP #2: Agent Client Protocol (Zed Industries / JetBrains)
This is the more practically relevant one. Released August 2025 by Zed Industries (co-developed with JetBrains and Google), it’s positioned as “the LSP for AI coding agents” — just as the Language Server Protocol standardized editor-to-language-server communication, ACP standardizes editor-to-AI-agent communication.
Architecture:
- JSON-RPC 2.0 over stdin/stdout (identical transport to LSP)
- Agent as server (subprocess), editor as client
- Lifecycle methods:
Initialize→LoadConfiguration→NewConversation/Prompt→ConversationUpdated(streaming notification) →CancelConversation→Shutdown - 25+ agents support ACP, including Google Gemini CLI, GitHub Copilot CLI, Cursor, and others
| |
Claude Code support status:
- Not natively supported — GitHub issue #6686 is a community feature request; Anthropic has not officially implemented the ACP server side
- But JetBrains integration works — JetBrains IDEs (IntelliJ IDEA, WebStorm, etc.) added ACP support in their AI Assistant plugin, and you can configure Claude Code CLI as a custom ACP agent. The IDE spawns
claudeas a subprocess and communicates via stdin/stdout JSON-RPC. This works because Claude Code’s CLI already supports stdin/stdout I/O, which is close enough to ACP’s expected behavior
5.1 Feasibility of Hermes as an ACP Client
Hermes could play a “virtual editor” — an ACP client:
- Spawn
claudeas a subprocess - Send JSON-RPC
initialize→loadConfiguration→newConversation→promptover stdin - Read streaming
conversationUpdatednotifications from stdout - Use
cancelConversationto interrupt long-running tasks - Use
readResource/editResourcefor file operations
This gives Hermes structured, protocol-level control of Claude Code with proper lifecycle management, cancellation, and streaming. However, Claude Code doesn’t officially implement the ACP server side — the JetBrains integration works because Claude Code’s stdin/stdout is close enough to ACP’s behavior.
Practical assessment: ACP is primarily designed for IDE-to-agent communication, not bot-to-agent. For Telegram bot scenarios, ACP’s stdin/stdout JSON-RPC pattern is architecturally very similar to direct terminal invocation’s stream-json — but stream-json is natively supported by Claude Code, while ACP requires implementing the client side. If you’re only doing bot integration, direct terminal invocation is simpler; if you’re building a general agent access layer (supporting multiple editors/agents), ACP is worth the investment.
References:
- ACP official: https://agentclientprotocol.com/get-started/introduction
- GitHub: https://github.com/agentclientprotocol/agent-client-protocol
- JetBrains ACP: https://www.jetbrains.com/acp/
- Claude Code ACP feature request: https://github.com/anthropics/claude-code/issues/6686
- IBM Agent Communication Protocol: https://www.ibm.com/think/topics/agent-communication-protocol
- ACP intro blog: https://www.calummurray.ca/blog/intro-to-acp
5.2 Side-by-Side: A2A vs ACP vs Direct Terminal Invocation
| Dimension | A2A | ACP (Agent Client) | Direct Terminal |
|---|---|---|---|
| Transport | JSON-RPC over HTTPS + SSE | JSON-RPC over stdin/stdout | Subprocess stdin/stdout |
| Maturity | Spec final, 50+ backers | Spec stable, 25+ agents | Production-ready, official docs |
| Claude Code native | ❌ (needs bridge/wrapper) | ❌ (works via JetBrains) | ✅ fully native |
| Hermes integration cost | Medium-High (build A2A server) | Medium (implement ACP client) | Low (subprocess + JSON parse) |
| Best for | Cross-vendor multi-agent | IDE integration, editor-agnostic | Bot/automation control of single CC |
| Streaming | SSE (HTTP) | stdin/stdout notifications | stream-json (real-time) |
| Session management | Task lifecycle | Conversation lifecycle | –resume/–continue |
Conclusion: Direct terminal invocation is the clear winner for immediate implementation — native, documented, no protocol implementation needed. A2A is the right choice for orchestrating multiple different agents. ACP is primarily an IDE concern and less relevant for a Telegram bot gateway, though its stdin/stdout JSON-RPC pattern is architecturally similar to what stream-json already provides.
5.3 Hermes’s ACP Implementation Pattern
| |
5.4 Hermes’s codex_app_server Template
The codex_app_server runtime is a more complete reference — it doesn’t just do ACP, it hands the entire conversation turn to the Codex CLI:
| |
Key files:
agent/transports/codex_app_server.py: spawn + JSON-RPC + streaming consumptioncodex_app_server_session.py: session managementcodex_event_projector.py: projects subprocess events to Hermes UI/transcriptagent/codex_runtime.py::run_codex_app_server_turn(): forwarder from run_agent.py
Path to add a Claude Code runtime: Copy these files, replace codex app-server with claude -p --input-format stream-json --output-format stream-json, replace JSON-RPC handshake with stream-json message protocol. OAuth credentials are already solved — anthropic_adapter.py::read_claude_code_credentials() can read Claude Code’s OAuth token.
Route 6: Hooks System (Bidirectional Hooks)
Principle: Claude Code’s Hooks system triggers custom scripts before/after tool execution and at session end. This isn’t “controlling” Claude Code — it’s Claude Code proactively notifying external systems.
| Dimension | Command-driven (terminal/SDK/MCP) | Event-driven (Hooks) |
|---|---|---|
| Direction | External → Claude Code | Claude Code → External |
| Trigger | External system | Claude Code itself |
| Use case | Drive Claude Code to do work | Monitor/intercept Claude Code’s behavior |
6.1 Three Hook Types
- PreToolUse: Triggers before tool execution. Can intercept — non-zero exit code blocks execution.
- PostToolUse: Triggers after tool execution. For notification.
- Stop: Triggers at session end. For cleanup.
| |
6.2 Hermes via Hooks: Telegram Real-time Notification
| |
| |
Hooks are not a standalone route — they’re a supplement. Best practice: SDK/subprocess drives work (command-driven) + Hooks monitors behavior (event-driven) + MCP bridges capabilities (protocol layer), combined.
Route 7: Cloud Sessions
Principle: Claude Code supports creating sessions in Anthropic’s cloud — not running locally.
| |
Significance: Hermes can create cloud sessions from a VPS — users chat in Telegram, computation happens in Anthropic’s cloud — the VPS doesn’t even need Claude Code installed.
| |
Advantages: No local install, no local code repo, natural multi-tenancy. Disadvantages: Depends on cloud availability, coarse control granularity, not suited for local filesystem tasks.
Side-by-Side Comparison
| Route | Complexity | Real-time | Bidirectional | Autonomy | Security | Hermes readiness |
|---|---|---|---|---|---|---|
| Direct terminal | Low | High (stream-json) | ✅ | High | Medium (needs perm mode) | ✅ codex_app_server template |
| SDK | Low | High | ✅ | High | High (canUseTool) | ⚠️ needs wrapping |
| MCP | Medium | Medium (req-resp) | ✅ | Medium | Medium | ✅ mcp_serve.py + mcp_tool.py |
| A2A | High | Medium | ✅ | High | Medium | ❌ not implemented |
| ACP | Medium | High | ✅ | High | Medium | ✅ copilot_acp_client.py |
| Hooks | Low | High (event-driven) | One-way (CC→ext) | N/A | High (can intercept) | ⚠️ needs config |
| Cloud | Lowest | Medium | One-way | High | Low | ❌ not implemented |
Practical Recommendation: Best Approach for Hermes + Claude Code
Based on Hermes’s actual architecture and existing infrastructure, a three-step approach:
Step 1: MCP Interconnection (Already exists, zero cost)
Hermes’s mcp_serve.py already exposes Telegram conversations as MCP tools. Claude Code can directly claude mcp add to connect to Hermes, proactively sending/receiving Telegram messages during coding. Zero development cost — already usable.
Meanwhile, Hermes’s tools/mcp_tool.py can already spawn claude mcp serve as a subprocess, letting Hermes proactively call Claude Code’s file operation tools. Also zero cost.
Use case: Lightweight collaboration — Claude Code needs to notify Telegram users, or Hermes needs to invoke Claude Code’s file operations.
Step 2: Subprocess Runtime (Copy codex_app_server template)
This is the core integration — letting Hermes hand entire conversation turns to Claude Code, just as it currently does with Codex.
Implementation path:
- Copy
agent/transports/codex_app_server.py→agent/transports/claude_code.py - Replace
codex app-servercommand withclaude -p --input-format stream-json --output-format stream-json - Replace JSON-RPC handshake with stream-json message protocol
- Copy
codex_app_server_session.py→claude_code_session.py - Copy
codex_event_projector.py→claude_event_projector.py(map codex events to Hermes UI) - Add
api_mode == "claude_code"branch inconversation_loop.py - Add
model.anthropic_runtime: "claude_code"switch in config.yaml - OAuth credentials already solved —
anthropic_adapter.py::read_claude_code_credentials()is reusable
Estimated effort: 2-3 days, because all infrastructure (subprocess management, event projection, session management, credential reading) already exists.
| |
Step 3: ACP Runtime (Optional, using existing acp template)
If Claude Code supports ACP in the future (not currently confirmed), copy the copilot_acp_client.py pattern to add an ACP-version Claude Code runtime. This would be more standardized than stream-json, but requires Claude Code to expose an ACP interface.
Permission Strategy
- Dev/testing:
--permission-mode dontAsk(deny what should be denied, safest) - Production:
--permission-prompts host+ SDKcanUseToolcallback (forward to Telegram inline keyboard for user approval) - Never use:
--dangerously-skip-permissionsin multi-tenant Telegram bot scenarios
Complete Architecture
| |
Conclusion
Claude Code was designed from the start with multiple layers of programmatic interfaces. From the lowest-level subprocess pipe, to the wrapped SDK, to the standardized MCP protocol, to the emerging A2A/ACP agent interoperability protocols, each route solves problems at a different level:
- Drive Claude Code to do work → SDK or direct terminal invocation (subprocess)
- Approve Claude Code’s operations → SDK’s
canUseToolcallback +--permission-prompts host - Expose capabilities to Claude Code → MCP Server (Hermes’s
mcp_serve.pyalready exists) - Let Claude Code use your tools → MCP Client +
claude mcp add(tools/mcp_tool.pyalready exists) - Monitor Claude Code’s behavior → Hooks
- Cross-agent collaboration → A2A / ACP (
copilot_acp_client.pyalready has the ACP template) - Don’t want to run locally → Cloud Sessions
For a Telegram bot gateway like Hermes, the good news is: most infrastructure already exists. The codex_app_server runtime provides a complete subprocess session-handoff template, copilot_acp provides the ACP protocol template, mcp_serve/mcp_tool provides MCP bidirectional communication, and anthropic_adapter provides OAuth credential reading. Adding a Claude Code runtime is essentially copying an existing pattern — it’s not building wheels from scratch, it’s plugging a new transport into a proven architecture.
Agent interoperability protocols (A2A, ACP) are the more future-oriented direction — when every AI tool exposes a standardized interface, “control” will no longer require subprocess pipes and SDK wrappers; it’ll be a protocol handshake. But until that day arrives, the stream-json pipe + canUseTool permission approval + existing runtime templates are the most pragmatic route.
