Skip to content
阅读进度0%
MCP待复核

从 LSP 到 MCP、ACP:AI Agent 时代的协议体系设计详解

CSDN 原文全文镜像:AI Agent 时代,协议会变得越来越重要。AI ClientIDEAgentToolResourcePrompt这些组件之间如果没有统一协议,就会变成大量脆弱的胶水代码。JSON-RPC:消息格式底座stdio / HTTP / S……

CSDN 原文镜像

本文为作者 CSDN 博客的全文镜像,原文发布于 2026-05-20。为适配本站结构,仅补充了站内元数据与来源说明,正文主体保持原文内容。

在这里插入图片描述

前言

过去我们做软件系统集成,常见的是 REST API、RPC、WebSocket、消息队列这些技术。但进入 AI Agent 时代之后,我们会发现新的问题出现了:

不是系统之间简单地调用接口,而是 AI 客户端、IDE、Agent、外部工具、聊天平台之间需要协同工作。

例如:

  • Claude Desktop 想调用你本地写的“查公司内部 Wiki”的工具。
  • Cursor / VS Code 想让一个 coding agent 接管项目重构任务。
  • 一个企业 AI 助手想同时接入微信、Slack、Discord、Telegram。
  • Agent 在 IDE 中修改文件时,需要向客户端请求权限、返回进度、支持取消任务。
  • AI 模型想知道有哪些工具可以调用、工具参数是什么、返回结果是什么。

这些场景背后,本质上都需要一套“通信协议”。

这篇文章围绕几个关键协议和架构模式展开:

  1. JSON-RPC 2.0:底层消息格式。
  2. stdio Transport:本地进程间通信方式。
  3. LSP:IDE 与语言服务器之间的经典协议。
  4. MCP:AI 客户端发现和调用外部工具的协议。
  5. ACP:IDE 与 AI coding agent 之间的协议。
  6. Gateway Plugin:把 Agent 接入微信、Slack、Discord 等聊天平台的插件架构。

如果用一句话概括它们的关系:

JSON-RPC 是消息格式,stdio/HTTP/SSE 是传输通道,LSP 是 IDE 时代的语言能力协议,MCP 是 AI 调用外部工具的协议,ACP 是 IDE 驱动 Agent 的协议,Gateway Plugin 是 Agent 接入多聊天平台的适配层。


一、先理解 JSON-RPC:这些协议的共同底座

无论是 LSP、MCP 还是 ACP,它们都大量借鉴或直接使用了 JSON-RPC 2.0

JSON-RPC 是一种非常轻量的远程调用协议。它不像 REST 那样围绕 URL 和 HTTP Method 设计,而是围绕“方法名 + 参数 + 请求 ID”设计。

JSON-RPC 2.0 官方规范中定义了 Request、Response、Notification、Error 等核心结构;其中 Notification 是没有 id 的请求,服务端不需要返回响应。(jsonrpc.org)

1.1 JSON-RPC 请求格式

一个最简单的 JSON-RPC 请求长这样:

json
<span class="token punctuation">{<!-- --></span>
<span class="token string-property property">"jsonrpc"</span><span class="token operator">:</span> <span class="token string">"2.0"</span><span class="token punctuation">,</span>
<span class="token string-property property">"id"</span><span class="token operator">:</span> <span class="token number">1</span><span class="token punctuation">,</span>
<span class="token string-property property">"method"</span><span class="token operator">:</span> <span class="token string">"tools/list"</span><span class="token punctuation">,</span>
<span class="token string-property property">"params"</span><span class="token operator">:</span> <span class="token punctuation">{<!-- --></span><span class="token punctuation">}</span>
<span class="token punctuation">}</span>

字段含义:

字段含义
jsonrpc协议版本,固定为 "2.0"
id请求 ID,用来匹配响应
method要调用的方法名
params方法参数

服务端响应:

json
<span class="token punctuation">{<!-- --></span>
<span class="token string-property property">"jsonrpc"</span><span class="token operator">:</span> <span class="token string">"2.0"</span><span class="token punctuation">,</span>
<span class="token string-property property">"id"</span><span class="token operator">:</span> <span class="token number">1</span><span class="token punctuation">,</span>
<span class="token string-property property">"result"</span><span class="token operator">:</span> <span class="token punctuation">{<!-- --></span>
<span class="token string-property property">"tools"</span><span class="token operator">:</span> <span class="token punctuation">[</span><span class="token punctuation">]</span>
<span class="token punctuation">}</span>
<span class="token punctuation">}</span>

如果出错,则返回:

json
<span class="token punctuation">{<!-- --></span>
<span class="token string-property property">"jsonrpc"</span><span class="token operator">:</span> <span class="token string">"2.0"</span><span class="token punctuation">,</span>
<span class="token string-property property">"id"</span><span class="token operator">:</span> <span class="token number">1</span><span class="token punctuation">,</span>
<span class="token string-property property">"error"</span><span class="token operator">:</span> <span class="token punctuation">{<!-- --></span>
<span class="token string-property property">"code"</span><span class="token operator">:</span> <span class="token operator">-</span><span class="token number">32601</span><span class="token punctuation">,</span>
<span class="token string-property property">"message"</span><span class="token operator">:</span> <span class="token string">"Method not found"</span>
<span class="token punctuation">}</span>
<span class="token punctuation">}</span>

1.2 Notification:不需要响应的消息

JSON-RPC 中还有一种特殊消息,叫 Notification。

它没有 id

json
<span class="token punctuation">{<!-- --></span>
<span class="token string-property property">"jsonrpc"</span><span class="token operator">:</span> <span class="token string">"2.0"</span><span class="token punctuation">,</span>
<span class="token string-property property">"method"</span><span class="token operator">:</span> <span class="token string">"notifications/initialized"</span><span class="token punctuation">,</span>
<span class="token string-property property">"params"</span><span class="token operator">:</span> <span class="token punctuation">{<!-- --></span><span class="token punctuation">}</span>
<span class="token punctuation">}</span>

因为没有 id,服务端就不需要返回响应。

这类消息很适合用于:

  • 初始化完成通知
  • 进度更新
  • 日志推送
  • 状态变更
  • 文件变化通知

JSON-RPC 2.0 规范明确说明,没有 id 的 Request 就是 Notification,服务端不能回复 Notification。(jsonrpc.org)

1.3 为什么这些协议喜欢用 JSON-RPC?

因为它非常适合本地工具、IDE、Agent 之间通信。

它的优点是:

  1. 简单:一个 JSON 对象就能表达一次调用。
  2. 语言无关:Python、TypeScript、Go、Rust、Java 都能轻松实现。
  3. 传输无关:可以跑在 stdio、WebSocket、HTTP、TCP 上。
  4. 天然支持双向通信:客户端可以请求服务端,服务端也可以发送通知。
  5. 适合长生命周期进程:例如 IDE 启动一个 language server,持续通信。

这也是为什么 LSP、MCP、ACP 都和 JSON-RPC 有很深的关系。


二、再理解 stdio:为什么这些协议喜欢用标准输入输出?

很多人第一次看到 MCP 或 ACP 的时候,会疑惑:

为什么不用 HTTP,而是用 stdio?

stdio 指的是:

  • stdin:标准输入
  • stdout:标准输出
  • stderr:标准错误

也就是说,客户端可以启动一个子进程,然后通过这个子进程的输入输出流通信。

例如:

text
Claude Desktop / Cursor / IDE
|
| 启动子进程
v
python mcp_server.py
|
| stdin / stdout
v
JSON-RPC 消息

2.1 stdio 的优势

1. 本地部署简单

不需要开放端口。

例如你写一个 MCP Server:

bash
python wiki_mcp_server.py

AI 客户端直接启动这个进程即可。

不用考虑:

  • 端口冲突
  • 防火墙
  • HTTPS 证书
  • 反向代理
  • 外网访问

2. 安全边界更清晰

stdio 通信通常发生在本机父子进程之间。

相比暴露 HTTP 服务,它的攻击面更小。

3. 非常适合 IDE / 桌面客户端

LSP 早就大量采用类似模式:

text
VS Code 启动 pyright / rust-analyzer / gopls
然后通过 JSON-RPC 通信

ACP 也借鉴了类似思想。ACP 官方相关文档说明,它是基于 JSON-RPC 的协议,客户端通常会以子进程方式启动 agent,并通过 stdio 通信,但协议本身也可以适配其他双向流。(docs.rs)

2.2 stdio 的缺点

stdio 也不是万能的。

它不适合:

  • 多客户端共享同一个服务
  • 跨机器访问
  • 云端部署
  • 需要负载均衡的服务
  • 需要浏览器直接访问的服务

这时就更适合 HTTP、SSE、WebSocket 或 Streamable HTTP。

MCP 当前规范中也明确提到,MCP 使用 JSON-RPC 编码消息,并定义了 stdio 和 Streamable HTTP 两类标准传输机制。(模型上下文协议)


三、LSP:IDE 协议设计的经典范式

在讲 MCP 和 ACP 之前,必须先讲 LSP。

LSP 全称是 Language Server Protocol,即语言服务器协议。

它解决的问题是:

不同编辑器都需要代码补全、跳转定义、悬浮文档、诊断报错,但如果每个语言都给每个编辑器单独适配一遍,成本会爆炸。

以前的情况是:

text
Python 插件需要适配 VS Code
Python 插件需要适配 Vim
Python 插件需要适配 Emacs
Python 插件需要适配 JetBrains

Go 插件也要适配 VS Code
Go 插件也要适配 Vim
Go 插件也要适配 Emacs
……

这就是 N × M 的集成问题。

LSP 的思路是:

编辑器只实现 LSP Client,语言能力只实现 LSP Server,中间用统一协议通信。

text
VS Code / Vim / Emacs / Cursor
|
| LSP
v
Python Language Server / Go Language Server / Rust Analyzer

LSP 官方规范描述的是编辑器和语言服务器之间的通信协议,当前稳定规范是 3.17。(GitHub Microsoft)

3.1 LSP 的核心流程

典型 LSP 流程:

text
1. initialize
2. initialized
3. textDocument/didOpen
4. textDocument/didChange
5. textDocument/completion
6. textDocument/hover
7. textDocument/definition
8. textDocument/publishDiagnostics

例如编辑器打开一个 Python 文件:

json
<span class="token punctuation">{<!-- --></span>
<span class="token string-property property">"jsonrpc"</span><span class="token operator">:</span> <span class="token string">"2.0"</span><span class="token punctuation">,</span>
<span class="token string-property property">"id"</span><span class="token operator">:</span> <span class="token number">1</span><span class="token punctuation">,</span>
<span class="token string-property property">"method"</span><span class="token operator">:</span> <span class="token string">"initialize"</span><span class="token punctuation">,</span>
<span class="token string-property property">"params"</span><span class="token operator">:</span> <span class="token punctuation">{<!-- --></span>
<span class="token string-property property">"processId"</span><span class="token operator">:</span> <span class="token number">12345</span><span class="token punctuation">,</span>
<span class="token string-property property">"rootUri"</span><span class="token operator">:</span> <span class="token string">"file:///project"</span><span class="token punctuation">,</span>
<span class="token string-property property">"capabilities"</span><span class="token operator">:</span> <span class="token punctuation">{<!-- --></span><span class="token punctuation">}</span>
<span class="token punctuation">}</span>
<span class="token punctuation">}</span>

语言服务器返回自己支持的能力:

json
<span class="token punctuation">{<!-- --></span>
<span class="token string-property property">"jsonrpc"</span><span class="token operator">:</span> <span class="token string">"2.0"</span><span class="token punctuation">,</span>
<span class="token string-property property">"id"</span><span class="token operator">:</span> <span class="token number">1</span><span class="token punctuation">,</span>
<span class="token string-property property">"result"</span><span class="token operator">:</span> <span class="token punctuation">{<!-- --></span>
<span class="token string-property property">"capabilities"</span><span class="token operator">:</span> <span class="token punctuation">{<!-- --></span>
<span class="token string-property property">"hoverProvider"</span><span class="token operator">:</span> <span class="token boolean">true</span><span class="token punctuation">,</span>
<span class="token string-property property">"definitionProvider"</span><span class="token operator">:</span> <span class="token boolean">true</span><span class="token punctuation">,</span>
<span class="token string-property property">"completionProvider"</span><span class="token operator">:</span> <span class="token punctuation">{<!-- --></span>
<span class="token string-property property">"resolveProvider"</span><span class="token operator">:</span> <span class="token boolean">true</span>
<span class="token punctuation">}</span>
<span class="token punctuation">}</span>
<span class="token punctuation">}</span>
<span class="token punctuation">}</span>

之后编辑器就知道:

text
这个语言服务器支持 hover、definition、completion。

3.2 LSP 的价值

LSP 最核心的价值是解耦:

text
编辑器不需要懂 Python / Go / Rust 的语义
语言服务器不需要关心 VS Code / Vim / Emacs 的 UI

编辑器只负责展示:

  • 补全列表
  • 错误波浪线
  • 跳转结果
  • 悬浮文档

语言服务器只负责计算:

  • 代码语义
  • 类型信息
  • 诊断错误
  • 定义位置
  • 引用位置

这就是协议的力量。

LSP 通过统一协议把“编辑器”和“语言智能”解耦,减少了不同编辑器重复实现语言能力的成本。相关介绍也强调,LSP 让语言相关能力可以实现一次,并在多个支持 LSP 的编辑器中复用。(nabeelvalley.co.za)

3.3 LSP 对 AI Agent 协议的启发

LSP 的成功给 MCP / ACP 很大启发。

因为 AI Agent 时代也遇到了类似问题:

text
Claude Desktop 想接各种工具
Cursor 想接各种 agent
VS Code 想接各种 coding agent
聊天平台想接各种 AI 后端

如果每个客户端都和每个工具、每个 agent 单独适配,就会再次变成 N × M 的集成灾难。

所以我们需要新的标准协议:

text
MCP:统一 AI 调用工具的方式
ACP:统一 IDE 调用 Agent 的方式
Gateway Plugin:统一聊天平台接入 Agent 的方式

四、MCP:让 AI 客户端发现并调用外部工具

MCP 全称是 Model Context Protocol

它解决的问题是:

AI 客户端如何标准化地发现外部工具、读取外部资源、调用外部能力?

MCP 官方文档中将 tools 描述为允许模型与外部系统交互的机制,例如查询数据库、调用 API 或执行计算;每个 tool 有唯一名称和描述其参数的 metadata/schema。(模型上下文协议)

4.1 MCP 的典型场景

假设你写了一个公司内部 Wiki 查询服务:

text
wiki_search(query: str) -> str

你希望 Claude Desktop、Cursor 或其他 AI 客户端能够调用它。

没有 MCP 时,你可能要为每个客户端单独写插件。

有 MCP 后,你可以写一个 MCP Server:

text
AI Client
|
| MCP
v
Wiki MCP Server
|
v
公司内部 Wiki / 知识库 / 数据库

AI 客户端只需要知道:

  1. 这个 MCP Server 暴露了哪些工具?
  2. 每个工具叫什么?
  3. 参数 schema 是什么?
  4. 如何调用?
  5. 返回结果是什么?

4.2 MCP 的核心角色

MCP 中通常有三个角色:

text
MCP Host:AI 应用本体,例如 Claude Desktop、Cursor
MCP Client:Host 内部负责协议通信的客户端
MCP Server:暴露工具、资源、Prompt 的服务

可以理解为:

text
Claude Desktop = Host
Claude 内部 MCP 连接模块 = Client
你写的 wiki_mcp_server.py = Server

4.3 MCP 的核心能力

MCP Server 通常可以暴露三类能力:

能力说明
Tools可被模型调用的函数,例如查数据库、发请求、计算
Resources可被读取的资源,例如文件、文档、上下文
Prompts可复用的提示词模板

你给出的几个核心方法非常典型:

text
initialize
tools/list
tools/call
resources/list
resources/read
prompts/list

其中最常用的是:

text
initialize
tools/list
tools/call

4.4 MCP initialize:协议握手

初始化请求示例:

json
<span class="token punctuation">{<!-- --></span>
<span class="token string-property property">"jsonrpc"</span><span class="token operator">:</span> <span class="token string">"2.0"</span><span class="token punctuation">,</span>
<span class="token string-property property">"id"</span><span class="token operator">:</span> <span class="token number">1</span><span class="token punctuation">,</span>
<span class="token string-property property">"method"</span><span class="token operator">:</span> <span class="token string">"initialize"</span><span class="token punctuation">,</span>
<span class="token string-property property">"params"</span><span class="token operator">:</span> <span class="token punctuation">{<!-- --></span>
<span class="token string-property property">"protocolVersion"</span><span class="token operator">:</span> <span class="token string">"2024-11-05"</span><span class="token punctuation">,</span>
<span class="token string-property property">"capabilities"</span><span class="token operator">:</span> <span class="token punctuation">{<!-- --></span><span class="token punctuation">}</span><span class="token punctuation">,</span>
<span class="token string-property property">"clientInfo"</span><span class="token operator">:</span> <span class="token punctuation">{<!-- --></span>
<span class="token string-property property">"name"</span><span class="token operator">:</span> <span class="token string">"claude-desktop"</span><span class="token punctuation">,</span>
<span class="token string-property property">"version"</span><span class="token operator">:</span> <span class="token string">"1.0.0"</span>
<span class="token punctuation">}</span>
<span class="token punctuation">}</span>
<span class="token punctuation">}</span>

服务端响应:

json
<span class="token punctuation">{<!-- --></span>
<span class="token string-property property">"jsonrpc"</span><span class="token operator">:</span> <span class="token string">"2.0"</span><span class="token punctuation">,</span>
<span class="token string-property property">"id"</span><span class="token operator">:</span> <span class="token number">1</span><span class="token punctuation">,</span>
<span class="token string-property property">"result"</span><span class="token operator">:</span> <span class="token punctuation">{<!-- --></span>
<span class="token string-property property">"protocolVersion"</span><span class="token operator">:</span> <span class="token string">"2024-11-05"</span><span class="token punctuation">,</span>
<span class="token string-property property">"capabilities"</span><span class="token operator">:</span> <span class="token punctuation">{<!-- --></span>
<span class="token string-property property">"tools"</span><span class="token operator">:</span> <span class="token punctuation">{<!-- --></span><span class="token punctuation">}</span><span class="token punctuation">,</span>
<span class="token string-property property">"resources"</span><span class="token operator">:</span> <span class="token punctuation">{<!-- --></span><span class="token punctuation">}</span><span class="token punctuation">,</span>
<span class="token string-property property">"prompts"</span><span class="token operator">:</span> <span class="token punctuation">{<!-- --></span><span class="token punctuation">}</span>
<span class="token punctuation">}</span><span class="token punctuation">,</span>
<span class="token string-property property">"serverInfo"</span><span class="token operator">:</span> <span class="token punctuation">{<!-- --></span>
<span class="token string-property property">"name"</span><span class="token operator">:</span> <span class="token string">"company-wiki-mcp"</span><span class="token punctuation">,</span>
<span class="token string-property property">"version"</span><span class="token operator">:</span> <span class="token string">"1.0.0"</span>
<span class="token punctuation">}</span>
<span class="token punctuation">}</span>
<span class="token punctuation">}</span>

这个过程类似 LSP 的 initialize。

它的作用是:

  • 客户端告诉服务端自己是谁。
  • 服务端告诉客户端自己支持什么能力。
  • 双方确认协议版本。
  • 双方交换 capabilities。

4.5 MCP tools/list:列出可用工具

客户端请求:

json
<span class="token punctuation">{<!-- --></span>
<span class="token string-property property">"jsonrpc"</span><span class="token operator">:</span> <span class="token string">"2.0"</span><span class="token punctuation">,</span>
<span class="token string-property property">"id"</span><span class="token operator">:</span> <span class="token number">2</span><span class="token punctuation">,</span>
<span class="token string-property property">"method"</span><span class="token operator">:</span> <span class="token string">"tools/list"</span><span class="token punctuation">,</span>
<span class="token string-property property">"params"</span><span class="token operator">:</span> <span class="token punctuation">{<!-- --></span><span class="token punctuation">}</span>
<span class="token punctuation">}</span>

服务端返回:

json
<span class="token punctuation">{<!-- --></span>
<span class="token string-property property">"jsonrpc"</span><span class="token operator">:</span> <span class="token string">"2.0"</span><span class="token punctuation">,</span>
<span class="token string-property property">"id"</span><span class="token operator">:</span> <span class="token number">2</span><span class="token punctuation">,</span>
<span class="token string-property property">"result"</span><span class="token operator">:</span> <span class="token punctuation">{<!-- --></span>
<span class="token string-property property">"tools"</span><span class="token operator">:</span> <span class="token punctuation">[</span>
<span class="token punctuation">{<!-- --></span>
<span class="token string-property property">"name"</span><span class="token operator">:</span> <span class="token string">"search_company_wiki"</span><span class="token punctuation">,</span>
<span class="token string-property property">"description"</span><span class="token operator">:</span> <span class="token string">"搜索公司内部 Wiki,适合查询制度、流程、项目文档"</span><span class="token punctuation">,</span>
<span class="token string-property property">"inputSchema"</span><span class="token operator">:</span> <span class="token punctuation">{<!-- --></span>
<span class="token string-property property">"type"</span><span class="token operator">:</span> <span class="token string">"object"</span><span class="token punctuation">,</span>
<span class="token string-property property">"properties"</span><span class="token operator">:</span> <span class="token punctuation">{<!-- --></span>
<span class="token string-property property">"query"</span><span class="token operator">:</span> <span class="token punctuation">{<!-- --></span>
<span class="token string-property property">"type"</span><span class="token operator">:</span> <span class="token string">"string"</span><span class="token punctuation">,</span>
<span class="token string-property property">"description"</span><span class="token operator">:</span> <span class="token string">"搜索关键词"</span>
<span class="token punctuation">}</span>
<span class="token punctuation">}</span><span class="token punctuation">,</span>
<span class="token string-property property">"required"</span><span class="token operator">:</span> <span class="token punctuation">[</span><span class="token string">"query"</span><span class="token punctuation">]</span>
<span class="token punctuation">}</span>
<span class="token punctuation">}</span>
<span class="token punctuation">]</span>
<span class="token punctuation">}</span>
<span class="token punctuation">}</span>

这个结果非常重要。

因为模型并不是直接“知道”你的工具,而是通过 tools/list 获取工具列表。

模型看到工具描述后,才能判断:

text
用户问的是公司制度问题,应该调用 search_company_wiki。

4.6 MCP tools/call:执行工具

客户端调用:

json
<span class="token punctuation">{<!-- --></span>
<span class="token string-property property">"jsonrpc"</span><span class="token operator">:</span> <span class="token string">"2.0"</span><span class="token punctuation">,</span>
<span class="token string-property property">"id"</span><span class="token operator">:</span> <span class="token number">3</span><span class="token punctuation">,</span>
<span class="token string-property property">"method"</span><span class="token operator">:</span> <span class="token string">"tools/call"</span><span class="token punctuation">,</span>
<span class="token string-property property">"params"</span><span class="token operator">:</span> <span class="token punctuation">{<!-- --></span>
<span class="token string-property property">"name"</span><span class="token operator">:</span> <span class="token string">"search_company_wiki"</span><span class="token punctuation">,</span>
<span class="token string-property property">"arguments"</span><span class="token operator">:</span> <span class="token punctuation">{<!-- --></span>
<span class="token string-property property">"query"</span><span class="token operator">:</span> <span class="token string">"年假申请流程"</span>
<span class="token punctuation">}</span>
<span class="token punctuation">}</span>
<span class="token punctuation">}</span>

服务端返回:

json
<span class="token punctuation">{<!-- --></span>
<span class="token string-property property">"jsonrpc"</span><span class="token operator">:</span> <span class="token string">"2.0"</span><span class="token punctuation">,</span>
<span class="token string-property property">"id"</span><span class="token operator">:</span> <span class="token number">3</span><span class="token punctuation">,</span>
<span class="token string-property property">"result"</span><span class="token operator">:</span> <span class="token punctuation">{<!-- --></span>
<span class="token string-property property">"content"</span><span class="token operator">:</span> <span class="token punctuation">[</span>
<span class="token punctuation">{<!-- --></span>
<span class="token string-property property">"type"</span><span class="token operator">:</span> <span class="token string">"text"</span><span class="token punctuation">,</span>
<span class="token string-property property">"text"</span><span class="token operator">:</span> <span class="token string">"年假申请需要在 OA 系统提交请假单,直属领导审批后生效。"</span>
<span class="token punctuation">}</span>
<span class="token punctuation">]</span>
<span class="token punctuation">}</span>
<span class="token punctuation">}</span>

这就是 MCP 的核心闭环:

text
发现工具 → 理解工具 → 调用工具 → 返回结果 → 模型组织回答

4.7 MCP 的本质

MCP 不是简单的函数调用。

它真正解决的是:

AI 应用和外部工具之间的标准化连接问题。

以前:

text
Claude 接 Wiki 要写一套
Cursor 接 Wiki 要写一套
自研 Agent 接 Wiki 又要写一套

现在:

text
只要实现一个 Wiki MCP Server
多个 MCP Client 都可以接入

所以 MCP 可以理解为:

text
AI 工具生态里的 USB-C 接口

五、ACP:让 IDE 可以唤起和控制 Agent

ACP 全称是 Agent Client Protocol

它解决的问题和 MCP 不一样。

MCP 是:

text
AI 调用工具

ACP 是:

text
IDE 调用 Agent

ACP 官方介绍中明确说,它标准化了代码编辑器和 coding agents 之间的通信;相关文档也说明 ACP 面向 agent-editor 集成,而如果你的目标是让 agent 调用外部工具,则应该看 MCP。(GitHub)

5.1 ACP 的典型场景

用户在 IDE 里说:

text
帮我重构这个函数。

这时不是简单调用一个工具,而是让一个 Agent 接管任务。

Agent 可能需要:

  1. 读取当前文件。
  2. 分析项目结构。
  3. 修改多个文件。
  4. 运行测试。
  5. 返回 diff。
  6. 请求用户确认。
  7. 支持取消任务。
  8. 支持中途 steer,比如“慢一点”“更保守一点”“不要改接口”。

这和 MCP 的“调用一个工具”完全不是一个层级。

5.2 ACP 和 LSP 的关系

ACP 很像 LSP。

LSP 是:

text
IDE <-> Language Server

ACP 是:

text
IDE <-> Coding Agent

LSP 解决的是:

text
补全、跳转、诊断、hover

ACP 解决的是:

text
任务执行、文件修改、进度更新、权限请求、取消、中途引导

所以可以这样理解:

text
LSP 是 IDE 接入语言智能的协议。
ACP 是 IDE 接入 AI Agent 的协议。

5.3 ACP 的核心流程

根据 ACP 官方概览,典型流程包括:客户端发送 session/prompt,Agent 发送 session/update 通知进度,必要时 Agent 向客户端请求文件操作或权限,客户端可发送 session/cancel 中断任务,最终 session/prompt 返回 stop reason。(agentclientprotocol.com)

一个简化流程:

text
1. initialize
2. session/new
3. session/prompt
4. session/update
5. session/cancel
6. session/prompt response

不同版本和实现中方法名可能略有差异。例如你提到的 newSessionpromptcancel/steer/queue 更像是概念层或某些实现中的命令表达;当前公开文档中更常见的是 session/promptsession/updatesession/cancel 这类命名。ACP 仍处于发展阶段,部分实现和命名可能会演进。(goose-docs.ai)

5.4 ACP initialize:客户端注册

示例:

json
<span class="token punctuation">{<!-- --></span>
<span class="token string-property property">"jsonrpc"</span><span class="token operator">:</span> <span class="token string">"2.0"</span><span class="token punctuation">,</span>
<span class="token string-property property">"id"</span><span class="token operator">:</span> <span class="token number">1</span><span class="token punctuation">,</span>
<span class="token string-property property">"method"</span><span class="token operator">:</span> <span class="token string">"initialize"</span><span class="token punctuation">,</span>
<span class="token string-property property">"params"</span><span class="token operator">:</span> <span class="token punctuation">{<!-- --></span>
<span class="token string-property property">"protocolVersion"</span><span class="token operator">:</span> <span class="token string">"0.1.0"</span><span class="token punctuation">,</span>
<span class="token string-property property">"clientInfo"</span><span class="token operator">:</span> <span class="token punctuation">{<!-- --></span>
<span class="token string-property property">"name"</span><span class="token operator">:</span> <span class="token string">"cursor"</span><span class="token punctuation">,</span>
<span class="token string-property property">"version"</span><span class="token operator">:</span> <span class="token string">"1.0.0"</span>
<span class="token punctuation">}</span><span class="token punctuation">,</span>
<span class="token string-property property">"capabilities"</span><span class="token operator">:</span> <span class="token punctuation">{<!-- --></span>
<span class="token string-property property">"fileSystem"</span><span class="token operator">:</span> <span class="token boolean">true</span><span class="token punctuation">,</span>
<span class="token string-property property">"terminal"</span><span class="token operator">:</span> <span class="token boolean">true</span><span class="token punctuation">,</span>
<span class="token string-property property">"diff"</span><span class="token operator">:</span> <span class="token boolean">true</span>
<span class="token punctuation">}</span>
<span class="token punctuation">}</span>
<span class="token punctuation">}</span>

Agent 返回:

json
<span class="token punctuation">{<!-- --></span>
<span class="token string-property property">"jsonrpc"</span><span class="token operator">:</span> <span class="token string">"2.0"</span><span class="token punctuation">,</span>
<span class="token string-property property">"id"</span><span class="token operator">:</span> <span class="token number">1</span><span class="token punctuation">,</span>
<span class="token string-property property">"result"</span><span class="token operator">:</span> <span class="token punctuation">{<!-- --></span>
<span class="token string-property property">"agentInfo"</span><span class="token operator">:</span> <span class="token punctuation">{<!-- --></span>
<span class="token string-property property">"name"</span><span class="token operator">:</span> <span class="token string">"my-coding-agent"</span><span class="token punctuation">,</span>
<span class="token string-property property">"version"</span><span class="token operator">:</span> <span class="token string">"1.0.0"</span>
<span class="token punctuation">}</span><span class="token punctuation">,</span>
<span class="token string-property property">"capabilities"</span><span class="token operator">:</span> <span class="token punctuation">{<!-- --></span>
<span class="token string-property property">"streaming"</span><span class="token operator">:</span> <span class="token boolean">true</span><span class="token punctuation">,</span>
<span class="token string-property property">"fileEdit"</span><span class="token operator">:</span> <span class="token boolean">true</span><span class="token punctuation">,</span>
<span class="token string-property property">"terminalCommand"</span><span class="token operator">:</span> <span class="token boolean">true</span><span class="token punctuation">,</span>
<span class="token string-property property">"cancellation"</span><span class="token operator">:</span> <span class="token boolean">true</span>
<span class="token punctuation">}</span>
<span class="token punctuation">}</span>
<span class="token punctuation">}</span>

这里交换的是:

text
客户端能提供什么上下文?
Agent 能执行什么任务?
双方支持哪些能力?

5.5 ACP 创建会话

一个 coding agent 往往是会话化的。

因为它需要记住:

  • 当前任务目标
  • 当前项目路径
  • 已经读取过的文件
  • 已经修改过的文件
  • 当前计划
  • 用户中途补充的约束

示例:

json
<span class="token punctuation">{<!-- --></span>
<span class="token string-property property">"jsonrpc"</span><span class="token operator">:</span> <span class="token string">"2.0"</span><span class="token punctuation">,</span>
<span class="token string-property property">"id"</span><span class="token operator">:</span> <span class="token number">2</span><span class="token punctuation">,</span>
<span class="token string-property property">"method"</span><span class="token operator">:</span> <span class="token string">"session/new"</span><span class="token punctuation">,</span>
<span class="token string-property property">"params"</span><span class="token operator">:</span> <span class="token punctuation">{<!-- --></span>
<span class="token string-property property">"workspace"</span><span class="token operator">:</span> <span class="token string">"file:///Users/me/project"</span><span class="token punctuation">,</span>
<span class="token string-property property">"mode"</span><span class="token operator">:</span> <span class="token string">"edit"</span>
<span class="token punctuation">}</span>
<span class="token punctuation">}</span>

返回:

json
<span class="token punctuation">{<!-- --></span>
<span class="token string-property property">"jsonrpc"</span><span class="token operator">:</span> <span class="token string">"2.0"</span><span class="token punctuation">,</span>
<span class="token string-property property">"id"</span><span class="token operator">:</span> <span class="token number">2</span><span class="token punctuation">,</span>
<span class="token string-property property">"result"</span><span class="token operator">:</span> <span class="token punctuation">{<!-- --></span>
<span class="token string-property property">"sessionId"</span><span class="token operator">:</span> <span class="token string">"sess_123"</span>
<span class="token punctuation">}</span>
<span class="token punctuation">}</span>

5.6 ACP prompt:发送任务

用户在 IDE 里说:

text
帮我把这个函数拆成三个小函数,并补充单元测试。

对应请求:

json
<span class="token punctuation">{<!-- --></span>
<span class="token string-property property">"jsonrpc"</span><span class="token operator">:</span> <span class="token string">"2.0"</span><span class="token punctuation">,</span>
<span class="token string-property property">"id"</span><span class="token operator">:</span> <span class="token number">3</span><span class="token punctuation">,</span>
<span class="token string-property property">"method"</span><span class="token operator">:</span> <span class="token string">"session/prompt"</span><span class="token punctuation">,</span>
<span class="token string-property property">"params"</span><span class="token operator">:</span> <span class="token punctuation">{<!-- --></span>
<span class="token string-property property">"sessionId"</span><span class="token operator">:</span> <span class="token string">"sess_123"</span><span class="token punctuation">,</span>
<span class="token string-property property">"prompt"</span><span class="token operator">:</span> <span class="token string">"帮我把这个函数拆成三个小函数,并补充单元测试。"</span>
<span class="token punctuation">}</span>
<span class="token punctuation">}</span>

Agent 开始工作。

这时它可能会不断发送进度通知:

json
<span class="token punctuation">{<!-- --></span>
<span class="token string-property property">"jsonrpc"</span><span class="token operator">:</span> <span class="token string">"2.0"</span><span class="token punctuation">,</span>
<span class="token string-property property">"method"</span><span class="token operator">:</span> <span class="token string">"session/update"</span><span class="token punctuation">,</span>
<span class="token string-property property">"params"</span><span class="token operator">:</span> <span class="token punctuation">{<!-- --></span>
<span class="token string-property property">"sessionId"</span><span class="token operator">:</span> <span class="token string">"sess_123"</span><span class="token punctuation">,</span>
<span class="token string-property property">"message"</span><span class="token operator">:</span> <span class="token string">"正在分析项目结构..."</span>
<span class="token punctuation">}</span>
<span class="token punctuation">}</span>

再比如:

json
<span class="token punctuation">{<!-- --></span>
<span class="token string-property property">"jsonrpc"</span><span class="token operator">:</span> <span class="token string">"2.0"</span><span class="token punctuation">,</span>
<span class="token string-property property">"method"</span><span class="token operator">:</span> <span class="token string">"session/update"</span><span class="token punctuation">,</span>
<span class="token string-property property">"params"</span><span class="token operator">:</span> <span class="token punctuation">{<!-- --></span>
<span class="token string-property property">"sessionId"</span><span class="token operator">:</span> <span class="token string">"sess_123"</span><span class="token punctuation">,</span>
<span class="token string-property property">"message"</span><span class="token operator">:</span> <span class="token string">"已找到目标函数,准备修改 service.py 和 test_service.py"</span>
<span class="token punctuation">}</span>
<span class="token punctuation">}</span>

最后返回:

json
<span class="token punctuation">{<!-- --></span>
<span class="token string-property property">"jsonrpc"</span><span class="token operator">:</span> <span class="token string">"2.0"</span><span class="token punctuation">,</span>
<span class="token string-property property">"id"</span><span class="token operator">:</span> <span class="token number">3</span><span class="token punctuation">,</span>
<span class="token string-property property">"result"</span><span class="token operator">:</span> <span class="token punctuation">{<!-- --></span>
<span class="token string-property property">"stopReason"</span><span class="token operator">:</span> <span class="token string">"completed"</span><span class="token punctuation">,</span>
<span class="token string-property property">"summary"</span><span class="token operator">:</span> <span class="token string">"已完成函数拆分,并新增 3 个单元测试。"</span>
<span class="token punctuation">}</span>
<span class="token punctuation">}</span>

5.7 ACP cancel:中断任务

Agent 任务可能很长。

用户可能中途发现方向不对,于是点击取消。

json
<span class="token punctuation">{<!-- --></span>
<span class="token string-property property">"jsonrpc"</span><span class="token operator">:</span> <span class="token string">"2.0"</span><span class="token punctuation">,</span>
<span class="token string-property property">"id"</span><span class="token operator">:</span> <span class="token number">4</span><span class="token punctuation">,</span>
<span class="token string-property property">"method"</span><span class="token operator">:</span> <span class="token string">"session/cancel"</span><span class="token punctuation">,</span>
<span class="token string-property property">"params"</span><span class="token operator">:</span> <span class="token punctuation">{<!-- --></span>
<span class="token string-property property">"sessionId"</span><span class="token operator">:</span> <span class="token string">"sess_123"</span>
<span class="token punctuation">}</span>
<span class="token punctuation">}</span>

Agent 收到后应该尽量停止当前任务。

返回:

json
<span class="token punctuation">{<!-- --></span>
<span class="token string-property property">"jsonrpc"</span><span class="token operator">:</span> <span class="token string">"2.0"</span><span class="token punctuation">,</span>
<span class="token string-property property">"id"</span><span class="token operator">:</span> <span class="token number">4</span><span class="token punctuation">,</span>
<span class="token string-property property">"result"</span><span class="token operator">:</span> <span class="token punctuation">{<!-- --></span>
<span class="token string-property property">"cancelled"</span><span class="token operator">:</span> <span class="token boolean">true</span>
<span class="token punctuation">}</span>
<span class="token punctuation">}</span>

5.8 steer:中途引导

你提到的 /steer 很关键。

它不是简单的“新任务”,而是对当前任务的方向修正。

例如用户说:

text
/steer 慢一点,先不要改文件,先给我解释计划。

Agent 应该调整策略:

text
从直接修改模式 → 计划说明模式

或者:

text
/steer 保守一点,不要改公共接口。

Agent 应该把这个约束加入当前会话。

这类能力是 coding agent 和普通 chat bot 的重要区别。

5.9 queue:排队消息

当 Agent 正在执行长任务时,用户可能又发来一句:

text
顺便把测试也补上。

如果立即打断当前任务,可能会导致状态混乱。

所以可以设计 /queue

text
当前任务继续执行
新消息进入队列
当前任务完成后再处理

这对于长任务 Agent 非常重要。


六、MCP 和 ACP 的核心差异

MCP 和 ACP 很容易混淆。

其实它们解决的问题完全不同。

对比项MCPACP
全称Model Context ProtocolAgent Client Protocol
主要对象AI 客户端与外部工具IDE 与 coding agent
核心问题让模型发现并调用工具让 IDE 唤起、控制、协作 Agent
典型客户端Claude Desktop、Cursor、自研 Agent HostCursor、VS Code、Zed 等 IDE
典型服务端Wiki MCP Server、DB MCP Server、Git MCP ServerCoding Agent、Deep Agent、Goose
通信风格工具发现与调用会话、任务、进度、文件操作、权限
类比AI 时代的工具 USB-CAI Agent 时代的 LSP
主动/被动工具被 AI 调用,偏被动Agent 被 IDE 唤起执行任务,偏主动

可以这样记:

text
MCP:让 Agent 会“用工具”。
ACP:让 IDE 会“用 Agent”。

或者更直接:

text
MCP 是 Tool Protocol。
ACP 是 Agent Protocol。

LangChain 文档中也明确区分了两者:ACP 用于 coding agents 和 code editors / IDEs 通信;如果需要 agent 调用外部服务器上的工具,应参考 MCP。(LangChain 文档)


七、Gateway Plugin:把 Agent 接入聊天平台

MCP 和 ACP 主要面向 AI 客户端、IDE、工具生态。

但企业里还有另一个常见需求:

我希望用户可以在微信、Slack、Discord、Telegram 里直接和 Agent 对话。

这时候就需要 Gateway Plugin。

它不是一个像 MCP / ACP 那样的统一协议标准,更像是一种插件化架构模式。

你的设计是这样的:

text
gateway/
platforms/
wechat/
slack/
discord/
telegram/
...

每个平台一个插件包。

每个插件实现统一接口:

python
<span class="token keyword">class</span> <span class="token class-name">PlatformPlugin</span><span class="token punctuation">:</span>
<span class="token keyword">def</span> <span class="token function">receive_message</span><span class="token punctuation">(</span>self<span class="token punctuation">)</span> <span class="token operator">-</span><span class="token operator">></span> Message<span class="token punctuation">:</span>
<span class="token punctuation">.</span><span class="token punctuation">.</span><span class="token punctuation">.</span>

<span class="token keyword">def</span> <span class="token function">send_message</span><span class="token punctuation">(</span>self<span class="token punctuation">,</span> msg<span class="token punctuation">:</span> Message<span class="token punctuation">)</span> <span class="token operator">-</span><span class="token operator">></span> <span class="token boolean">None</span><span class="token punctuation">:</span>
<span class="token punctuation">.</span><span class="token punctuation">.</span><span class="token punctuation">.</span>

<span class="token keyword">def</span> <span class="token function">authenticate</span><span class="token punctuation">(</span>self<span class="token punctuation">)</span> <span class="token operator">-</span><span class="token operator">></span> <span class="token builtin">bool</span><span class="token punctuation">:</span>
<span class="token punctuation">.</span><span class="token punctuation">.</span><span class="token punctuation">.</span>

7.1 为什么需要 Gateway Plugin?

因为不同聊天平台的接入方式完全不同。

Slack 使用 Events API 时,Slack 会把订阅的事件发送给你的应用;Slack 文档中也说明 Events API 支持通过 Socket Mode 或指定公开 HTTP endpoint 接收事件。(Slack 开发者文档)

Telegram Bot API 则支持 getUpdates 和 webhook 两种互斥的更新接收方式,收到的是 JSON 序列化的 Update 对象。(core.telegram.org)

不同平台差异包括:

平台接收消息方式发送消息方式特殊能力
SlackEvents API / Socket ModeWeb APIThread、Channel、App Mention
TelegramgetUpdates / WebhookBot APIChat ID、Inline Keyboard
DiscordGateway / HTTP APIREST / GatewayGuild、Channel、Interaction
微信回调 / 轮询 / 企业微信 API平台 API企业身份、群聊、审批流

如果业务层直接适配每个平台,就会非常混乱。

所以需要 Gateway Plugin 做一层抽象。

7.2 Gateway Plugin 的核心目标

Gateway Plugin 解决的是:

text
不同聊天平台输入输出不统一的问题

它把平台消息统一转换为内部标准消息:

python
<span class="token decorator annotation punctuation">@dataclass</span>
<span class="token keyword">class</span> <span class="token class-name">Message</span><span class="token punctuation">:</span>
platform<span class="token punctuation">:</span> <span class="token builtin">str</span>
conversation_id<span class="token punctuation">:</span> <span class="token builtin">str</span>
user_id<span class="token punctuation">:</span> <span class="token builtin">str</span>
text<span class="token punctuation">:</span> <span class="token builtin">str</span>
attachments<span class="token punctuation">:</span> <span class="token builtin">list</span>
raw<span class="token punctuation">:</span> <span class="token builtin">dict</span>

然后交给 Agent Runtime:

text
Slack Message
Telegram Update
Discord Event
Wechat Message
|
v
Gateway Plugin
|
v
统一 Message 对象
|
v
Agent Runtime

Agent 返回结果后,再由对应插件转换回平台格式:

text
Agent Response
|
v
Gateway Plugin
|
v
Slack / Telegram / Discord / WeChat

7.3 Gateway Plugin 架构示例

text
+----------------------+
|      Slack Plugin    |
+----------------------+
|
+----------------------+
|    Telegram Plugin   |
+----------------------+
|
+----------------------+
|     WeChat Plugin    |
+----------------------+
|
v
+----------------------+
|   Gateway Core       |
| - auth               |
| - routing            |
| - session mapping    |
| - rate limit         |
| - retry              |
+----------------------+
|
v
+----------------------+
|   Agent Runtime      |
| - memory             |
| - tools              |
| - skills             |
| - model              |
+----------------------+

7.4 Gateway Plugin 的关键模块

1. 平台认证

python
<span class="token keyword">def</span> <span class="token function">authenticate</span><span class="token punctuation">(</span>self<span class="token punctuation">)</span> <span class="token operator">-</span><span class="token operator">></span> <span class="token builtin">bool</span><span class="token punctuation">:</span>
<span class="token punctuation">.</span><span class="token punctuation">.</span><span class="token punctuation">.</span>

不同平台认证方式不同:

  • Slack:OAuth / Bot Token
  • Telegram:Bot Token
  • Discord:Bot Token
  • 企业微信:CorpID / Secret / AgentID

插件层负责处理平台认证,不要把这些细节暴露给 Agent Runtime。

2. 消息接收

python
<span class="token keyword">def</span> <span class="token function">receive_message</span><span class="token punctuation">(</span>self<span class="token punctuation">)</span> <span class="token operator">-</span><span class="token operator">></span> Message<span class="token punctuation">:</span>
<span class="token punctuation">.</span><span class="token punctuation">.</span><span class="token punctuation">.</span>

不同平台收到的原始消息结构完全不同。

插件要把它转成统一 Message。

例如 Slack 原始事件:

json
<span class="token punctuation">{<!-- --></span>
<span class="token string-property property">"type"</span><span class="token operator">:</span> <span class="token string">"message"</span><span class="token punctuation">,</span>
<span class="token string-property property">"user"</span><span class="token operator">:</span> <span class="token string">"U123"</span><span class="token punctuation">,</span>
<span class="token string-property property">"channel"</span><span class="token operator">:</span> <span class="token string">"C123"</span><span class="token punctuation">,</span>
<span class="token string-property property">"text"</span><span class="token operator">:</span> <span class="token string">"帮我总结一下今天的工作"</span>
<span class="token punctuation">}</span>

统一成:

json
<span class="token punctuation">{<!-- --></span>
<span class="token string-property property">"platform"</span><span class="token operator">:</span> <span class="token string">"slack"</span><span class="token punctuation">,</span>
<span class="token string-property property">"conversation_id"</span><span class="token operator">:</span> <span class="token string">"C123"</span><span class="token punctuation">,</span>
<span class="token string-property property">"user_id"</span><span class="token operator">:</span> <span class="token string">"U123"</span><span class="token punctuation">,</span>
<span class="token string-property property">"text"</span><span class="token operator">:</span> <span class="token string">"帮我总结一下今天的工作"</span>
<span class="token punctuation">}</span>

3. 消息发送

python
<span class="token keyword">def</span> <span class="token function">send_message</span><span class="token punctuation">(</span>self<span class="token punctuation">,</span> msg<span class="token punctuation">:</span> Message<span class="token punctuation">)</span> <span class="token operator">-</span><span class="token operator">></span> <span class="token boolean">None</span><span class="token punctuation">:</span>
<span class="token punctuation">.</span><span class="token punctuation">.</span><span class="token punctuation">.</span>

Agent 返回的统一结构:

json
<span class="token punctuation">{<!-- --></span>
<span class="token string-property property">"conversation_id"</span><span class="token operator">:</span> <span class="token string">"C123"</span><span class="token punctuation">,</span>
<span class="token string-property property">"text"</span><span class="token operator">:</span> <span class="token string">"今天你主要完成了三个事项……"</span>
<span class="token punctuation">}</span>

Slack 插件转换成 Slack Web API 调用。

Telegram 插件转换成 sendMessage

Discord 插件转换成 Discord 消息发送。

4. 会话映射

同一个用户可能在多个平台出现:

text
Slack user U123
Telegram user T456
企业微信 user W789

Gateway 需要维护身份映射:

text
platform_user_id -> internal_user_id

这样才能共享 Agent 记忆和上下文。

5. 平台能力降级

不同平台支持的能力不同。

例如:

  • Slack 支持 Thread。
  • Telegram 支持 Inline Keyboard。
  • Discord 支持 Slash Command。
  • 微信可能有自己的卡片消息。

Agent Runtime 不应该关心这些差异。

Gateway Plugin 应该做能力适配:

python
<span class="token keyword">class</span> <span class="token class-name">PlatformCapabilities</span><span class="token punctuation">:</span>
supports_thread<span class="token punctuation">:</span> <span class="token builtin">bool</span>
supports_markdown<span class="token punctuation">:</span> <span class="token builtin">bool</span>
supports_file_upload<span class="token punctuation">:</span> <span class="token builtin">bool</span>
supports_buttons<span class="token punctuation">:</span> <span class="token builtin">bool</span>

如果平台不支持某种能力,就降级成普通文本。


八、把 MCP、ACP、Gateway Plugin 放到一个系统里

在一个完整 AI Agent 系统中,这三者可以同时存在。

例如你要做一个企业 AI 助手:

text
用户入口:
- IDE
- Claude Desktop
- 企业微信
- Slack

Agent 能力:
- 查询内部 Wiki
- 查询数据库
- 读取代码仓库
- 修改代码
- 生成日报

这时候架构可以是:

text
                  +-------------------+
|   Claude Desktop  |
+-------------------+
|
| MCP
v
+-------------------+    +-------------------+
|   Wiki MCP Server |    |   DB MCP Server   |
+-------------------+    +-------------------+

+-------------------+
|   Cursor / IDE    |
+-------------------+
|
| ACP
v
+-------------------+
|   Coding Agent    |
+-------------------+

+-------------------+     +-------------------+
| Slack / WeChat    | --> | Gateway Plugin    |
| Telegram / Discord|     | Platform Adapter  |
+-------------------+     +-------------------+
|
v
+-------------------+
|   Agent Runtime   |
+-------------------+

8.1 分工关系

模块解决什么问题
MCPAgent 如何调用外部工具
ACPIDE 如何驱动 Agent 执行代码任务
Gateway Plugin聊天平台如何接入 Agent
LSPIDE 如何获得语言能力
JSON-RPC请求、响应、通知的消息格式
stdio / HTTP / SSE消息传输通道

8.2 一个完整调用链示例

用户在 Slack 里问:

text
公司年假怎么申请?

流程:

text
1. Slack Plugin 收到 message event
2. Gateway 转成统一 Message
3. Agent Runtime 接收用户问题
4. Agent 判断需要查询内部制度
5. Agent 通过 MCP 调用 wiki_search 工具
6. Wiki MCP Server 返回制度内容
7. Agent 组织回答
8. Gateway 调用 Slack Plugin 发回消息

调用链:

text
Slack
-> Gateway Plugin
-> Agent Runtime
-> MCP Client
-> Wiki MCP Server
-> Agent Runtime
-> Gateway Plugin
-> Slack

用户在 Cursor 里说:

text
帮我重构 order_service.py,并补充测试。

流程:

text
1. Cursor 通过 ACP 创建 session
2. Cursor 发送 session/prompt
3. Coding Agent 分析项目文件
4. Agent 请求文件读写权限
5. Agent 修改代码
6. Agent 返回进度 update
7. Agent 返回最终 diff 和总结

调用链:

text
Cursor
-> ACP
-> Coding Agent
-> File System / Tools
-> ACP Update
-> Cursor UI

九、为什么这些协议都喜欢“initialize + capabilities”?

你会发现 LSP、MCP、ACP 都有一个共同点:

text
第一步都是 initialize。

这是为什么?

因为这类协议都不是一次性 HTTP API,而是长连接、长生命周期、双向协作。

双方需要先交换能力。

例如:

LSP initialize

编辑器问语言服务器:

text
你支持 hover 吗?
你支持 completion 吗?
你支持 definition 吗?

语言服务器返回:

text
我支持 hover、completion、diagnostics。

MCP initialize

AI Client 问 MCP Server:

text
你支持 tools 吗?
你支持 resources 吗?
你支持 prompts 吗?

MCP Server 返回:

text
我支持 tools/list 和 tools/call。

ACP initialize

IDE 问 Coding Agent:

text
你支持文件编辑吗?
你支持任务取消吗?
你支持流式进度吗?

Agent 返回:

text
我支持 file edit、streaming update、cancel。

这就是 capabilities 协商。

它的好处是:

  1. 客户端不用提前写死服务端能力。
  2. 服务端可以按需暴露能力。
  3. 不同版本之间更容易兼容。
  4. 可以做渐进式增强。
  5. 可以支持实验性能力。

十、协议设计中的几个关键概念

10.1 Request / Response

用于需要结果的调用。

例如:

text
tools/list
tools/call
session/prompt
textDocument/hover

都有明确返回值。

10.2 Notification

用于不需要响应的事件。

例如:

text
initialized
progress update
file changed
diagnostics published

适合“我告诉你一件事,但你不用回复我”。

10.3 Capability

表示一方支持什么能力。

例如:

json
<span class="token punctuation">{<!-- --></span>
<span class="token string-property property">"capabilities"</span><span class="token operator">:</span> <span class="token punctuation">{<!-- --></span>
<span class="token string-property property">"tools"</span><span class="token operator">:</span> <span class="token punctuation">{<!-- --></span><span class="token punctuation">}</span><span class="token punctuation">,</span>
<span class="token string-property property">"resources"</span><span class="token operator">:</span> <span class="token punctuation">{<!-- --></span><span class="token punctuation">}</span><span class="token punctuation">,</span>
<span class="token string-property property">"prompts"</span><span class="token operator">:</span> <span class="token punctuation">{<!-- --></span><span class="token punctuation">}</span>
<span class="token punctuation">}</span>
<span class="token punctuation">}</span>

10.4 Session

表示一次上下文连续的任务。

ACP 中 session 很重要,因为 coding agent 通常不是一次调用就结束,而是持续多轮工作。

10.5 Transport

Transport 是消息怎么传。

常见有:

text
stdio
HTTP
SSE
WebSocket
Streamable HTTP

协议定义“消息长什么样”,Transport 定义“消息怎么发”。


十一、自己实现一个最小 MCP Server

下面用 Python 写一个非常简化的 MCP 风格 Server。

注意:这是教学示例,不是完整 MCP SDK 实现。

python
<span class="token keyword">import</span> sys
<span class="token keyword">import</span> json

TOOLS <span class="token operator">=</span> <span class="token punctuation">[</span>
<span class="token punctuation">{<!-- --></span>
<span class="token string">"name"</span><span class="token punctuation">:</span> <span class="token string">"search_company_wiki"</span><span class="token punctuation">,</span>
<span class="token string">"description"</span><span class="token punctuation">:</span> <span class="token string">"搜索公司内部 Wiki"</span><span class="token punctuation">,</span>
<span class="token string">"inputSchema"</span><span class="token punctuation">:</span> <span class="token punctuation">{<!-- --></span>
<span class="token string">"type"</span><span class="token punctuation">:</span> <span class="token string">"object"</span><span class="token punctuation">,</span>
<span class="token string">"properties"</span><span class="token punctuation">:</span> <span class="token punctuation">{<!-- --></span>
<span class="token string">"query"</span><span class="token punctuation">:</span> <span class="token punctuation">{<!-- --></span>
<span class="token string">"type"</span><span class="token punctuation">:</span> <span class="token string">"string"</span><span class="token punctuation">,</span>
<span class="token string">"description"</span><span class="token punctuation">:</span> <span class="token string">"搜索关键词"</span>
<span class="token punctuation">}</span>
<span class="token punctuation">}</span><span class="token punctuation">,</span>
<span class="token string">"required"</span><span class="token punctuation">:</span> <span class="token punctuation">[</span><span class="token string">"query"</span><span class="token punctuation">]</span>
<span class="token punctuation">}</span>
<span class="token punctuation">}</span>
<span class="token punctuation">]</span>

<span class="token keyword">def</span> <span class="token function">send_response</span><span class="token punctuation">(</span>request_id<span class="token punctuation">,</span> result<span class="token punctuation">)</span><span class="token punctuation">:</span>
resp <span class="token operator">=</span> <span class="token punctuation">{<!-- --></span>
<span class="token string">"jsonrpc"</span><span class="token punctuation">:</span> <span class="token string">"2.0"</span><span class="token punctuation">,</span>
<span class="token string">"id"</span><span class="token punctuation">:</span> request_id<span class="token punctuation">,</span>
<span class="token string">"result"</span><span class="token punctuation">:</span> result
<span class="token punctuation">}</span>
<span class="token keyword">print</span><span class="token punctuation">(</span>json<span class="token punctuation">.</span>dumps<span class="token punctuation">(</span>resp<span class="token punctuation">,</span> ensure_ascii<span class="token operator">=</span><span class="token boolean">False</span><span class="token punctuation">)</span><span class="token punctuation">,</span> flush<span class="token operator">=</span><span class="token boolean">True</span><span class="token punctuation">)</span>

<span class="token keyword">def</span> <span class="token function">send_error</span><span class="token punctuation">(</span>request_id<span class="token punctuation">,</span> code<span class="token punctuation">,</span> message<span class="token punctuation">)</span><span class="token punctuation">:</span>
resp <span class="token operator">=</span> <span class="token punctuation">{<!-- --></span>
<span class="token string">"jsonrpc"</span><span class="token punctuation">:</span> <span class="token string">"2.0"</span><span class="token punctuation">,</span>
<span class="token string">"id"</span><span class="token punctuation">:</span> request_id<span class="token punctuation">,</span>
<span class="token string">"error"</span><span class="token punctuation">:</span> <span class="token punctuation">{<!-- --></span>
<span class="token string">"code"</span><span class="token punctuation">:</span> code<span class="token punctuation">,</span>
<span class="token string">"message"</span><span class="token punctuation">:</span> message
<span class="token punctuation">}</span>
<span class="token punctuation">}</span>
<span class="token keyword">print</span><span class="token punctuation">(</span>json<span class="token punctuation">.</span>dumps<span class="token punctuation">(</span>resp<span class="token punctuation">,</span> ensure_ascii<span class="token operator">=</span><span class="token boolean">False</span><span class="token punctuation">)</span><span class="token punctuation">,</span> flush<span class="token operator">=</span><span class="token boolean">True</span><span class="token punctuation">)</span>

<span class="token keyword">def</span> <span class="token function">handle_initialize</span><span class="token punctuation">(</span>req<span class="token punctuation">)</span><span class="token punctuation">:</span>
send_response<span class="token punctuation">(</span>req<span class="token punctuation">[</span><span class="token string">"id"</span><span class="token punctuation">]</span><span class="token punctuation">,</span> <span class="token punctuation">{<!-- --></span>
<span class="token string">"protocolVersion"</span><span class="token punctuation">:</span> <span class="token string">"2024-11-05"</span><span class="token punctuation">,</span>
<span class="token string">"capabilities"</span><span class="token punctuation">:</span> <span class="token punctuation">{<!-- --></span>
<span class="token string">"tools"</span><span class="token punctuation">:</span> <span class="token punctuation">{<!-- --></span><span class="token punctuation">}</span>
<span class="token punctuation">}</span><span class="token punctuation">,</span>
<span class="token string">"serverInfo"</span><span class="token punctuation">:</span> <span class="token punctuation">{<!-- --></span>
<span class="token string">"name"</span><span class="token punctuation">:</span> <span class="token string">"demo-wiki-mcp"</span><span class="token punctuation">,</span>
<span class="token string">"version"</span><span class="token punctuation">:</span> <span class="token string">"1.0.0"</span>
<span class="token punctuation">}</span>
<span class="token punctuation">}</span><span class="token punctuation">)</span>

<span class="token keyword">def</span> <span class="token function">handle_tools_list</span><span class="token punctuation">(</span>req<span class="token punctuation">)</span><span class="token punctuation">:</span>
send_response<span class="token punctuation">(</span>req<span class="token punctuation">[</span><span class="token string">"id"</span><span class="token punctuation">]</span><span class="token punctuation">,</span> <span class="token punctuation">{<!-- --></span>
<span class="token string">"tools"</span><span class="token punctuation">:</span> TOOLS
<span class="token punctuation">}</span><span class="token punctuation">)</span>

<span class="token keyword">def</span> <span class="token function">handle_tools_call</span><span class="token punctuation">(</span>req<span class="token punctuation">)</span><span class="token punctuation">:</span>
params <span class="token operator">=</span> req<span class="token punctuation">.</span>get<span class="token punctuation">(</span><span class="token string">"params"</span><span class="token punctuation">,</span> <span class="token punctuation">{<!-- --></span><span class="token punctuation">}</span><span class="token punctuation">)</span>
name <span class="token operator">=</span> params<span class="token punctuation">.</span>get<span class="token punctuation">(</span><span class="token string">"name"</span><span class="token punctuation">)</span>
arguments <span class="token operator">=</span> params<span class="token punctuation">.</span>get<span class="token punctuation">(</span><span class="token string">"arguments"</span><span class="token punctuation">,</span> <span class="token punctuation">{<!-- --></span><span class="token punctuation">}</span><span class="token punctuation">)</span>

<span class="token keyword">if</span> name <span class="token operator">!=</span> <span class="token string">"search_company_wiki"</span><span class="token punctuation">:</span>
send_error<span class="token punctuation">(</span>req<span class="token punctuation">[</span><span class="token string">"id"</span><span class="token punctuation">]</span><span class="token punctuation">,</span> <span class="token operator">-</span><span class="token number">32601</span><span class="token punctuation">,</span> <span class="token string-interpolation"><span class="token string">f"Unknown tool: </span><span class="token interpolation"><span class="token punctuation">{<!-- --></span>name<span class="token punctuation">}</span></span><span class="token string">"</span></span><span class="token punctuation">)</span>
<span class="token keyword">return</span>

query <span class="token operator">=</span> arguments<span class="token punctuation">.</span>get<span class="token punctuation">(</span><span class="token string">"query"</span><span class="token punctuation">,</span> <span class="token string">""</span><span class="token punctuation">)</span>

<span class="token comment"># 这里应该接真实的 Wiki / RAG / 数据库</span>
result <span class="token operator">=</span> <span class="token string-interpolation"><span class="token string">f"你查询的是:</span><span class="token interpolation"><span class="token punctuation">{<!-- --></span>query<span class="token punctuation">}</span></span><span class="token string">。这里返回公司内部 Wiki 的模拟结果。"</span></span>

send_response<span class="token punctuation">(</span>req<span class="token punctuation">[</span><span class="token string">"id"</span><span class="token punctuation">]</span><span class="token punctuation">,</span> <span class="token punctuation">{<!-- --></span>
<span class="token string">"content"</span><span class="token punctuation">:</span> <span class="token punctuation">[</span>
<span class="token punctuation">{<!-- --></span>
<span class="token string">"type"</span><span class="token punctuation">:</span> <span class="token string">"text"</span><span class="token punctuation">,</span>
<span class="token string">"text"</span><span class="token punctuation">:</span> result
<span class="token punctuation">}</span>
<span class="token punctuation">]</span>
<span class="token punctuation">}</span><span class="token punctuation">)</span>

<span class="token keyword">def</span> <span class="token function">main</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">:</span>
<span class="token keyword">for</span> line <span class="token keyword">in</span> sys<span class="token punctuation">.</span>stdin<span class="token punctuation">:</span>
line <span class="token operator">=</span> line<span class="token punctuation">.</span>strip<span class="token punctuation">(</span><span class="token punctuation">)</span>
<span class="token keyword">if</span> <span class="token keyword">not</span> line<span class="token punctuation">:</span>
<span class="token keyword">continue</span>

req <span class="token operator">=</span> json<span class="token punctuation">.</span>loads<span class="token punctuation">(</span>line<span class="token punctuation">)</span>
method <span class="token operator">=</span> req<span class="token punctuation">.</span>get<span class="token punctuation">(</span><span class="token string">"method"</span><span class="token punctuation">)</span>

<span class="token keyword">if</span> method <span class="token operator">==</span> <span class="token string">"initialize"</span><span class="token punctuation">:</span>
handle_initialize<span class="token punctuation">(</span>req<span class="token punctuation">)</span>
<span class="token keyword">elif</span> method <span class="token operator">==</span> <span class="token string">"tools/list"</span><span class="token punctuation">:</span>
handle_tools_list<span class="token punctuation">(</span>req<span class="token punctuation">)</span>
<span class="token keyword">elif</span> method <span class="token operator">==</span> <span class="token string">"tools/call"</span><span class="token punctuation">:</span>
handle_tools_call<span class="token punctuation">(</span>req<span class="token punctuation">)</span>
<span class="token keyword">else</span><span class="token punctuation">:</span>
send_error<span class="token punctuation">(</span>req<span class="token punctuation">.</span>get<span class="token punctuation">(</span><span class="token string">"id"</span><span class="token punctuation">)</span><span class="token punctuation">,</span> <span class="token operator">-</span><span class="token number">32601</span><span class="token punctuation">,</span> <span class="token string-interpolation"><span class="token string">f"Method not found: </span><span class="token interpolation"><span class="token punctuation">{<!-- --></span>method<span class="token punctuation">}</span></span><span class="token string">"</span></span><span class="token punctuation">)</span>

<span class="token keyword">if</span> __name__ <span class="token operator">==</span> <span class="token string">"__main__"</span><span class="token punctuation">:</span>
main<span class="token punctuation">(</span><span class="token punctuation">)</span>

这个最小版本实现了:

text
initialize
tools/list
tools/call

也就是 MCP 最核心的工具调用闭环。


十二、自己实现一个最小 ACP 风格 Agent

下面是一个简化版 ACP 风格 Agent。

python
<span class="token keyword">import</span> sys
<span class="token keyword">import</span> json
<span class="token keyword">import</span> uuid

sessions <span class="token operator">=</span> <span class="token punctuation">{<!-- --></span><span class="token punctuation">}</span>

<span class="token keyword">def</span> <span class="token function">send_response</span><span class="token punctuation">(</span>request_id<span class="token punctuation">,</span> result<span class="token punctuation">)</span><span class="token punctuation">:</span>
<span class="token keyword">print</span><span class="token punctuation">(</span>json<span class="token punctuation">.</span>dumps<span class="token punctuation">(</span><span class="token punctuation">{<!-- --></span>
<span class="token string">"jsonrpc"</span><span class="token punctuation">:</span> <span class="token string">"2.0"</span><span class="token punctuation">,</span>
<span class="token string">"id"</span><span class="token punctuation">:</span> request_id<span class="token punctuation">,</span>
<span class="token string">"result"</span><span class="token punctuation">:</span> result
<span class="token punctuation">}</span><span class="token punctuation">,</span> ensure_ascii<span class="token operator">=</span><span class="token boolean">False</span><span class="token punctuation">)</span><span class="token punctuation">,</span> flush<span class="token operator">=</span><span class="token boolean">True</span><span class="token punctuation">)</span>

<span class="token keyword">def</span> <span class="token function">send_notification</span><span class="token punctuation">(</span>method<span class="token punctuation">,</span> params<span class="token punctuation">)</span><span class="token punctuation">:</span>
<span class="token keyword">print</span><span class="token punctuation">(</span>json<span class="token punctuation">.</span>dumps<span class="token punctuation">(</span><span class="token punctuation">{<!-- --></span>
<span class="token string">"jsonrpc"</span><span class="token punctuation">:</span> <span class="token string">"2.0"</span><span class="token punctuation">,</span>
<span class="token string">"method"</span><span class="token punctuation">:</span> method<span class="token punctuation">,</span>
<span class="token string">"params"</span><span class="token punctuation">:</span> params
<span class="token punctuation">}</span><span class="token punctuation">,</span> ensure_ascii<span class="token operator">=</span><span class="token boolean">False</span><span class="token punctuation">)</span><span class="token punctuation">,</span> flush<span class="token operator">=</span><span class="token boolean">True</span><span class="token punctuation">)</span>

<span class="token keyword">def</span> <span class="token function">handle_initialize</span><span class="token punctuation">(</span>req<span class="token punctuation">)</span><span class="token punctuation">:</span>
send_response<span class="token punctuation">(</span>req<span class="token punctuation">[</span><span class="token string">"id"</span><span class="token punctuation">]</span><span class="token punctuation">,</span> <span class="token punctuation">{<!-- --></span>
<span class="token string">"agentInfo"</span><span class="token punctuation">:</span> <span class="token punctuation">{<!-- --></span>
<span class="token string">"name"</span><span class="token punctuation">:</span> <span class="token string">"demo-coding-agent"</span><span class="token punctuation">,</span>
<span class="token string">"version"</span><span class="token punctuation">:</span> <span class="token string">"1.0.0"</span>
<span class="token punctuation">}</span><span class="token punctuation">,</span>
<span class="token string">"capabilities"</span><span class="token punctuation">:</span> <span class="token punctuation">{<!-- --></span>
<span class="token string">"streaming"</span><span class="token punctuation">:</span> <span class="token boolean">True</span><span class="token punctuation">,</span>
<span class="token string">"cancellation"</span><span class="token punctuation">:</span> <span class="token boolean">True</span><span class="token punctuation">,</span>
<span class="token string">"fileEdit"</span><span class="token punctuation">:</span> <span class="token boolean">False</span>
<span class="token punctuation">}</span>
<span class="token punctuation">}</span><span class="token punctuation">)</span>

<span class="token keyword">def</span> <span class="token function">handle_session_new</span><span class="token punctuation">(</span>req<span class="token punctuation">)</span><span class="token punctuation">:</span>
session_id <span class="token operator">=</span> <span class="token builtin">str</span><span class="token punctuation">(</span>uuid<span class="token punctuation">.</span>uuid4<span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">)</span>
sessions<span class="token punctuation">[</span>session_id<span class="token punctuation">]</span> <span class="token operator">=</span> <span class="token punctuation">{<!-- --></span>
<span class="token string">"messages"</span><span class="token punctuation">:</span> <span class="token punctuation">[</span><span class="token punctuation">]</span><span class="token punctuation">,</span>
<span class="token string">"cancelled"</span><span class="token punctuation">:</span> <span class="token boolean">False</span>
<span class="token punctuation">}</span>

send_response<span class="token punctuation">(</span>req<span class="token punctuation">[</span><span class="token string">"id"</span><span class="token punctuation">]</span><span class="token punctuation">,</span> <span class="token punctuation">{<!-- --></span>
<span class="token string">"sessionId"</span><span class="token punctuation">:</span> session_id
<span class="token punctuation">}</span><span class="token punctuation">)</span>

<span class="token keyword">def</span> <span class="token function">handle_session_prompt</span><span class="token punctuation">(</span>req<span class="token punctuation">)</span><span class="token punctuation">:</span>
params <span class="token operator">=</span> req<span class="token punctuation">.</span>get<span class="token punctuation">(</span><span class="token string">"params"</span><span class="token punctuation">,</span> <span class="token punctuation">{<!-- --></span><span class="token punctuation">}</span><span class="token punctuation">)</span>
session_id <span class="token operator">=</span> params<span class="token punctuation">.</span>get<span class="token punctuation">(</span><span class="token string">"sessionId"</span><span class="token punctuation">)</span>
prompt <span class="token operator">=</span> params<span class="token punctuation">.</span>get<span class="token punctuation">(</span><span class="token string">"prompt"</span><span class="token punctuation">)</span>

<span class="token keyword">if</span> session_id <span class="token keyword">not</span> <span class="token keyword">in</span> sessions<span class="token punctuation">:</span>
send_response<span class="token punctuation">(</span>req<span class="token punctuation">[</span><span class="token string">"id"</span><span class="token punctuation">]</span><span class="token punctuation">,</span> <span class="token punctuation">{<!-- --></span>
<span class="token string">"stopReason"</span><span class="token punctuation">:</span> <span class="token string">"error"</span><span class="token punctuation">,</span>
<span class="token string">"message"</span><span class="token punctuation">:</span> <span class="token string">"Session not found"</span>
<span class="token punctuation">}</span><span class="token punctuation">)</span>
<span class="token keyword">return</span>

sessions<span class="token punctuation">[</span>session_id<span class="token punctuation">]</span><span class="token punctuation">[</span><span class="token string">"messages"</span><span class="token punctuation">]</span><span class="token punctuation">.</span>append<span class="token punctuation">(</span><span class="token punctuation">{<!-- --></span>
<span class="token string">"role"</span><span class="token punctuation">:</span> <span class="token string">"user"</span><span class="token punctuation">,</span>
<span class="token string">"content"</span><span class="token punctuation">:</span> prompt
<span class="token punctuation">}</span><span class="token punctuation">)</span>

send_notification<span class="token punctuation">(</span><span class="token string">"session/update"</span><span class="token punctuation">,</span> <span class="token punctuation">{<!-- --></span>
<span class="token string">"sessionId"</span><span class="token punctuation">:</span> session_id<span class="token punctuation">,</span>
<span class="token string">"message"</span><span class="token punctuation">:</span> <span class="token string">"正在分析任务..."</span>
<span class="token punctuation">}</span><span class="token punctuation">)</span>

send_notification<span class="token punctuation">(</span><span class="token string">"session/update"</span><span class="token punctuation">,</span> <span class="token punctuation">{<!-- --></span>
<span class="token string">"sessionId"</span><span class="token punctuation">:</span> session_id<span class="token punctuation">,</span>
<span class="token string">"message"</span><span class="token punctuation">:</span> <span class="token string">"正在制定执行计划..."</span>
<span class="token punctuation">}</span><span class="token punctuation">)</span>

send_response<span class="token punctuation">(</span>req<span class="token punctuation">[</span><span class="token string">"id"</span><span class="token punctuation">]</span><span class="token punctuation">,</span> <span class="token punctuation">{<!-- --></span>
<span class="token string">"stopReason"</span><span class="token punctuation">:</span> <span class="token string">"completed"</span><span class="token punctuation">,</span>
<span class="token string">"summary"</span><span class="token punctuation">:</span> <span class="token string-interpolation"><span class="token string">f"已收到任务:</span><span class="token interpolation"><span class="token punctuation">{<!-- --></span>prompt<span class="token punctuation">}</span></span><span class="token string">。这里是模拟执行结果。"</span></span>
<span class="token punctuation">}</span><span class="token punctuation">)</span>

<span class="token keyword">def</span> <span class="token function">handle_session_cancel</span><span class="token punctuation">(</span>req<span class="token punctuation">)</span><span class="token punctuation">:</span>
params <span class="token operator">=</span> req<span class="token punctuation">.</span>get<span class="token punctuation">(</span><span class="token string">"params"</span><span class="token punctuation">,</span> <span class="token punctuation">{<!-- --></span><span class="token punctuation">}</span><span class="token punctuation">)</span>
session_id <span class="token operator">=</span> params<span class="token punctuation">.</span>get<span class="token punctuation">(</span><span class="token string">"sessionId"</span><span class="token punctuation">)</span>

<span class="token keyword">if</span> session_id <span class="token keyword">in</span> sessions<span class="token punctuation">:</span>
sessions<span class="token punctuation">[</span>session_id<span class="token punctuation">]</span><span class="token punctuation">[</span><span class="token string">"cancelled"</span><span class="token punctuation">]</span> <span class="token operator">=</span> <span class="token boolean">True</span>

send_response<span class="token punctuation">(</span>req<span class="token punctuation">[</span><span class="token string">"id"</span><span class="token punctuation">]</span><span class="token punctuation">,</span> <span class="token punctuation">{<!-- --></span>
<span class="token string">"cancelled"</span><span class="token punctuation">:</span> <span class="token boolean">True</span>
<span class="token punctuation">}</span><span class="token punctuation">)</span>

<span class="token keyword">def</span> <span class="token function">main</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">:</span>
<span class="token keyword">for</span> line <span class="token keyword">in</span> sys<span class="token punctuation">.</span>stdin<span class="token punctuation">:</span>
line <span class="token operator">=</span> line<span class="token punctuation">.</span>strip<span class="token punctuation">(</span><span class="token punctuation">)</span>
<span class="token keyword">if</span> <span class="token keyword">not</span> line<span class="token punctuation">:</span>
<span class="token keyword">continue</span>

req <span class="token operator">=</span> json<span class="token punctuation">.</span>loads<span class="token punctuation">(</span>line<span class="token punctuation">)</span>
method <span class="token operator">=</span> req<span class="token punctuation">.</span>get<span class="token punctuation">(</span><span class="token string">"method"</span><span class="token punctuation">)</span>

<span class="token keyword">if</span> method <span class="token operator">==</span> <span class="token string">"initialize"</span><span class="token punctuation">:</span>
handle_initialize<span class="token punctuation">(</span>req<span class="token punctuation">)</span>
<span class="token keyword">elif</span> method <span class="token operator">==</span> <span class="token string">"session/new"</span><span class="token punctuation">:</span>
handle_session_new<span class="token punctuation">(</span>req<span class="token punctuation">)</span>
<span class="token keyword">elif</span> method <span class="token operator">==</span> <span class="token string">"session/prompt"</span><span class="token punctuation">:</span>
handle_session_prompt<span class="token punctuation">(</span>req<span class="token punctuation">)</span>
<span class="token keyword">elif</span> method <span class="token operator">==</span> <span class="token string">"session/cancel"</span><span class="token punctuation">:</span>
handle_session_cancel<span class="token punctuation">(</span>req<span class="token punctuation">)</span>
<span class="token keyword">else</span><span class="token punctuation">:</span>
send_response<span class="token punctuation">(</span>req<span class="token punctuation">.</span>get<span class="token punctuation">(</span><span class="token string">"id"</span><span class="token punctuation">)</span><span class="token punctuation">,</span> <span class="token punctuation">{<!-- --></span>
<span class="token string">"error"</span><span class="token punctuation">:</span> <span class="token string-interpolation"><span class="token string">f"Unknown method: </span><span class="token interpolation"><span class="token punctuation">{<!-- --></span>method<span class="token punctuation">}</span></span><span class="token string">"</span></span>
<span class="token punctuation">}</span><span class="token punctuation">)</span>

<span class="token keyword">if</span> __name__ <span class="token operator">==</span> <span class="token string">"__main__"</span><span class="token punctuation">:</span>
main<span class="token punctuation">(</span><span class="token punctuation">)</span>

这个示例体现了 ACP 的核心思想:

text
不是调用一个工具,而是创建一个 Agent 会话,让 Agent 持续处理任务。

十三、Gateway Plugin 最小实现

下面是一个简单的插件抽象。

python
<span class="token keyword">from</span> dataclasses <span class="token keyword">import</span> dataclass
<span class="token keyword">from</span> typing <span class="token keyword">import</span> Any<span class="token punctuation">,</span> Dict<span class="token punctuation">,</span> List

<span class="token decorator annotation punctuation">@dataclass</span>
<span class="token keyword">class</span> <span class="token class-name">Message</span><span class="token punctuation">:</span>
platform<span class="token punctuation">:</span> <span class="token builtin">str</span>
conversation_id<span class="token punctuation">:</span> <span class="token builtin">str</span>
user_id<span class="token punctuation">:</span> <span class="token builtin">str</span>
text<span class="token punctuation">:</span> <span class="token builtin">str</span>
attachments<span class="token punctuation">:</span> List<span class="token punctuation">[</span>Any<span class="token punctuation">]</span>
raw<span class="token punctuation">:</span> Dict<span class="token punctuation">[</span><span class="token builtin">str</span><span class="token punctuation">,</span> Any<span class="token punctuation">]</span>

<span class="token keyword">class</span> <span class="token class-name">PlatformPlugin</span><span class="token punctuation">:</span>
name<span class="token punctuation">:</span> <span class="token builtin">str</span>

<span class="token keyword">def</span> <span class="token function">authenticate</span><span class="token punctuation">(</span>self<span class="token punctuation">)</span> <span class="token operator">-</span><span class="token operator">></span> <span class="token builtin">bool</span><span class="token punctuation">:</span>
<span class="token keyword">raise</span> NotImplementedError

<span class="token keyword">def</span> <span class="token function">receive_message</span><span class="token punctuation">(</span>self<span class="token punctuation">)</span> <span class="token operator">-</span><span class="token operator">></span> Message<span class="token punctuation">:</span>
<span class="token keyword">raise</span> NotImplementedError

<span class="token keyword">def</span> <span class="token function">send_message</span><span class="token punctuation">(</span>self<span class="token punctuation">,</span> msg<span class="token punctuation">:</span> Message<span class="token punctuation">)</span> <span class="token operator">-</span><span class="token operator">></span> <span class="token boolean">None</span><span class="token punctuation">:</span>
<span class="token keyword">raise</span> NotImplementedError

Slack 插件示例:

python
<span class="token keyword">class</span> <span class="token class-name">SlackPlugin</span><span class="token punctuation">(</span>PlatformPlugin<span class="token punctuation">)</span><span class="token punctuation">:</span>
name <span class="token operator">=</span> <span class="token string">"slack"</span>

<span class="token keyword">def</span> <span class="token function">authenticate</span><span class="token punctuation">(</span>self<span class="token punctuation">)</span> <span class="token operator">-</span><span class="token operator">></span> <span class="token builtin">bool</span><span class="token punctuation">:</span>
<span class="token comment"># 校验 bot token</span>
<span class="token keyword">return</span> <span class="token boolean">True</span>

<span class="token keyword">def</span> <span class="token function">receive_message</span><span class="token punctuation">(</span>self<span class="token punctuation">)</span> <span class="token operator">-</span><span class="token operator">></span> Message<span class="token punctuation">:</span>
<span class="token comment"># 从 Slack Events API 接收事件</span>
event <span class="token operator">=</span> <span class="token punctuation">{<!-- --></span>
<span class="token string">"channel"</span><span class="token punctuation">:</span> <span class="token string">"C123"</span><span class="token punctuation">,</span>
<span class="token string">"user"</span><span class="token punctuation">:</span> <span class="token string">"U123"</span><span class="token punctuation">,</span>
<span class="token string">"text"</span><span class="token punctuation">:</span> <span class="token string">"帮我总结一下今天的工作"</span>
<span class="token punctuation">}</span>

<span class="token keyword">return</span> Message<span class="token punctuation">(</span>
platform<span class="token operator">=</span><span class="token string">"slack"</span><span class="token punctuation">,</span>
conversation_id<span class="token operator">=</span>event<span class="token punctuation">[</span><span class="token string">"channel"</span><span class="token punctuation">]</span><span class="token punctuation">,</span>
user_id<span class="token operator">=</span>event<span class="token punctuation">[</span><span class="token string">"user"</span><span class="token punctuation">]</span><span class="token punctuation">,</span>
text<span class="token operator">=</span>event<span class="token punctuation">[</span><span class="token string">"text"</span><span class="token punctuation">]</span><span class="token punctuation">,</span>
attachments<span class="token operator">=</span><span class="token punctuation">[</span><span class="token punctuation">]</span><span class="token punctuation">,</span>
raw<span class="token operator">=</span>event
<span class="token punctuation">)</span>

<span class="token keyword">def</span> <span class="token function">send_message</span><span class="token punctuation">(</span>self<span class="token punctuation">,</span> msg<span class="token punctuation">:</span> Message<span class="token punctuation">)</span> <span class="token operator">-</span><span class="token operator">></span> <span class="token boolean">None</span><span class="token punctuation">:</span>
<span class="token comment"># 调用 Slack Web API 发送消息</span>
<span class="token keyword">print</span><span class="token punctuation">(</span><span class="token string-interpolation"><span class="token string">f"[Slack] send to </span><span class="token interpolation"><span class="token punctuation">{<!-- --></span>msg<span class="token punctuation">.</span>conversation_id<span class="token punctuation">}</span></span><span class="token string">: </span><span class="token interpolation"><span class="token punctuation">{<!-- --></span>msg<span class="token punctuation">.</span>text<span class="token punctuation">}</span></span><span class="token string">"</span></span><span class="token punctuation">)</span>

Telegram 插件示例:

python
<span class="token keyword">class</span> <span class="token class-name">TelegramPlugin</span><span class="token punctuation">(</span>PlatformPlugin<span class="token punctuation">)</span><span class="token punctuation">:</span>
name <span class="token operator">=</span> <span class="token string">"telegram"</span>

<span class="token keyword">def</span> <span class="token function">authenticate</span><span class="token punctuation">(</span>self<span class="token punctuation">)</span> <span class="token operator">-</span><span class="token operator">></span> <span class="token builtin">bool</span><span class="token punctuation">:</span>
<span class="token comment"># 校验 bot token</span>
<span class="token keyword">return</span> <span class="token boolean">True</span>

<span class="token keyword">def</span> <span class="token function">receive_message</span><span class="token punctuation">(</span>self<span class="token punctuation">)</span> <span class="token operator">-</span><span class="token operator">></span> Message<span class="token punctuation">:</span>
update <span class="token operator">=</span> <span class="token punctuation">{<!-- --></span>
<span class="token string">"message"</span><span class="token punctuation">:</span> <span class="token punctuation">{<!-- --></span>
<span class="token string">"chat"</span><span class="token punctuation">:</span> <span class="token punctuation">{<!-- --></span><span class="token string">"id"</span><span class="token punctuation">:</span> <span class="token string">"T_CHAT_123"</span><span class="token punctuation">}</span><span class="token punctuation">,</span>
<span class="token string">"from"</span><span class="token punctuation">:</span> <span class="token punctuation">{<!-- --></span><span class="token string">"id"</span><span class="token punctuation">:</span> <span class="token string">"T_USER_456"</span><span class="token punctuation">}</span><span class="token punctuation">,</span>
<span class="token string">"text"</span><span class="token punctuation">:</span> <span class="token string">"帮我查一下年假制度"</span>
<span class="token punctuation">}</span>
<span class="token punctuation">}</span>

msg <span class="token operator">=</span> update<span class="token punctuation">[</span><span class="token string">"message"</span><span class="token punctuation">]</span>

<span class="token keyword">return</span> Message<span class="token punctuation">(</span>
platform<span class="token operator">=</span><span class="token string">"telegram"</span><span class="token punctuation">,</span>
conversation_id<span class="token operator">=</span><span class="token builtin">str</span><span class="token punctuation">(</span>msg<span class="token punctuation">[</span><span class="token string">"chat"</span><span class="token punctuation">]</span><span class="token punctuation">[</span><span class="token string">"id"</span><span class="token punctuation">]</span><span class="token punctuation">)</span><span class="token punctuation">,</span>
user_id<span class="token operator">=</span><span class="token builtin">str</span><span class="token punctuation">(</span>msg<span class="token punctuation">[</span><span class="token string">"from"</span><span class="token punctuation">]</span><span class="token punctuation">[</span><span class="token string">"id"</span><span class="token punctuation">]</span><span class="token punctuation">)</span><span class="token punctuation">,</span>
text<span class="token operator">=</span>msg<span class="token punctuation">[</span><span class="token string">"text"</span><span class="token punctuation">]</span><span class="token punctuation">,</span>
attachments<span class="token operator">=</span><span class="token punctuation">[</span><span class="token punctuation">]</span><span class="token punctuation">,</span>
raw<span class="token operator">=</span>update
<span class="token punctuation">)</span>

<span class="token keyword">def</span> <span class="token function">send_message</span><span class="token punctuation">(</span>self<span class="token punctuation">,</span> msg<span class="token punctuation">:</span> Message<span class="token punctuation">)</span> <span class="token operator">-</span><span class="token operator">></span> <span class="token boolean">None</span><span class="token punctuation">:</span>
<span class="token comment"># 调用 Telegram sendMessage</span>
<span class="token keyword">print</span><span class="token punctuation">(</span><span class="token string-interpolation"><span class="token string">f"[Telegram] send to </span><span class="token interpolation"><span class="token punctuation">{<!-- --></span>msg<span class="token punctuation">.</span>conversation_id<span class="token punctuation">}</span></span><span class="token string">: </span><span class="token interpolation"><span class="token punctuation">{<!-- --></span>msg<span class="token punctuation">.</span>text<span class="token punctuation">}</span></span><span class="token string">"</span></span><span class="token punctuation">)</span>

Gateway Core:

python
<span class="token keyword">class</span> <span class="token class-name">Gateway</span><span class="token punctuation">:</span>
<span class="token keyword">def</span> <span class="token function">__init__</span><span class="token punctuation">(</span>self<span class="token punctuation">)</span><span class="token punctuation">:</span>
self<span class="token punctuation">.</span>plugins <span class="token operator">=</span> <span class="token punctuation">{<!-- --></span><span class="token punctuation">}</span>

<span class="token keyword">def</span> <span class="token function">register</span><span class="token punctuation">(</span>self<span class="token punctuation">,</span> plugin<span class="token punctuation">:</span> PlatformPlugin<span class="token punctuation">)</span><span class="token punctuation">:</span>
<span class="token keyword">if</span> plugin<span class="token punctuation">.</span>authenticate<span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">:</span>
self<span class="token punctuation">.</span>plugins<span class="token punctuation">[</span>plugin<span class="token punctuation">.</span>name<span class="token punctuation">]</span> <span class="token operator">=</span> plugin

<span class="token keyword">def</span> <span class="token function">handle_message</span><span class="token punctuation">(</span>self<span class="token punctuation">,</span> plugin_name<span class="token punctuation">:</span> <span class="token builtin">str</span><span class="token punctuation">)</span><span class="token punctuation">:</span>
plugin <span class="token operator">=</span> self<span class="token punctuation">.</span>plugins<span class="token punctuation">[</span>plugin_name<span class="token punctuation">]</span>

incoming <span class="token operator">=</span> plugin<span class="token punctuation">.</span>receive_message<span class="token punctuation">(</span><span class="token punctuation">)</span>

response_text <span class="token operator">=</span> self<span class="token punctuation">.</span>call_agent<span class="token punctuation">(</span>incoming<span class="token punctuation">)</span>

outgoing <span class="token operator">=</span> Message<span class="token punctuation">(</span>
platform<span class="token operator">=</span>incoming<span class="token punctuation">.</span>platform<span class="token punctuation">,</span>
conversation_id<span class="token operator">=</span>incoming<span class="token punctuation">.</span>conversation_id<span class="token punctuation">,</span>
user_id<span class="token operator">=</span>incoming<span class="token punctuation">.</span>user_id<span class="token punctuation">,</span>
text<span class="token operator">=</span>response_text<span class="token punctuation">,</span>
attachments<span class="token operator">=</span><span class="token punctuation">[</span><span class="token punctuation">]</span><span class="token punctuation">,</span>
raw<span class="token operator">=</span><span class="token punctuation">{<!-- --></span><span class="token punctuation">}</span>
<span class="token punctuation">)</span>

plugin<span class="token punctuation">.</span>send_message<span class="token punctuation">(</span>outgoing<span class="token punctuation">)</span>

<span class="token keyword">def</span> <span class="token function">call_agent</span><span class="token punctuation">(</span>self<span class="token punctuation">,</span> msg<span class="token punctuation">:</span> Message<span class="token punctuation">)</span> <span class="token operator">-</span><span class="token operator">></span> <span class="token builtin">str</span><span class="token punctuation">:</span>
<span class="token comment"># 这里可以调用真实 Agent Runtime</span>
<span class="token keyword">return</span> <span class="token string-interpolation"><span class="token string">f"Agent 收到你的消息:</span><span class="token interpolation"><span class="token punctuation">{<!-- --></span>msg<span class="token punctuation">.</span>text<span class="token punctuation">}</span></span><span class="token string">"</span></span>

使用:

python
gateway <span class="token operator">=</span> Gateway<span class="token punctuation">(</span><span class="token punctuation">)</span>
gateway<span class="token punctuation">.</span>register<span class="token punctuation">(</span>SlackPlugin<span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">)</span>
gateway<span class="token punctuation">.</span>register<span class="token punctuation">(</span>TelegramPlugin<span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">)</span>

gateway<span class="token punctuation">.</span>handle_message<span class="token punctuation">(</span><span class="token string">"slack"</span><span class="token punctuation">)</span>
gateway<span class="token punctuation">.</span>handle_message<span class="token punctuation">(</span><span class="token string">"telegram"</span><span class="token punctuation">)</span>

十四、这几类协议应该怎么选?

场景一:我想让 Claude / Cursor 调用我写的工具

用 MCP。

例如:

text
查数据库
查 Wiki
读文件
调用内部 API
查订单
查日志

你应该写:

text
xxx_mcp_server.py

暴露:

text
tools/list
tools/call

场景二:我想让 IDE 调用我的 coding agent

用 ACP。

例如:

text
帮我重构代码
帮我修 bug
帮我生成测试
帮我解释项目结构
帮我迁移接口

你应该实现:

text
initialize
session/new
session/prompt
session/update
session/cancel

场景三:我想让 Agent 接入微信、Slack、Telegram

用 Gateway Plugin。

例如:

text
企业微信 AI 助手
Slack 工作流助手
Telegram 私人助理
Discord 社区机器人

你应该设计:

text
PlatformPlugin
receive_message
send_message
authenticate

场景四:我想给 IDE 做代码补全、跳转、诊断

用 LSP。

例如:

text
自研 DSL 的语法检查
SQL 编辑器智能提示
配置文件 schema 校验
工作流 DSL 补全

你应该实现:

text
initialize
textDocument/didOpen
textDocument/didChange
textDocument/completion
textDocument/hover
textDocument/publishDiagnostics

十五、总结

AI Agent 时代,协议会变得越来越重要。

因为我们不再只是做一个“调用大模型的应用”,而是在构建一个复杂生态:

text
AI Client
IDE
Agent
Tool
Resource
Prompt
File System
Chat Platform
Enterprise System

这些组件之间如果没有统一协议,就会变成大量脆弱的胶水代码。

本文介绍的几个协议和架构,可以这样理解:

text
JSON-RPC:消息格式底座
stdio / HTTP / SSE:传输方式
LSP:IDE 调语言能力
MCP:AI 调外部工具
ACP:IDE 调 AI Agent
Gateway Plugin:聊天平台接入 Agent

它们之间不是替代关系,而是分工关系。

最终可以总结成一句话:

LSP 解决“编辑器如何使用语言能力”,MCP 解决“模型如何使用外部工具”,ACP 解决“IDE 如何驱动 Agent”,Gateway Plugin 解决“不同聊天平台如何接入 Agent”。

如果你正在做企业级 Agent、AI IDE、内部知识库助手、OA 智能助手、代码智能体平台,这几个协议和架构思想都非常值得深入理解。

因为未来复杂 Agent 系统的竞争,不只是模型能力的竞争,更是:

text
协议设计能力
工具生态能力
上下文组织能力
多端接入能力
工程化落地能力

谁能把这些能力标准化、模块化、插件化,谁就更容易构建真正可扩展的 AI Agent 平台。

基于 VitePress 构建