传输层-MCP的搭建(一)

学习MCP的几个重要的点如下:

  • 核心架构
  • 资源(Reason)
  • 工具(Tool)
  • 采样(Sampling)
  • 根(Root)
  • 传输(Transports)

今天我们来说一下MCP的传输层,MCP的传输层是用来处理客户端和服务器之间的实际通信。

所有的传输都使用JSON-RPC 2.0来交换消息,JSON-RPC消息必须使用UTF-8编码

MCP协议目前有三种通信方式,准确的来说是2种,还有1种是为了兼容版本使用的

  1. stdio:使用标准的输入和输出流进行通信,对于本地集成命令行工具特别有用
  2. 流式HTTP:通过 HTTP 进行通信,支持流式传输,协议版本:2025-03-26
  3. SSE:通过 HTTP 进行通信,支持流式传输。(协议版本 2024-11-05 开始支持,即将废弃)

一、stdio传输

🌵🌵sudio传输的利弊:

主要优势:

  • 无外部依赖
  • 进程间通信极快
  • 结构简单,调试容易

缺点:

  • 不支持并发和多对多通信

🏂 使用场景:

  • 构建命令行工具
  • 实现本地集成
  • 简单的进程通信
  • 使用Shell脚本

🧐运行流程如下:

  1. 客户端以子进程的方式启动服务器
  2. 客户端往服务器的 stdin 写入消息
  3. 服务器从自身的 stdin 读取消息
  4. 服务端往自身的 stdout 写入消息
  5. 客户端从服务器的 stdout 读取消息
  6. 客户端终止子进程,关闭服务器的 stdin
  7. 服务器关闭自身的 stdout

🐳🐳 接下来,我们使用两种方式来简单实用一个stdio的传输案例

第一种:使用简单的stdio中的分隔符来切割消息

为什么要使用分割符来切割消息呢?

因为不手动切割消息的话,消息是会"粘"在一起的,不会因为你写两条child.stdin.write('hello world \n')就自动帮你分割

原因如下: Node.js 的流(Stream)处理机制。当你使用 stdin.write() 连续写入数据时,这些数据可能会被操作系统的缓冲区合并成一个数据块。当缓冲区满了或者有其他触发条件时,这些数据才会被一次性发送给子进程。

这就像水管一样:你往水管里倒水,但水不会立即从另一端流出,而是会积累到一定量后一起流出。

创建两个js文件:parent.js和child.js文件

javascript 复制代码
//child.js文件
import process from "process";

process.stdin.setEncoding('utf8');

//接收父进程输入的数据-简单处理
process.stdin.on('data', (data) => {
    const trimmed = data.trim()// 父进程

    // 子进程
    let buffer = '';
    buffer += data;
    const messages = buffer.split('\n');
    buffer = messages.pop(); // 保留不完整的消息
    for (const msg of messages) {
        if (msg) {
            const response = `[子进程处理] ${msg.toUpperCase()}`;
            process.stdout.write(response + '\n');
        }
    }
});
javascript 复制代码
//parent.js文件
import { spawn } from 'child_process';
import { fileURLToPath } from 'url';
import { dirname, join } from 'path';


//简单处理
const __filename = fileURLToPath(import.meta.url);
const __dirname = dirname(__filename);

//运行子进程的命令
const child = spawn('node', [join(__dirname, 'child.js')], {
    stdio: ['pipe', 'pipe', 'inherit']
})

//接收子进程输出的数据传输到父进程来
child.stdout.on('data', (data) => {
    console.log('父进程接收到:', data.toString().trim())
})

//运行命令
child.stdin.write('hello world \n')
child.stdin.write('from js parent \n')


setTimeout(() => {
    child.stdin.end()
}, 1000)

输出结果


第二种:使用IPC机制来实现消息的分隔

进程间通信(IPC)机制,它是一种更高级的数据传输方式,Node中使用的fork来运行子进程文件的。

优点是:

  1. 可以发送结构化的 JavaScript 对象(会自动序列化和反序列化)
  2. 不需要手动处理消息边界
  3. 更加清晰和易于维护

缺点是:

  1. 只能用于 JavaScript 子进程,不能用于其他可执行文件
  2. 有一定的性能开销,不适合传输大量数据
javascript 复制代码
//parentIPC.js
import { fork } from 'child_process';
import { fileURLToPath } from 'url';
import { dirname, join } from 'path';

const __filename = fileURLToPath(import.meta.url);
const __dirname = dirname(__filename);

const child = fork(join(__dirname, 'childIPC.js'));

// 发送结构化的消息
child.send({ text: 'hello world' });
child.send({ text: 'from js parent' });

// 接收子进程的消息
child.on('message', (message) => {
    console.log('父进程接收到:', message.response);
});

setTimeout(() => {
    child.disconnect(); // 关闭 IPC 通道
}, 1000);
javascript 复制代码
//childIPC.js
process.on('message', (message) => {
    const response = `[子进程处理IPC] ${message.text.toUpperCase()}`;
    // 发送结构化的响应回父进程
    process.send({ response });
});

输出结果和上面一样

二、流式HTTP

  • 服务器是独立进程,可同时处理多个客户端连接。
  • 使用 HTTP POST 和 GET 进行通信。
  • 支持 服务器发送事件(SSE) 实现流式传输。
  • 允许服务端主动向客户端推送消息(而不是等客户端轮询)
  • 可用于 基本 MCP 通信
  • 支持更高级功能:流式传输 + 服务端通知 + 请求回传

在可流式 HTTP 传输中,服务器作为一个独立进程运行,可以处理多个客户端连接此传输使用 HTTP POST 和 GET 请求 。服务器可以选择使用服务器发送事件(SSE)来流式传输多个服务器消息。这允许基本的 MCP 服务器,以及支持流式传输和服务器到客户端通知和请求的更丰富功能的服务器。

服务器必须提供一个支持 POST 和 GET 方法的单个 HTTP 端点路径(以下称为 MCP 端点)。例如,这可能是一个类似于 https://example.com/mcp 的 URL。

流式HTTP的消息交互,客户端和服务端之间的会有多种方式,我们从客户端的角度来解读理解更好

MCP 客户端可以使用 GET 或者 POST 请求给服务器发消息,每个请求必须设置请求头 Accept,传递以下两个值:

  • application/json 接收服务器响应的 JSON-RPC 编码消息
  • text/event-stream 由服务器开启流式传输通道,客户端从这个流里面读取事件消息

所以客户端有两种请求服务端的方式,一种的是向服务端发送消息(POST的JSON-RPC的请求),另外一种监听来自服务端的消息(GET的SSE流的服务端推送)

形象的区别就是:谁说话?谁听?

  • GET 是客户端说:"我开始听了"
    • 服务器可以随时说话(推送消息)
    • 客户端需要被动响应(尤其是带 id 的 JSON-RPC 请求)
  • POST 是客户端说:"我有事要说"
    • 主动发送请求、通知、响应(给之前的 JSON-RPC 请求回应)

🎉🎉两种在职责上面是清晰的,GET用于监听,POST用于交互

1、向服务端发送消息

  • 客户端必须要使用POST 的请求方式发送JSON-RPC消息
  • 客户端必须包含一个Accept头,列出 application/jsontext/event-stream作为支持的内容类型。
  • 客户端POST请求的正文必须有以下之一,批量处理可以多个调用合并为一个POST达到减少网络请求数
    • 单个JSON-RPC请求、通知、响应
    • 包含一个或多个请求或响应的数组批量处理
    • 包含一个或多个响应的数组批量处理
  • 当客户端发送的是 [ 通知 ],那么服务端只需要确认收到,
    • 服务端处理成功:HTTP 202 Accepted -服务端处理失败:HTTP 400 Bad Request
  • 当客户端发送的是 [ 请求 ],那么服务端有两种响应方式
    • 同步响应:响应单条JSON
javascript 复制代码
Content-Type: application/json
{
  "jsonrpc": "2.0",
  "result": "ok",
  "id": 1
}
css 复制代码
- <font style="color:rgb(62, 62, 62);">异步流式响应(SSE):流式推送消息结果</font>
javascript 复制代码
Content-Type: text/event-stream
data: {"jsonrpc": "2.0", "result": "ok", "id": 1}
  • 流式响应,有一些规范需要注意:
    • 每个请求都要有对应的响应
    • 响应之前可以先发送消息,但是不能跳过最终响应
    • 所有响应发完之后记得关闭
    • 因为网络问题产生的"断开"行为,不表示"取消",服务端是不能理解为客户端取消,客户端必须要主动明确的发送取消通知
javascript 复制代码
{
  "jsonrpc": "2.0",
  "method": "MCP.CancelledNotification",
  "params": { "id": 99 }
}
  • 支持"可恢复"流:防止消息丢失,需要手动实现响应的恢复逻辑,实现方式可以参考如下:
    • 服务器可以在其 SSE 事件中附加一个 id字段
    • 如果客户端希望在断开连接后恢复,它应该向 MCP 端点发出 HTTP GET 请求,并包含 Last-Event-ID 头信息以指示它接收到的最后一个事件 ID

2、监听来自服务端的消息

客户端可以跳过HTTP的GET方式请求建立一个"接收型"SSE连接,用于监听服务端主动推送的消息

  • 客户端通过GET打开一个SSE流
  • 请求头中必须带Accept头
  • 服务端响应方式必须二选一:支持SSE/返回结果(Content-Type: text/event-stream) 或者 不支持SSE/直接拒绝(HTTP 405 Method Not Allowed
  • 服务端可以推送两类消息:通知 | 请求,都是JSON-RPC格式的

三、总结

🌲🌲🌲 关键点回顾:

  • 所有传输方式都基于 JSON-RPC 2.0 协议,UTF-8 编码
  • MCP 支持三种传输方式:stdio(本地通信)、Streamable HTTP(推荐)、SSE(向后兼容)
  • stdio:简单高效,适合 CLI 与本地代理
  • 流式 HTTP :结合 POSTSSE,支持多客户端、多种通信模式,是真正的现代通信核心
  • 客户端通过 POST 发出请求,通过 GET 建立监听
  • JSON-RPC 请求支持批处理、通知、响应的组合,具有强大的表达能力
  • 断线重连机制 (基于 Last-Event-ID)保障了流的可靠性与恢复性
  • 取消行为需显式声明,不可依赖连接断开隐含推理
相关推荐
希望永不加班29 分钟前
Spring AOP 代理模式:CGLIB 与 JDK 动态代理区别
java·开发语言·后端·spring·代理模式
2301_7990730232 分钟前
基于 Next.js + 火山引擎 AI 的电商素材智能生成工具实战——字节跳动前端训练营成果
javascript·人工智能·火山引擎
浮游本尊1 小时前
一次合同同步背后的多阶段流水线:从外部主数据到本地歧义消解
后端
lv__pf1 小时前
springboot原理
java·spring boot·后端
段小二2 小时前
服务一重启全丢了——Spring AI Alibaba Agent 三层持久化完整方案
java·后端
UIUV2 小时前
Go语言入门到精通学习笔记
后端·go·编程语言
lizhongxuan2 小时前
开发 Agent 的坑
后端
段小二2 小时前
Agent 自动把机票改错了,推理完全正确——这才是真正的风险
java·后端
itjinyin3 小时前
ShardingSphere-jdbc 5.5.0 + spring boot 基础配置 - 实战篇
java·spring boot·后端