Featured image of post Hermes 控制Claude Code的技术路线全景:从终端调用到Agent互操作协议

Hermes 控制Claude Code的技术路线全景:从终端调用到Agent互操作协议

一个Telegram bot网关要操控Claude Code编程Agent,有多少条路可走?本文盘点7条技术路线——从最直接的终端子进程调用,到SDK编程化API,到MCP双向通信,再到A2A/ACP等Agent互操作协议——逐一拆解原理、给出代码示例、做横向对比,最后基于Hermes已有的codex_app_server和copilot_acp runtime模板给出实战推荐。

新发布

一句话概括

Hermes(一个 Telegram bot 网关)要控制 Claude Code(Anthropic 的 CLI 编程 Agent),有 7 条技术路线:直接终端调用、SDK 编程化 API、MCP 双向通信、A2A Agent-to-Agent 协议、ACP Agent 通信协议、Hooks 钩子系统、Cloud 云端会话。其中 Hermes 已经有了 codex_app_server 和 copilot_acp 两个子进程 runtime 模板——加一个 Claude Code runtime 本质上是复制已有模式。对 Telegram bot 场景,stream-json 流式管道 + canUseTool 外部权限审批 + 现有 codex_app_server 模板 是当前最务实的路线。

为什么需要从外部控制 Claude Code

Claude Code 是一个终端里的编程 Agent。你敲一行 claude,它进入交互式会话,读你的代码库、改文件、跑测试、提交 git——所有这些都在一个 TTY 里发生。

但很多时候你不想坐在终端前面:

  • 你想从 Telegram 发一条消息,让 Claude Code 去修一个 bug
  • 你想用一个 bot 网关(Hermes)把多个用户的消息路由到不同的 Claude Code 会话
  • 你想让一个 Agent 调用另一个 Agent(Agent-to-Agent 协作)

这些场景的共同点:控制端不在 TTY 里,需要一个程序化接口来驱动 Claude Code。

Hermes 是 NousResearch 的开源 Agent 框架——一个 Telegram bot 网关,用 Python 写的,通过 gateway/run.py 把 Telegram 消息路由到不同的 LLM。它已经能调用 OpenAI、Anthropic、DeepSeek 等多种模型,甚至已经有一个 codex_app_server runtime 把整个对话回合交给 Codex CLI 子进程处理。问题是:怎么把 Claude Code 也接进来?

Claude Code v2.1.267(本文撰写时的版本)提供了不止一条路。下面逐一拆解。


路线一:直接终端调用(Direct Terminal Invocation)

原理:Claude Code 本质是一个 CLI 二进制。任何能 spawn 子进程的语言都能直接调用它,通过 stdin/stdout 通信。这是最底层的路线,也是所有其他路线的基础——SDK 本质上也是在做这件事。

Hermes 已有先例:Hermes 的 codex_app_server runtime 就是这条路——spawn codex app-server 子进程,通过 JSON-RPC over stdio 驱动整个对话回合。文件 agent/transports/codex_app_server.py 完整实现了这个模式。加一个 Claude Code runtime,本质上是复制这个模板。

1.1 Headless 模式:-p / --print

1
2
3
4
5
6
7
8
# 最简单的调用:给一个 prompt,拿回文本
claude -p "解释这段代码的作用" --output-format text

# 拿结构化 JSON(单条结果)
claude -p "列出所有 TODO 注释" --output-format json

# 拿流式 JSON(实时逐条输出)
claude -p "重构这个函数" --output-format stream-json

--output-format 有三个选项(仅 --print 模式生效):

格式用途特点
text简单文本一次性返回,适合短任务
json结构化结果返回一个 JSON 对象,包含完整结果
stream-json实时流式每条消息(思考、工具调用、结果)逐条输出,适合长任务和实时监控

stream-json 是 Telegram bot 场景的关键——你不想等 5 分钟才拿到一个完整结果,而是想看到"Claude 正在读文件"、“Claude 正在改代码"这样的实时进度,然后逐条推回 Telegram。

1.2 流式输入:--input-format stream-json

1
2
# 双向流式:stdin 和 stdout 都是 stream-json
claude -p --input-format stream-json --output-format stream-json

这让外部系统通过 stdin 以 JSON 消息的形式逐条发送 prompt,而不是一次性传完。这意味着在一个 Claude Code 会话里可以持续对话——发一条消息,等回复,再发一条。

配合 --include-partial-messages,还能拿到逐 token 的部分消息——连 Claude 正在打字的过程都能实时看到。

1.3 会话管理

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
# 继续最近的对话(同目录下)
claude -c

# 恢复指定会话
claude --resume <session-id>

# 恢复时创建新会话 ID(不覆盖原会话)
claude --resume <session-id> --fork-session

# 从 PR 恢复会话
claude --from-pr 42

会话持久化是 Claude Code 的内置能力。每个会话有唯一 ID,可以被 --resume 恢复。这意味着 Hermes 可以为每个 Telegram 用户维护独立的 Claude Code 会话——用户 A 的对话不会泄露给用户 B。

1.4 后台会话管理

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
# 启动后台会话,立即返回 ID
claude --bg "帮我修 bug"

# 列出所有后台会话(JSON 格式,方便程序解析)
claude agents --json

# 查看某个会话的最近输出
claude logs f130ab53

# 附加到后台会话(进入交互式)
claude attach f130ab53

# 停止后台会话(对话保留,可再次 attach)
claude stop f130ab53

# 删除后台会话
claude rm f130ab53

--bg 是 Telegram bot 场景的杀手级功能。Hermes 可以:收到用户消息后 claude --bg 启动后台会话,拿到 session ID 存入数据库,用 claude logs 轮询或 stream-json 实时推送回 Telegram,用户离开后 stop 保留会话。

1.5 权限控制

这是 Telegram bot 场景最关键的问题:Claude Code 默认会弹权限确认。在无人值守的 bot 场景,没有人在终端前点"yes”。

Claude Code 提供 6 种权限模式:

模式行为适用场景
manual每次操作都问交互式,人在终端前
acceptEdits自动接受文件编辑,其他仍问半自动
auto智能判断哪些可自动执行半自动
plan只读,不执行任何修改代码审查、安全分析
bypassPermissions跳过所有权限检查全自动(高风险)
dontAsk不问,该问的一律拒绝最安全的自动模式
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
# 最安全的自动模式:该问的一律拒绝
claude -p "修这个 bug" --permission-mode dontAsk

# 外部系统审批权限(SDK host 或 permission-prompt-tool 回答)
claude -p "修 bug" --permission-prompts host

# 白名单指定工具
claude -p "跑测试" --allowedTools "Bash(npm test)" --permission-mode dontAsk

# 受限模式:移除所有能执行命令的工具
claude -p "分析代码" --restricted

--permission-prompts host 是桥梁——它允许外部系统(通过 SDK)回答权限问题。这正是 Telegram bot 场景需要的:Hermes 收到 Claude Code 的权限请求,转发给用户在 Telegram 里点"允许"或"拒绝"。

1.6 子进程调用示例(Python,Hermes 风格)

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
import subprocess, json

def claude_stream(prompt: str, session_id: str = None, cwd: str = "."):
    """启动 Claude Code 流式会话,逐条返回消息。
    本质上与 Hermes 的 codex_app_server runtime 同构。"""
    cmd = [
        "claude", "-p", prompt,
        "--output-format", "stream-json",
        "--input-format", "stream-json",
        "--permission-mode", "dontAsk",
        "--allowedTools", "Bash(git *) Edit Read",
    ]
    if session_id:
        cmd.extend(["--resume", session_id])

    proc = subprocess.Popen(
        cmd, stdin=subprocess.PIPE, stdout=subprocess.PIPE,
        stderr=subprocess.PIPE, cwd=cwd, text=True
    )

    for line in proc.stdout:
        msg = json.loads(line)
        msg_type = msg.get("type", "")
        if msg_type == "assistant":
            for block in msg.get("message", {}).get("content", []):
                if block.get("type") == "text":
                    yield {"type": "text", "content": block["text"]}
                elif block.get("type") == "tool_use":
                    yield {"type": "tool", "name": block["name"]}
        elif msg_type == "result":
            yield {"type": "done", "cost": msg.get("cost_usd")}
            break
    proc.wait()

# Hermes 调用
for event in claude_stream("修复 auth 模块的空指针异常"):
    if event["type"] == "text":
        send_to_telegram(user_id, event["content"])

路线二:Claude Code SDK(编程化 API)

原理:Anthropic 在 @anthropic-ai/claude-code npm 包里封装了 query() 函数,把子进程管理、消息解析、流式输出、权限审批全部封装成一层 API。SDK 和直接终端调用的底层一样——都是 spawn claude 子进程——但 SDK 封装了所有脏活。

2.1 TypeScript/JavaScript SDK

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
import { query, type Message } from "@anthropic-ai/claude-code";

const result = await query({
  prompt: "修复 auth 模块的 bug",
  options: {
    model: "claude-sonnet-5",
    permissionMode: "dontAsk",
    allowedTools: ["Bash(git *)", "Edit", "Read"],
    cwd: "/path/to/project",
  },
});

for await (const message of result) {
  switch (message.type) {
    case "assistant":
      console.log(message.message.content);
      break;
    case "result":
      console.log(`花费: $${message.cost_usd}`);
      break;
  }
}

2.2 Python SDK

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
from claude_code_sdk import query, ClaudeCodeOptions

async def run_claude(prompt: str):
    options = ClaudeCodeOptions(
        model="claude-sonnet-5",
        permission_mode="dontAsk",
        allowed_tools=["Bash(git *)", "Edit", "Read"],
    )
    async for message in query(prompt=prompt, options=options):
        if message.type == "assistant":
            print(message.content)

2.3 canUseTool 回调——外部权限审批

这是 SDK 相比裸子进程调用的最大优势:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
const result = await query({
  prompt: "重构数据库迁移脚本",
  options: {
    permissionMode: "dontAsk",
    canUseTool: async (toolName, input) => {
      // Hermes 把权限请求转发到 Telegram
      const approved = await ask_user_in_telegram(
        `Claude 想执行 ${toolName}:\n${JSON.stringify(input)}\n允许吗?`
      );
      return approved ? "allow" : "deny";
    },
  },
});

canUseTool 让 Hermes 变成一个权限审批代理:Claude Code 每次想执行一个工具,都会先问 Hermes,Hermes 再问 Telegram 用户。这比 bypassPermissions(全开)或 dontAsk(全拒)精细得多。

2.4 SDK 附带完整类型定义

Claude Code SDK 附带 sdk-tools.d.ts,覆盖所有内置工具的类型——AgentInput、BashInput、FileEditInput、McpInput、WorkflowInput、CronCreateInput 等共 30 余种。外部系统可以类型安全地构造和解析工具调用。

2.5 SDK vs 直接终端调用

维度直接终端调用SDK
进程管理手动 spawn/wait自动
消息解析手动 JSON.parse类型化对象
权限审批不支持 canUseTool✅ 回调式审批
错误处理手动检查 exit code内置重试和错误类型
语言任意TS/JS、Python

结论:如果 Hermes 的某个组件是 Node 或 Python,用 SDK。如果要嵌入 Hermes 的 Python gateway,可以参考 codex_app_server 的子进程模式自己封装——因为 Hermes 的 transport 层已经有一套 subprocess JSON-RPC 的成熟基础设施。


路线三:MCP(Model Context Protocol)

原理:MCP 是 Anthropic 定义的开放标准——让 AI 模型连接外部工具和数据源。Claude Code 既能做 MCP 客户端(连接别人的 server),也能做 MCP 服务端(把自己的能力暴露出去)。

Hermes 已有实践:Hermes 有 mcp_serve.py——一个 MCP server 把 Telegram 对话暴露为工具,让 Claude Code、Cursor、Codex 等任何 MCP client 都能读取消息历史、发送消息、管理审批。同时 tools/mcp_tool.py 已经能 spawn claude mcp serve 作为子进程。

3.1 Claude Code 作为 MCP Server

1
claude mcp serve

这会把 Claude Code 变成一个 MCP server,通过 stdio 暴露代码编辑、命令执行、文件读写等能力。任何 MCP client 都能连接它。

3.2 Claude Code 作为 MCP Client

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
# 添加 stdio MCP server
claude mcp add my-server -- python /path/to/server.py

# 添加 HTTP MCP server
claude mcp add --transport http hermes-api https://hermes.example.com/mcp

# 带认证
claude mcp add --transport http hermes-api https://hermes.example.com/mcp \
  --header "Authorization: Bearer <token>"

# 从 JSON 添加
claude mcp add-json hermes-server '{"type":"stdio","command":"python","args":["server.py"]}'

# 列出/查看/登录/删除
claude mcp list
claude mcp get hermes-api
claude mcp login hermes-api
claude mcp remove hermes-api

3.3 四种组合

MCP 是唯一一个双向对等的路线——Hermes 和 Claude Code 都可以做 server 或 client:

组合 A:Hermes 做 Server,Claude Code 做 Client Hermes 实现一个 MCP server,暴露 Telegram 工具(发消息、读消息)。Claude Code 用 claude mcp add 连接它,就能在代码里直接收发 Telegram 消息。这是 Hermes 已有的 mcp_serve.py 实现的方向。

组合 B:Claude Code 做 Server,Hermes 做 Client Claude Code 用 claude mcp serve 暴露编程能力。Hermes 作为 MCP client 连接它,像调用工具一样调用 Claude Code。这是 Hermes 的 tools/mcp_tool.py 已经支持的方向。

组合 C:互为 Server 两边都跑 MCP server,互连。适合复杂协作场景。

组合 D:都做 Client,共享第三方 Server 一个中间 MCP server 同时服务两边。较少见。

3.4 隔离控制

1
2
# 只使用指定 config 里的 server
claude -p "分析代码" --mcp-config hermes-only.json --strict-mcp-config

--strict-mcp-config 确保 Claude Code 只连接你指定的 server——多租户场景防止会话串扰。

3.5 MCP 的定位

MCP 适合能力桥接(让 Claude Code 用 Hermes 的 Telegram 工具,或让 Hermes 用 Claude Code 的编程工具),但不适合做完整的对话回合交接——MCP 是工具调用层面的协议,不是会话管理层面的。如果要交出整个对话回合,应该用路线一的子进程模式或路线五的 ACP。


路线四:A2A(Agent-to-Agent Protocol)

原理:A2A 是 Google 于 2025 年 4 月发布的开放协议,目标是让不同框架构建的 AI Agent(LangChain、CrewAI、AutoGen 等)互相发现并协作。现已移交 Linux Foundation 托管,50+ 科技公司背书(Salesforce、SAP、Atlassian、Microsoft)。

可以把它理解为 Agent 之间的 HTTP:就像 HTTP 让不同厂商的浏览器和服务器互相对话,A2A 让不同厂商的 Agent 互相对话。

4.1 三层架构

内容
数据模型层Task、Message、AgentCard、Part、Artifact
操作层Send Message、Send Streaming Message、Get/Cancel Task、Get Agent Card
协议绑定层JSON-RPC 2.0(主要)、gRPC、HTTP/REST

4.2 核心机制

Agent Card 发现:每个 Agent 在 /.well-known/agent.json 发布一个 JSON 元数据文档,描述自己的身份、能力、技能、支持的输入输出模态、认证方式。

1
2
3
4
5
6
7
8
{
  "name": "claude-code-agent",
  "description": "Anthropic's CLI coding agent",
  "url": "https://hermes.example.com/claude-code",
  "capabilities": { "streaming": true, "pushNotifications": true },
  "skills": [{ "id": "code-review", "name": "Code Review" }],
  "authentication": { "schemes": ["bearer"] }
}

JSON-RPC over HTTPS:所有通信走 JSON-RPC 2.0。Send Streaming Message 用 SSE(Server-Sent Events)做实时流式响应。

Task 生命周期submitted → working → input-required → completed/canceled/failed。Agent 把一个任务委托给另一个 Agent,然后轮询或流式获取结果。

1
2
3
4
5
6
7
8
{
  "jsonrpc": "2.0", "id": 1,
  "method": "tasks/send",
  "params": {
    "id": "task-123",
    "message": { "role": "user", "parts": [{"type":"text","text":"Review this code"}] }
  }
}

4.3 Claude Code 支持情况

不原生支持。 Claude Code 没有实现 A2A server 或 client。但有三条可行路径:

  1. Anthropic 自己做过演示:Anthropic 有一场网络研讨会(“Deploying Multi-Agent Systems using MCP and A2A with Claude on Vertex AI”),展示了 MCP(Agent-to-tools)+ A2A(Agent-to-Agent)+ Claude 的组合。

  2. A2A-MCP Bridge(社区方案):GitHub 上有 GongRzhe/A2A-MCP-Server,一个 MCP server 桥接 A2A 和 MCP。因为 Claude Code 支持 MCP,这个 bridge 让 Claude Code 间接参与 A2A 工作流——Claude Code 可以把 A2A 暴露的 Agent 当 MCP 工具调用,反之亦然。

  3. python-a2a 库:PyPI 上有 python-a2a,一个完整的 A2A client/server 库,可以用来把 Claude Code CLI 包装成 A2A server。

4.4 Hermes 如何通过 A2A 控制 Claude Code

方案 A:把 Claude Code 包装成 A2A server

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
# Hermes 侧:一个薄 HTTP server,实现 A2A 协议
from python_a2a import A2AServer, AgentCard, Skill
import subprocess, json

class ClaudeCodeA2A(A2AServer):
    @property
    def agent_card(self):
        return AgentCard(
            name="claude-code",
            description="Anthropic's CLI coding agent",
            skills=[Skill(id="coding", name="Code Generation & Editing")],
        )

    def handle_message(self, message):
        # 把 A2A 消息转成 Claude Code CLI 调用
        prompt = message.parts[0].text
        result = subprocess.run(
            ["claude", "-p", prompt, "--output-format", "json",
             "--permission-mode", "dontAsk"],
            capture_output=True, text=True
        )
        return json.loads(result.stdout)

Hermes 发送 A2A tasks/send 请求 → server spawn Claude Code → 返回结果作为 task artifact。标准化的发现、流式(SSE)、任务生命周期全部免费。

方案 B:用 A2A-MCP Bridge

部署 A2A-MCP Bridge 作为 MCP server,Claude Code 连接它。Hermes 作为 A2A client,把任务委托给 bridge,bridge 翻译成 MCP 工具调用让 Claude Code 处理。间接但无需自定义 server 代码。

4.5 A2A 的定位

A2A 适合跨厂商多 Agent 编排——如果 Hermes 不只要控制 Claude Code,还要控制 Gemini CLI、Copilot CLI、其他 Agent,A2A 是统一接口。但如果只控制 Claude Code 一个,A2A 的协议开销不划算——直接终端调用更简单。

参考资源

  • A2A 规范:https://github.com/a2aproject/A2A/blob/main/docs/specification.md
  • Google 博客: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/

路线五:ACP(Agent Communication Protocol)

原理:ACP(Agent Client Protocol / Agent Communication Protocol)是一个标准化的 Agent 间通信协议,通过 JSON-RPC over stdio 让一个 Agent 把对话回合交给另一个 Agent。

Hermes 已有完整实现——这是本路线最大的不同点:它不是理论,而是已经在 Hermes 里跑起来的代码:

  • agent/copilot_acp_client.py:spawn copilot --acp 子进程,ACP JSON-RPC,“把 Hermes 请求转发给 copilot –acp”
  • acp_adapter/:暴露 Hermes 自身为一个 ACP agent,让 Zed 等编辑器通过 hermes acp 连接
  • agent/transports/codex_app_server.py:类似的子进程 JSON-RPC 模式(codex 版本),作为 ACP runtime 的兄弟模板

也就是说,加一个 Claude Code ACP runtime,本质上是复制 copilot_acp_client.py 的模式——把 copilot --acp 换成 claude(如果 Claude Code 支持 ACP 协议),或者换成 claude -p --input-format stream-json --output-format stream-json(用 stream-json 代替 ACP JSON-RPC)。

5.0 两个 ACP

“ACP” 在 AI Agent 生态里有两个含义,都与 Claude Code 相关:

ACP 之一:Agent Communication Protocol(IBM)

IBM Research 2025 年发布的开放标准,最初为 BeeAI 平台构建。REST-native 设计(不是 JSON-RPC),支持多模态消息(文本、文件、结构化数据)、异步流式、Agent 身份(AID)。已于 2025 年底合并入 A2A(Linux Foundation 下)——官方站点 agentcommunicationprotocol.dev 明确写着"ACP is now part of A2A"。

所以如果你听到"ACP 协议",在 Agent 互操作的语境下,它基本上就是 A2A 的一部分了。

ACP 之二:Agent Client Protocol(Zed Industries / JetBrains)

这才是更实际相关的那个。2025 年 8 月由 Zed Industries 发布(与 JetBrains 和 Google 联合开发),定位是 “AI 编程 Agent 的 LSP”——就像 Language Server Protocol 标准化了编辑器到语言服务器的通信,ACP 标准化了编辑器到 AI 编程 Agent 的通信。

架构

  • JSON-RPC 2.0 over stdin/stdout(和 LSP 完全一致的传输方式)
  • Agent 作为 server(子进程),编辑器作为 client
  • 生命周期方法:InitializeLoadConfigurationNewConversation / PromptConversationUpdated(流式通知)→ CancelConversationShutdown
  • 25+ Agent 已支持,包括 Google Gemini CLI、GitHub Copilot CLI、Cursor 等
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
// ACP 握手
{
  "jsonrpc": "2.0", "id": 1,
  "method": "initialize",
  "params": { "clientInfo": { "name": "hermes", "version": "1.0" } }
}

// 发起对话
{
  "jsonrpc": "2.0", "id": 2,
  "method": "newConversation",
  "params": { "prompt": "修复 auth 模块的 bug" }
}

// Agent 流式推送进度
{ "method": "conversationUpdated", "params": { "content": "正在读取 auth.py..." } }

Claude Code 支持情况

  • 不原生支持——GitHub issue #6686 是社区功能请求,Anthropic 尚未官方实现 ACP server
  • 但 JetBrains 集成可用——JetBrains IDE(IntelliJ IDEA、WebStorm 等)在 AI Assistant 插件中加了 ACP 支持,可以配置 Claude Code CLI 作为自定义 ACP agent。IDE spawn claude 子进程,通过 stdin/stdout JSON-RPC 通信。这之所以能工作,是因为 Claude Code 的 CLI 已经支持 stdin/stdout I/O,与 ACP 的预期行为足够接近

5.3 Hermes 作为 ACP client 的可行性

Hermes 可以扮演一个"虚拟编辑器"——ACP client:

  1. Spawn claude 作为子进程
  2. 发送 JSON-RPC initializeloadConfigurationnewConversationprompt
  3. 读取流式 conversationUpdated 通知
  4. cancelConversation 中断长任务
  5. readResource / editResource 做文件操作

这给了 Hermes 一个结构化的、协议级别的 Claude Code 控制能力,带完整的生命周期管理、取消和流式。但 Claude Code 不官方实现 ACP server 端——JetBrains 集成能工作是因为 Claude Code 的 stdin/stdout 足够接近 ACP 的行为。

实际评估:ACP 主要为 IDE-to-agent 通信设计,不是 bot-to-agent。对 Telegram bot 场景,ACP 的 stdin/stdout JSON-RPC 模式和直接终端调用的 stream-json 架构上非常相似——但 stream-json 是 Claude Code 原生支持的,ACP 需要额外实现 client 端。如果只做 bot 集成,直接终端调用更简单;如果要做通用 Agent 接入层(支持多种编辑器/Agent),ACP 值得投入。

参考资源

  • ACP 官方站:https://agentclientprotocol.com/get-started/introduction
  • GitHub:https://github.com/agentclientprotocol/agent-client-protocol
  • JetBrains ACP:https://www.jetbrains.com/acp/
  • Claude Code ACP 功能请求:https://github.com/anthropics/claude-code/issues/6686
  • IBM Agent Communication Protocol:https://www.ibm.com/think/topics/agent-communication-protocol
  • ACP 入门博客:https://www.calummurray.ca/blog/intro-to-acp

5.1 Hermes 的 ACP 实现模式

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
# agent/copilot_acp_client.py 的模式(简化)
import subprocess, json

class ACPRuntime:
    """ACP runtime: spawn CLI subprocess, JSON-RPC over stdio"""
    def __init__(self, command: list[str]):
        self.proc = subprocess.Popen(
            command, stdin=subprocess.PIPE, stdout=subprocess.PIPE,
            stderr=subprocess.PIPE, text=True
        )

    def initialize(self):
        """ACP handshake"""
        return self._rpc("initialize", {"protocolVersion": "1.0"})

    def start_turn(self, messages: list, tools: list):
        """把对话回合交给子进程 Agent"""
        return self._rpc("turn/start", {"messages": messages, "tools": tools})

    def consume_stream(self):
        """消费流式事件直到 turn/completed"""
        for line in self.proc.stdout:
            event = json.loads(line)
            yield event
            if event.get("method") == "turn/completed":
                break

# Claude Code 版本(如果 CC 支持 ACP):
# runtime = ACPRuntime(["claude", "--acp"])
# 或用 stream-json 替代:
# runtime = ACPRuntime(["claude", "-p", "--input-format", "stream-json",
#                       "--output-format", "stream-json"])

5.2 Hermes 的 codex_app_server 模板

codex_app_server runtime 是更完整的参考——它不只做 ACP,而是把整个对话回合交给 Codex CLI:

1
2
3
4
5
6
gateway 收到 Telegram 消息
  → conversation_loop.py 检测 api_mode == codex_app_server
  → 不走默认 Hermes 路径,而是 spawn codex app-server
  → initialize 握手 → thread/start → turn/start
  → 消费流式 item/* 通知直到 turn/completed
  → 把事件投影到 Hermes 显示和转录

关键文件:

  • agent/transports/codex_app_server.py:spawn + JSON-RPC + 流式消费
  • codex_app_server_session.py:会话管理
  • codex_event_projector.py:把子进程事件投影到 Hermes 的 UI/转录
  • agent/codex_runtime.py::run_codex_app_server_turn():从 run_agent.py 转调

加 Claude Code runtime 的路径:复制这套文件,把 codex app-server 换成 claude -p --input-format stream-json --output-format stream-json,把 JSON-RPC 握手换成 stream-json 消息协议。OAuth 凭证已经解决——anthropic_adapter.py::read_claude_code_credentials() 已经能读取 Claude Code 的 OAuth token。

5.4 横向看:A2A vs ACP vs 直接终端调用

维度A2AACP (Agent Client)直接终端调用
传输方式JSON-RPC over HTTPS + SSEJSON-RPC over stdin/stdout子进程 stdin/stdout
成熟度规范定稿,50+ 背书规范稳定,25+ agent生产就绪,官方文档
Claude Code 原生支持❌(需 bridge/wrapper)❌(JetBrains 可用)✅ 完全原生
Hermes 集成成本中高(建 A2A server)中(实现 ACP client)低(子进程 + JSON 解析)
最适合跨厂商多 Agent 编排IDE 集成、编辑器通用接入Bot/自动化控制单个 Claude Code
流式SSE(HTTP)stdin/stdout 通知stream-json(实时)
会话管理Task 生命周期Conversation 生命周期–resume/–continue

结论:直接终端调用是即刻可用的赢家——原生、文档齐全、无需协议实现。A2A 适合编排多个不同 Agent。ACP 主要是 IDE 关注点,对 Telegram bot 不如 stream-json 直接。


路线六:Hooks 系统(双向钩子)

原理:Claude Code 的 Hooks 系统在工具执行的前后、会话结束时触发自定义脚本。这不是"控制"Claude Code,而是 Claude Code 主动通知外部系统

维度命令驱动(终端/SDK/MCP)事件驱动(Hooks)
方向外部 → Claude CodeClaude Code → 外部
触发方外部系统Claude Code 自己
用途驱动 Claude Code 做事监控/拦截 Claude Code 的行为

6.1 三种 Hook 类型

  • PreToolUse:工具执行前触发。可以拦截——返回非零退出码阻止执行。
  • PostToolUse:工具执行后触发。用于通知。
  • Stop:会话结束时触发。用于收尾。
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
{
  "hooks": {
    "PreToolUse": [{
      "matcher": "Bash",
      "hooks": [{"type": "command", "command": "python /opt/hermes/hooks/pre_tool.py"}]
    }],
    "PostToolUse": [{
      "matcher": ".*",
      "hooks": [{"type": "command", "command": "python /opt/hermes/hooks/post_tool.py"}]
    }],
    "Stop": [{
      "hooks": [{"type": "command", "command": "python /opt/hermes/hooks/on_stop.py"}]
    }]
  }
}

6.2 Hermes 通过 Hooks 实现 Telegram 实时通知

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
# /opt/hermes/hooks/post_tool.py — 推送工具执行通知回 Telegram
import sys, json, requests

event = json.loads(sys.stdin.read())
tool = event["tool_name"]
inp = event.get("tool_input", {})

msg = f"🔧 Claude Code 执行了 {tool}"
if tool == "Edit": msg += f"\n文件: {inp.get('file_path')}"
elif tool == "Bash": msg += f"\n命令: {inp.get('command', '')[:100]}"

requests.post("https://api.telegram.org/bot<TOKEN>/sendMessage",
              json={"chat_id": CHAT_ID, "text": msg})
1
2
3
4
5
6
7
8
9
# /opt/hermes/hooks/pre_tool.py — 安全拦截
import sys, json

event = json.loads(sys.stdin.read())
cmd = event.get("tool_input", {}).get("command", "")
dangerous = ["rm -rf /", "DROP TABLE", "git push --force"]
if any(d in cmd for d in dangerous):
    print(json.dumps({"decision": "block", "reason": f"危险命令被拦截: {cmd}"}))
    sys.exit(2)  # 非零退出码 = 阻止执行

Hooks 不是独立路线——它是补充。最佳实践:SDK/子进程驱动做事(命令驱动)+ Hooks 监控行为(事件驱动)+ MCP 暴露能力(协议层),三者组合。


路线七:Cloud Sessions(云端会话)

原理:Claude Code 支持在 Anthropic 云端创建会话——不在本地跑。

1
2
3
4
5
6
7
8
9
# 创建云端会话
claude --cloud "帮我重构这个项目的认证模块"

# 通过 session ID 或 claude.ai/code URL 附加
claude --cloud <session-id>
claude --cloud https://claude.ai/code/sessions/abc123

# 在自托管环境上创建
claude --environment ccpool_xxx "分析代码安全性"

意义:Hermes 可以在 VPS 上创建云端会话,用户在 Telegram 里对话,实际计算在 Anthropic 云端——VPS 甚至不需要装 Claude Code。

1
Telegram 用户 → Hermes (VPS) → claude --cloud → Cloud Session (Anthropic 云端) → GitHub 仓库

优势:无需本地安装、无需本地代码仓库、天然多租户。 劣势:依赖云服务可用性、控制粒度粗、不适合需要本地文件系统的任务。


横向对比

路线复杂度实时性双向性自主性安全性Hermes 现成度
直接终端调用高(stream-json)✅ 双向中(需配权限模式)✅ 有 codex_app_server 模板
SDK✅ 双向高(canUseTool)⚠️ 需封装
MCP中(请求-响应)✅ 双向✅ mcp_serve.py + mcp_tool.py 已有
A2A✅ 双向❌ 未实现
ACP✅ 双向✅ copilot_acp_client.py 已有
Hooks高(事件驱动)单向(CC→外部)N/A高(可拦截)⚠️ 需配置
Cloud最低单向❌ 未实现

实战推荐:Hermes 集成 Claude Code 的最佳方案

基于 Hermes 的实际架构和已有基础设施,推荐分三步走:

第一步:MCP 互连(已有,0 成本)

Hermes 的 mcp_serve.py 已经把 Telegram 对话暴露为 MCP 工具。Claude Code 可以直接 claude mcp add 连接 Hermes,在编程过程中主动收发 Telegram 消息。这一步零开发成本——已经可用。

同时,Hermes 的 tools/mcp_tool.py 已经能 spawn claude mcp serve 作为子进程,让 Hermes 主动调用 Claude Code 的编程工具。也是零成本。

适用场景:轻量协作——Claude Code 需要通知 Telegram 用户,或 Hermes 需要调用 Claude Code 的文件操作能力。

第二步:子进程 runtime(复制 codex_app_server 模板)

这是核心集成——让 Hermes 能把整个对话回合交给 Claude Code,就像它现在交给 Codex 一样。

实现路径:

  1. 复制 agent/transports/codex_app_server.pyagent/transports/claude_code.py
  2. codex app-server 命令换成 claude -p --input-format stream-json --output-format stream-json
  3. 把 JSON-RPC 握手换成 stream-json 消息协议
  4. 复制 codex_app_server_session.pyclaude_code_session.py
  5. 复制 codex_event_projector.pyclaude_event_projector.py(把 codex 事件映射到 Hermes UI)
  6. conversation_loop.py 加一个 api_mode == "claude_code" 分支
  7. 在 config.yaml 加 model.anthropic_runtime: "claude_code" 开关
  8. OAuth 凭证已解决——anthropic_adapter.py::read_claude_code_credentials() 可复用

预估工作量:2-3 天,因为基础设施(子进程管理、事件投影、会话管理、凭证读取)全部已有。

1
2
3
4
5
6
# config.yaml 新增
model:
  default: claude-sonnet-5
  provider: anthropic
  anthropic_runtime: "claude_code"  # 新增:交出对话回合
  api_mode: claude_code_stream      # 新增

第三步:ACP runtime(可选,用已有 acp 模板)

如果 Claude Code 未来支持 ACP 协议(目前未确认),可以复制 copilot_acp_client.py 的模式,加一个 ACP 版本的 Claude Code runtime。这会比 stream-json 更标准化,但前提是 Claude Code 要暴露 ACP 接口。

权限策略

  • 开发/测试阶段--permission-mode dontAsk(该拒的拒,最安全)
  • 生产阶段--permission-prompts host + SDK canUseTool 回调(转发到 Telegram inline keyboard 让用户审批)
  • 永不使用--dangerously-skip-permissions 在多租户 Telegram bot 场景

完整架构

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
Telegram 用户
    ↓ 消息
Hermes gateway (Python, /opt/hermes)
    ↓ api_mode == claude_code
    ↓ spawn: claude -p --input-format stream-json --output-format stream-json
Claude Code 子进程 (stream-json 双向管道)
    ↓ --permission-prompts host → canUseTool 回调
    ↓ Hermes 转发权限请求到 Telegram inline keyboard
    ↓ 用户点"允许" → canUseTool 返回 "allow"
Claude Code 执行工具 (Edit/Bash/Read...)
    ↓ PostToolUse hook → 推送通知回 Telegram
    ↓ stream-json 事件 → codex_event_projector 投影到 Hermes 转录
    ↓ turn/completed → 回复推回 Telegram

结论

Claude Code 从设计之初就预留了多层编程化接口。从最底层的子进程管道,到封装好的 SDK,到标准化的 MCP 协议,再到新兴的 A2A/ACP Agent 互操作协议,每条路线解决不同层面的问题:

  • 要驱动 Claude Code 做事 → SDK 或直接终端调用(子进程)
  • 要审批 Claude Code 的操作 → SDK 的 canUseTool 回调 + --permission-prompts host
  • 要暴露能力给 Claude Code → MCP Server(Hermes 的 mcp_serve.py 已有)
  • 要让 Claude Code 用你的工具 → MCP Client + claude mcp addtools/mcp_tool.py 已有)
  • 要监控 Claude Code 的行为 → Hooks
  • 要跨 Agent 协作 → A2A / ACP(copilot_acp_client.py 已有 ACP 模板)
  • 不想在本地跑 → Cloud Sessions

对于 Hermes 这样的 Telegram bot 网关,好消息是:大部分基础设施已经存在。codex_app_server runtime 提供了完整的子进程会话交接模板,copilot_acp 提供了 ACP 协议模板,mcp_serve/mcp_tool 提供了 MCP 双向通信,anthropic_adapter 提供了 OAuth 凭证读取。加一个 Claude Code runtime 本质上是复制已有模式——这不是从零开始造轮子,而是在一个已经验证过的架构上插一个新的 transport。

Agent 互操作协议(A2A、ACP)是更未来的方向——当每个 AI 工具都暴露标准化接口时,“控制"就不再需要子进程管道和 SDK 包装,而是一个协议握手的事。但在那一天到来之前,stream-json 管道 + canUseTool 权限审批 + 现有 runtime 模板,就是最务实的路线。