1. 为什么需要 MCP 事件
到目前为止,ChatGPT 与你的后端之间的交互几乎都是 RPC:模型调用工具,工具做点事并返回结果——搞定。对于 200–500 ms、最多几秒的短操作,这很方便。
但一旦出现长耗时任务——例如为 GiftGenius 分析一份很大的员工偏好文件、从一堆外部 API 聚合推荐、重算一个庞大的 feed——事情就变得不那么美妙。HTTP 超时、函数重启、永远转的加载动画,用户坐在那里猜:“它还活着,还是已经挂了?”
这时事件模型就该登场了。与其维持一次长时间的工具调用,不如启动一个任务,拿到 jobId,接下来由服务器主动推送事件:已开始、进度更新、完成、失败。MCP 中的这些事件实现为 JSON-RPC notifications——单向消息,没有 id,不期待响应。
重要的一点:事件不是“线上 console.log”。它是具有固定模式的协议消息,你的 UI(小部件)和/或代理必须像处理工具调用结果那样严谨地处理它。
回顾:MCP 中的消息类型
在继续之前,先简要回顾一下 MCP 里到底有哪些消息类型。
撇开营销层不谈,MCP 基于 JSON-RPC 2.0。它有三类基础消息:请求、响应和通知。
不赘述列表,我们直接看一张小对比表:
| 类型 | 字段 id | 谁发起 | 是否期待响应? | MCP 中的示例 |
|---|---|---|---|---|
| Request | 有 | 通常为客户端(ChatGPT) | 是 | 调用工具 tools/call |
| Response | 有 | MCP 服务器 | 这本身就是响应 | tools/call 的结果 |
| Notification | 无 | 客户端或服务器 | 否 | notifications/progress、resources/updated、logging/message |
MCP 事件正对应第三行:notifications。其特征:
- 顶层没有 id——不会有 result 或 error 作为回应;
- 发起方不等待 ACK——在协议层面“发射即忘”;
- 可靠性不是靠确认,而是靠处理器的幂等性与重试策略来构建。
重要限制:MCP 事件不会“随时随地在太空中乱飞”。它们存在于已建立的 MCP 连接之内,承载在具体的传输之上。最常见的是类似 SSE 的流(传输细节及其变体我们会在单独一讲中讨论)。
2. 什么是“实际的 MCP 事件”
从形式上说,MCP 事件就是 JSON-RPC notification,即如下对象:
{ "jsonrpc": "2.0", "method": "notifications/job/progress", "params": { "jobId": "job_123", "percentage": 30, "stage": "在目录中查找备选", "eventId": "evt_abc123", "timestamp": "2025-11-21T10:15:00Z" } }这里有几个要点:
- 在 method 字段中,我们编码事件类型及其“命名空间”。MCP 已经定义了一些标准的 notifications/... 方法用于日志、进度与资源变更,但你可以、也应该添加自己的业务专用方法,比如 notifications/job/progress 或 notifications/job/completed。
- 所有业务数据位于 params。我们会在那里保存任务标识(jobId)、事件唯一 ID(eventId)、时间(timestamp)、人类可读的消息等。
- 顶层没有 id 字段——这正是它是 notification 的原因。协议不提供对此的响应。如果服务器想知道“对方是否理解了”,可以再发一个事件,或等待客户端的后续反应(例如新的请求)。但就 JSON-RPC 而言,没有 ACK。
在心智模型上可以这样理解:工具调用 tools/call 是“你等待回复的信件”,而事件则是“来自 Slack 机器人的通知:“后台任务 #123 已完成””。
3. 事件分类:有哪些通知
如果只是放开“随便把 JSON 当 notifications 发”,两周后系统就会变成垃圾场:事件名称各不相同、字段到处漂移、UI 不知道如何处理。因此最好先约定一个小而清晰的分类法。
下面是一种与 MCP 规范以及 ChatGPT Apps 实际用例非常契合的分类方式。
作业生命周期事件(Job Lifecycle)
这类事件反映任务状态的关键跃迁。通常任务有一个状态机(state machine),如 pending → running → (completed | failed | canceled)。
典型事件:
- job.created —— 任务已注册;
- job.started —— worker 已开始执行;
- job.completed —— 任务成功完成;
- job.failed —— 任务失败并带错误;
- job.canceled