我的 MCP 学习之旅:从困惑到理解

记录我在学习 Model Context Protocol (MCP) 过程中的思考、疑问和收获

前言

最近在开发 AgentCrew 项目时,第一次深入接触了 MCP(Model Context Protocol)。作为一个优化"氛围式编程"(Vibe Coding)反馈循环的工具,我需要让 Cursor 能够通过 MCP 协议与我的 Agent 系统交互。

在这个过程中,我遇到了很多疑问,也学到了很多。这篇文章记录了我的学习历程,希望能帮助到同样在学习 MCP 的朋友。

第一个困惑:MCP 到底是什么?

最初的误解

刚开始,我以为 MCP 就是一个 npm 包,就像其他工具库一样,安装就能用。但当我看到配置文件中需要指定 index.js 路径时,我困惑了:

json 复制代码
{
  "mcpServers": {
    "agentcrew": {
      "command": "node",
      "args": ["D:\\Code\\agentCrew\\src\\index.js"]
    }
  }
}

为什么需要文件路径?为什么不能像其他工具一样直接使用?

理解:MCP 是协议,不是包

经过深入学习,我明白了:

MCP = Model Context Protocol(模型上下文协议)

  • MCP 是协议标准,类似 HTTP 协议
  • MCP 服务器是实现,类似 HTTP 服务器(Express、Nginx 等)
  • npm 包只是分发方式,不是 MCP 的本质

类比理解:

复制代码
HTTP 协议(标准)
  ↓
HTTP 服务器(实现)
  - Express(npm 包)
  - Nginx(系统服务)
  - Apache(系统服务)

MCP 协议(标准)
  ↓
MCP 服务器(实现)
  - @modelcontextprotocol/server-github(npm 包)
  - src/index.js(本地文件)
  - https://api.example.com/mcp(远程服务)

这个理解让我豁然开朗:MCP 是一个开放标准,任何人都可以实现自己的 MCP 服务器。

第二个困惑:为什么需要 index.js 路径?

疑问

既然 MCP 是协议,为什么配置文件中还需要指定本地文件路径?这让我觉得 MCP 似乎只能本地使用。

理解:stdio 传输方式

原来,MCP 支持多种传输方式:

  1. stdio(标准输入输出) - 本地进程

    • 配置:command + args
    • 适用:本地开发的 MCP 服务器
  2. SSE(Server-Sent Events) - 远程 HTTP 服务

    • 配置:url
    • 适用:第三方提供的远程服务
  3. WebSocket - 实时通信

    • 配置:url (wss://)
    • 适用:需要双向实时通信的场景

AgentCrew 使用 stdio 方式:

  • Cursor 启动本地 Node.js 进程
  • 通过 stdin/stdout 通信
  • 不需要网络端口
  • 数据不离开本地(隐私更好)

工作流程:

复制代码
Cursor 启动
  ↓
执行:node D:\Code\agentCrew\src\index.js
  ↓
启动 MCP 服务器进程
  ↓
通过 stdin/stdout 通信

第三个困惑:为什么不用简单的 HTTP RPC?

疑问

既然 LLM 是同步等待工具结果的,为什么不直接用简单的 HTTP RPC?每次调用一个 API,等待结果,继续生成。这样不是更简单吗?

理解:长连接的优势

虽然简单场景下,HTTP RPC 确实更简单,但 MCP 选择长连接有更深层的原因:

1. 流式响应

HTTP RPC:

复制代码
调用工具
  ↓
[等待 30 秒...]
  ↓
返回完整结果

MCP 长连接:

复制代码
调用工具
  ↓
工具: "开始处理..."(立即返回)
工具: "已处理 50%..."(推送进度)
工具: "完成"(最终结果)
2. 会话保持

HTTP RPC:

  • 每次请求都是独立的
  • 需要客户端管理状态
  • 需要重复传递上下文

MCP 长连接:

  • 会话状态自动维护
  • 工具调用之间共享上下文
  • 不需要重复传递
3. 统一协议

即使简单工具不需要复杂特性,统一协议也便于:

  • 扩展和管理
  • 未来添加新功能
  • 统一接口

类比: HTTP/1.1 也可以工作,但 HTTP/2 提供了更多能力。统一协议更有利于扩展。

第四个困惑:MCP vs Function Call

疑问

大模型一般还提供一个能力:function call 是支持 RPC 的。MCP 和 function call 的定位不一样吗?为什么 GitHub 这种调用不走 function call 而要走 MCP?

理解:两种不同的能力

Function Call(函数调用):

  • LLM 内置能力
  • 简单的 HTTP REST API 调用
  • 适合简单的请求-响应场景
  • 无状态,每次调用独立

MCP(Model Context Protocol):

  • 标准化协议
  • 支持复杂的交互模式
  • 支持流式响应、会话保持、主动推送
  • 完整的上下文管理

对比表:

特性 Function Call MCP
定位 LLM 内置能力 标准化协议
流式响应 ❌ 不支持 ✅ 支持
会话保持 ❌ 无状态 ✅ 有状态
主动推送 ❌ 不支持 ✅ 支持
标准化 ❌ 各厂商不同 ✅ 统一标准

为什么 GitHub 选择 MCP?

  1. 统一标准:所有工具使用统一协议
  2. 未来扩展:支持更复杂的交互模式
  3. 更好集成:统一的接口和管理
  4. 会话管理:支持多步骤操作

两者的关系:

  • 不是替代关系,而是互补关系
  • Function Call 适合简单的 API 调用
  • MCP 适合复杂的工具交互

第五个困惑:为什么不用 YAML 配置文件?

疑问

既然 MCP 服务器可以通过 npm 包提供,为什么不用 YAML 等配置文件来定义工具能力?这样不是更简单吗?

理解:协议 vs 配置文件

YAML 是配置文件格式,MCP 是通信协议!

YAML 的局限性:

  • ❌ 无法实时交互
  • ❌ 无法流式响应
  • ❌ 无法双向通信
  • ❌ 无法会话管理
  • ❌ 无法动态执行

MCP 协议的优势:

  • ✅ 实时交互
  • ✅ 流式响应
  • ✅ 双向通信
  • ✅ 会话管理
  • ✅ 动态执行(执行代码、访问数据库等)

类比理解:

YAML(类似 API 文档):

yaml 复制代码
# tools.yaml
tools:
  - name: parse_prd
    description: 解析 PRD

MCP(类似 HTTP 协议):

javascript 复制代码
// MCP 服务器实现
server.setRequestHandler(CallToolRequestSchema, async (request) => {
  if (request.params.name === 'parse_prd') {
    // 执行解析逻辑
    const result = await parsePRD(request.params.args);
    // 流式返回
    return {
      content: [
        { type: 'text', text: '开始解析...' },
        { type: 'text', text: '解析完成' },
        { type: 'text', text: JSON.stringify(result) }
      ]
    };
  }
});

关键理解:

  • YAML 是静态配置,无法执行
  • MCP 是动态协议,可以执行
  • 配置文件无法替代协议

第六个困惑:MCP 支持哪些数据格式?

疑问

通过 stdio 使用 MCP 时,协议支持哪些形式的数据提供给 agent?比如流式数据?或者是 JSON 形式的结果数据?

理解:MCP 的数据格式

基本响应格式:

typescript 复制代码
{
  content: Array<{
    type: 'text' | 'image' | 'resource',
    text?: string,        // 文本内容
    data?: string,        // Base64 编码的数据(用于图片等)
    mimeType?: string,    // MIME 类型
    uri?: string          // 资源 URI
  }>,
  isError?: boolean      // 是否为错误响应
}

支持的格式:

  1. JSON 格式(推荐)

    javascript 复制代码
    {
      content: [{
        type: 'text',
        text: JSON.stringify({
          session_id: 'default',
          data: { ... }
        }, null, 2)
      }]
    }
  2. 纯文本格式

    javascript 复制代码
    {
      content: [{
        type: 'text',
        text: '纯文本内容'
      }]
    }
  3. 流式响应(模拟)

    javascript 复制代码
    {
      content: [
        { type: 'text', text: '步骤1...' },
        { type: 'text', text: '步骤2...' },
        { type: 'text', text: '完成' }
      ]
    }

注意: 在 stdio 传输中,所有 content 项会一次性返回。真正的流式推送需要 SSE 或 WebSocket。

实际代码示例(AgentCrew):

javascript 复制代码
// src/index.js - handleParsePRD 方法
async handleParsePRD(prdContent, sessionId) {
  const parsed = await this.requirementParser.parse(prdContent);
  
  return {
    content: [
      {
        type: 'text',
        text: JSON.stringify({
          session_id: sessionId,
          requirements: parsed,
          summary: {
            features_count: parsed.features.length,
            user_stories_count: parsed.userStories.length,
          }
        }, null, 2)  // 格式化 JSON,缩进 2 空格
      }
    ]
  };
}

Transformer 架构的影响

理解 LLM 的行为

在学习过程中,我深入了解了 Transformer 架构,这帮助我理解了为什么 MCP 这样设计:

Transformer 的核心特点:

  • 顺序生成:token 按顺序生成
  • 上下文依赖:每个 token 依赖前面的所有 token
  • 同步等待:必须等待前一个 token 才能生成下一个

工具调用时的行为:

复制代码
LLM 生成: "我将调用 GitHub 工具"
  ↓
生成工具调用 token: { "tool": "github_search" }
  ↓
【暂停生成】← Transformer 停止生成新 token
  ↓
等待工具结果
  ↓
收到结果后,继续生成: "根据查询结果..."

关键理解:

  • LLM 确实是同步的
  • 工具调用时确实会暂停生成
  • 必须等待工具结果才能继续

流式响应时 LLM 的行为:

  • LLM 会一直等待直到收到最终结果
  • LLM 不会异步处理其他内容
  • 但可以逐步接收流式消息,提供更好的反馈

这个理解让我明白了为什么 MCP 需要支持流式响应:即使 LLM 是同步等待的,流式响应也能提供更好的用户体验。

实际开发中的收获

1. 会话管理的重要性

在开发 AgentCrew 时,我实现了会话管理功能:

javascript 复制代码
// 会话存储
this.sessions = new Map();

// 获取或创建会话
getSession(sessionId) {
  if (!this.sessions.has(sessionId)) {
    this.sessions.set(sessionId, {
      id: sessionId,
      requirements: null,
      tasks: null,
      generatedCode: [],
      feedbackHistory: [],
      createdAt: new Date().toISOString(),
    });
  }
  return this.sessions.get(sessionId);
}

这让我深刻理解了 MCP 会话管理的价值:

  • 多步骤操作可以共享上下文
  • 不需要重复传递数据
  • 更好的用户体验

2. 数据格式的选择

在实现过程中,我选择了 JSON 格式作为主要数据格式:

javascript 复制代码
return {
  content: [{
    type: 'text',
    text: JSON.stringify(data, null, 2)
  }]
};

原因:

  • ✅ LLM 可以轻松解析
  • ✅ 结构清晰
  • ✅ 易于扩展
  • ✅ 格式化后更易读

3. 错误处理

MCP 协议支持错误标记:

javascript 复制代码
return {
  content: [{
    type: 'text',
    text: JSON.stringify({
      error: true,
      message: error.message
    }, null, 2)
  }],
  isError: true  // 标记为错误
};

这让我意识到协议设计的人性化:不仅支持成功响应,还明确支持错误响应。

学习过程中的思考

1. 协议设计的智慧

MCP 的设计体现了几个重要的设计原则:

统一性:

  • 所有工具使用统一的协议
  • 便于管理和扩展
  • 降低学习成本

灵活性:

  • 支持多种传输方式(stdio/SSE/WebSocket)
  • 支持多种数据格式(text/image/resource)
  • 适应不同场景

可扩展性:

  • 协议层和实现层分离
  • 易于添加新功能
  • 未来兼容性好

2. 为什么需要协议?

在学习过程中,我反复思考:为什么需要协议?为什么不能直接用配置文件?

我的理解:

配置文件(YAML):

  • 静态定义
  • 无法执行
  • 无法交互

协议(MCP):

  • 动态交互
  • 可以执行
  • 实时通信

类比:

  • API 文档 vs API 协议
  • 函数签名 vs 函数实现
  • HTML vs HTTP

协议定义了"如何交互",而不仅仅是"有什么能力"。

3. 长连接 vs 短连接

为什么 MCP 选择长连接而不是简单的 HTTP RPC?

我的理解:

虽然简单场景下,HTTP RPC 确实更简单,但长连接提供了:

  • 流式响应(更好的用户体验)
  • 会话保持(多步骤操作)
  • 主动推送(实时反馈)
  • 低延迟(连接复用)

类比: HTTP/1.1 vs HTTP/2,虽然 HTTP/1.1 可以工作,但 HTTP/2 提供了更多能力。

总结:我的 MCP 学习收获

核心理解

  1. MCP 是协议标准,不是 npm 包

    • 类似 HTTP 协议
    • 可以有多种实现方式
    • 开放标准,任何人都可以实现
  2. MCP 支持多种传输方式

    • stdio(本地进程)
    • SSE(远程 HTTP 服务)
    • WebSocket(实时通信)
  3. MCP 的优势

    • 流式响应
    • 会话保持
    • 双向通信
    • 统一标准
  4. MCP vs Function Call

    • 不是替代关系,而是互补关系
    • Function Call 适合简单场景
    • MCP 适合复杂场景
  5. 协议 vs 配置文件

    • 配置文件是静态的
    • 协议是动态的
    • 协议可以执行,配置文件不能

实际应用

在开发 AgentCrew 的过程中,MCP 让我能够:

  • 实现复杂的多步骤工作流
  • 维护会话状态
  • 提供流式反馈
  • 统一接口管理

未来思考

学习 MCP 让我思考了几个问题:

  1. 协议设计的重要性

    • 好的协议设计可以大大简化开发
    • 统一标准降低学习成本
    • 可扩展性很重要
  2. AI 交互的未来

    • MCP 为 AI 交互提供了标准化方案
    • 未来可能会有更多类似的协议
    • 协议设计需要考虑 AI 的特殊性
  3. 开发者体验

    • MCP 的设计考虑了开发者体验
    • 支持多种传输方式,适应不同场景
    • 错误处理和流式响应都很人性化

写在最后

学习 MCP 的过程让我深刻理解了协议设计的重要性。一个好的协议不仅要功能强大,还要:

  • 易于理解
  • 易于实现
  • 易于扩展
  • 考虑实际使用场景

MCP 的设计体现了这些原则,这也是为什么它能够成为 AI 交互的标准协议。

希望这篇文章能够帮助到同样在学习 MCP 的朋友。如果你也有疑问,欢迎一起讨论!


相关资源:

相关推荐
Light602 小时前
破局而立:制造业软件企业的模式重构与AI赋能新路径
人工智能·云原生·工业软件·商业模式创新·ai赋能·人机协同·制造业软件
Quintus五等升2 小时前
深度学习①|线性回归的实现
人工智能·python·深度学习·学习·机器学习·回归·线性回归
natide2 小时前
text-generateion-webui模型加载器(Model Loaders)选项
人工智能·llama
野生的码农2 小时前
码农的妇产科实习记录
android·java·人工智能
TechubNews3 小时前
2026 年观察名单:基于 a16z「重大构想」,详解稳定币、RWA 及 AI Agent 等 8 大流行趋势
大数据·人工智能·区块链
脑极体3 小时前
机器人的罪与罚
人工智能·机器人
三不原则3 小时前
故障案例:容器启动失败排查(AI运维场景)——从日志分析到根因定位
运维·人工智能·kubernetes
点云SLAM3 小时前
凸优化(Convex Optimization)理论(1)
人工智能·算法·slam·数学原理·凸优化·数值优化理论·机器人应用
会周易的程序员3 小时前
多模态AI 基于工业级编译技术的PLC数据结构解析与映射工具
数据结构·c++·人工智能·单例模式·信息可视化·架构
BlockWay3 小时前
WEEX 成为 LALIGA 西甲联赛香港及台湾地区官方区域合作伙伴
大数据·人工智能·安全