第2章:MCP协议栈分析

好,咱们直接进入正题。MCP协议栈,说白了就是一套通信的规矩。你想想看,两个程序要对话,总得有个共同语言吧?MCP就是那个翻译官。我这些年做逆向分析,见过太多协议实现上的坑,MCP的设计其实挺巧妙的,但理解不透照样翻车。

2.1 MCP协议分层模型

MCP协议栈分四层,从下往上分别是:传输层、会话层、表示层、应用层。嗯,这里要注意,它跟OSI七层模型不太一样,MCP更轻量,专为AI工具链设计。

核心分层结构:

  • 传输层:负责原始字节流的收发。我习惯用WebSocket或HTTP/2作为底层载体,实际项目中WebSocket更常见。
  • 会话层:管理连接的生命周期。包括握手、心跳、重连。我曾经遇到过一个坑,会话超时时间设得太短,导致频繁断连。
  • 表示层:数据序列化与反序列化。MCP默认用JSON,但支持Protobuf扩展。我个人建议生产环境用Protobuf,性能差不少。
  • 应用层:业务逻辑。比如工具调用、资源访问、提示模板管理。

下面这张图是我手绘的分层模型,你一看就明白:

应用层(工具调用、资源访问) 表示层(JSON/Protobuf序列化) 会话层(握手、心跳、重连) 传输层(WebSocket/HTTP/2)

2.2 MCP协议数据包结构

MCP的数据包结构其实不复杂。每个包都是一个JSON对象,包含三个核心字段:typeidpayload。我刚开始逆向MCP流量时,就是靠这三个字段定位问题的。

{
  "type": "request",      // 消息类型:request/response/notification/error
  "id": "msg-001",        // 消息唯一标识,用于请求-响应配对
  "payload": {            // 具体业务数据
    "method": "tools/call",
    "params": {
      "name": "calculator",
      "arguments": {
        "a": 10,
        "b": 20
      }
    }
  }
}

我的小技巧:做逆向分析时,先抓包看type字段。如果是notification,说明是单向消息,不需要回复。如果是request,一定要找到对应的response,否则说明连接有问题。

数据包头部还有几个隐藏字段,在传输层会附加:

字段 长度 说明
magic 4字节 固定为0x4D4350(即"MCP")
version 2字节 主版本号+次版本号,当前为1.0
length 4字节 payload长度(大端序)
checksum 2字节 CRC16校验,可选字段

注意:我曾经在分析一个MCP实现时,发现对方把length字段解析错了——用了小端序。结果整个数据包解析全乱套。所以做协议逆向时,字节序一定要先确认清楚。

2.3 MCP协议握手流程

握手是MCP连接的第一步,说白了就是双方互相确认身份和能力。我见过不少开发者跳过握手直接发业务请求,结果被服务端无情拒绝。

标准握手流程分三步:

  1. 客户端发送初始化请求:包含协议版本、客户端名称、支持的能力列表。
  2. 服务端返回初始化响应:确认版本、返回服务端能力、分配会话ID。
  3. 客户端发送初始化完成通知:告诉服务端"我准备好了"。
// 步骤1:客户端 -> 服务端
{
  "type": "request",
  "id": "init-1",
  "payload": {
    "method": "initialize",
    "params": {
      "protocolVersion": "1.0",
      "clientInfo": {
        "name": "my-ai-agent",
        "version": "1.0.0"
      },
      "capabilities": {
        "tools": true,
        "resources": true,
        "prompts": false
      }
    }
  }
}

// 步骤2:服务端 -> 客户端
{
  "type": "response",
  "id": "init-1",
  "payload": {
    "protocolVersion": "1.0",
    "serverInfo": {
      "name": "mcp-server",
      "version": "2.1.0"
    },
    "capabilities": {
      "tools": true,
      "resources": false
    },
    "sessionId": "sess-abc123"
  }
}

// 步骤3:客户端 -> 服务端
{
  "type": "notification",
  "payload": {
    "method": "initialized"
  }
}

握手完成后,双方才能开始正常的业务通信。如果版本不匹配,服务端会返回错误,客户端需要降级或断开连接。

2.4 MCP协议状态机

MCP连接的状态机其实不复杂,但理解它对于排查问题特别重要。我画了个状态转换图,你一看就懂:

CLOSED HANDSHAKE ACTIVE CLOSING connect initialized close() timeout / error

状态说明:

  • CLOSED:初始状态,连接未建立。此时任何业务请求都会被丢弃。
  • HANDSHAKE:正在握手。客户端发送initialize后进入此状态。我遇到过一个问题:客户端发了握手请求但没收到响应,一直卡在这里。后来发现是防火墙把WebSocket升级请求拦截了。
  • ACTIVE:正常通信状态。可以发送工具调用、资源请求等。这是最常用的状态。
  • CLOSING:正在关闭连接。收到close通知或发生超时后进入。此时不再处理新请求,但会尽力完成正在处理的请求。

避坑指南:我曾经在分析一个MCP客户端时,发现它从HANDSHAKE状态直接跳到了CLOSED,中间没有经过CLOSING。查了半天,原来是握手超时处理写错了,直接抛异常关闭了socket。正确的做法是先发一个error通知,再优雅关闭。

状态机里还有一个隐藏规则:在ACTIVE状态下,如果连续3次心跳没有收到回复,应该自动进入CLOSING状态。这个机制是为了防止死连接占用资源。我建议你在实现时把这个阈值做成可配置的,不同网络环境需求不一样。

好了,MCP协议栈的核心内容就这些。分层模型让你知道每一层干什么,数据包结构让你能读懂网络流量,握手流程确保双方能正常沟通,状态机帮你理解连接的生命周期。把这四个点吃透了,MCP协议对你来说就没有秘密了。

专注资料整理