MCP Server开发:如何实现HTTP Stream

前言

最近MCP的概念大火,趁着这段时间的学习,打算自己着手开发一个MCP Server。作为程序员的MCP服务器,当然首先考虑这个server如何服务咱程序员了。利用端午几天时间,开发了一个简单的助力程序员提高工作效率的MCP servermcp-for-programmer。本文主要介绍HTTP Stream的连接方式

什么是MCP

Model Context Protocol (MCP) 是一种用于与AI模型交互的协议,它允许开发者定义和使用工具(tools)来扩展模型的能力。MCP服务器作为模型与工具之间的桥梁,负责处理请求、调用相应的工具,并将结果返回给模型。

MCP服务器架构

MCP服务器的核心架构包括:

  1. 传输层 :处理与客户端的通信,支持多种传输方式(stdio、SSE、HTTP Stream等)
  2. 工具注册:注册和管理可供模型调用的工具
  3. 提示词处理:加载和处理YAML格式的提示词模板
  4. 请求处理:解析请求,调用相应的工具,并返回结果

HTTP Stream

根据官方文档介绍,如果实现一个Stream连接有两种方式,With Session Management和Without Session Management。顾名思义,就是状态保持和非状态保持两种状态,这里我采用的是第一种。

  1. 客户端必须使用HTTP POST向MCP端点发送JSON-RPC消息
  2. 请求必须包含Accept头,列出application/json和text/event-stream作为支持的内容类型
  3. 请求体可以是单个JSON-RPC请求、通知、响应,或者是这些消息的批处理数组
  4. 如果输入仅包含JSON-RPC响应或通知:
    • 服务器接受输入时,必须返回HTTP状态码202 Accepted,无响应体
    • 服务器不接受输入时,必须返回HTTP错误状态码(如400 Bad Request)
  5. 如果输入包含任何JSON-RPC请求,服务器必须返回Content-Type: text/event-stream(启动SSE流)或Content-Type: application/json(返回一个JSON对象)

可恢复性与重传机制

事件ID机制

为了支持断开连接后的恢复和可能丢失的消息重传,MCP协议定义了以下机制:

  1. 服务器可以为SSE事件附加id字段,如SSE标准所述
  2. 如果存在,ID必须在该会话的所有流中全局唯一,或者在没有会话管理的情况下,在与特定客户端的所有流中全局唯一

当MCP客户端连接时,会产生一个新的session id的连接

断线重连实现

当连接断开后,客户端可以通过以下方式恢复连接:

  1. 客户端应该向MCP端点发起HTTP GET请求
  2. 请求中应包含Last-Event-ID头,指示最后接收到的事件ID
  3. 服务器可以使用此头部重放在断开连接的流上本应发送的消息,并从该点恢复流
  4. 服务器不能重放本应在不同流上传递的消息
typescript 复制代码
  // 从请求头获取Last-Event-ID,用于断线重连
  const lastEventId = req.headers["last-event-id"] as string | undefined;
  if (lastEventId) {
    console.log(`Client reconnecting with Last-Event-ID: ${lastEventId}`);
  } else {
    console.log(`Establishing new SSE stream for session ${sessionId}`);
  }
  
  // 重连机制由SDK实现,将请求交给传输对象处理
  await activeTransport.transport.handleRequest(req, res);
  return;

Last-Event-ID头部使用

Last-Event-ID头部是实现可恢复性的关键:

typescript 复制代码
    if (req.method === "GET" && reqUrl.pathname === endpoint) {
      const sessionId = req.headers["mcp-session-id"] as string | undefined;
      const activeTransport:
        | {
            server: McpServer;
            transport: StreamableHTTPServerTransport;
          }
        | undefined = sessionId ? activeTransports[sessionId] : undefined;

      if (!sessionId) {
        res.writeHead(400).end("No sessionId");
        return;
      }

      if (!activeTransport) {
        res.writeHead(400).end("No active transport");
        return;
      }

      const lastEventId = req.headers["last-event-id"] as string | undefined;
      if (lastEventId) {
        console.log(`Client reconnecting with Last-Event-ID: ${lastEventId}`);
      } else {
        console.log(`Establishing new SSE stream for session ${sessionId}`);
      }
      //* 重连机制由SDK实现
      await activeTransport.transport.handleRequest(req, res);
      return;
    }

会话管理

会话ID分配

通过mcp-session-id头实现会话的创建、维护和终止

MCP会话由客户端和服务器之间的逻辑相关交互组成,从初始化阶段开始:

  1. 使用Streamable HTTP传输的服务器可以在初始化时分配会话ID
  2. 会话ID通过包含在包含InitializeResult的HTTP响应的Mcp-Session-Id头中分配
  3. 会话ID应该是全局唯一且加密安全的(例如,安全生成的UUID、JWT或加密哈希)
  4. 会话ID必须仅包含可见的ASCII字符(范围从0x21到0x7E)

会话维护

会话维护的关键点:

  1. 如果服务器在初始化期间返回Mcp-Session-Id,使用Streamable HTTP传输的客户端必须在所有后续HTTP请求中包含它
  2. 需要会话ID的服务器应该对没有Mcp-Session-Id头的请求(初始化除外)响应HTTP 400 Bad Request
  3. 服务器可以随时终止会话,之后必须对包含该会话ID的请求响应HTTP 404 Not Found

项目简介

MCP-Server for Programmers 是一个基于 Model Context Protocol (MCP) 的服务器实现,专为帮助程序员理解和学习代码而设计。它能够通过提示词模板解析代码,提供代码解释、技术栈分析和最佳实践建议,帮助新手程序员更快地理解复杂代码。

代码地址

特性

  • 🚀 基于 MCP 协议,支持多种传输方式(stdio、SSE、streamable、HTTP Stream)
  • 📝 支持通过 YAML 文件定义提示词模板
  • 🔧 自动将提示词转换为工具,无需手动映射
  • 🧩 模板变量替换功能,支持条件渲染
  • 🌐 内置 Express 服务器,提供 REST API
  • 🔍 支持与 MCP Inspector 集成,方便调试
  • 📋 标准化的提示词规范,确保一致性和可维护性

已完成功能

  • ✅ MCP 服务器基础架构搭建
  • ✅ 多种传输方式支持(stdio、SSE、streamable)
  • ✅ YAML 提示词模板加载和解析
  • ✅ 提示词自动转换为工具功能
  • ✅ 模板变量替换和条件渲染
  • ✅ Express REST API 服务
  • ✅ 代码解释器提示词实现
  • ✅ Rollup构建系统用于库打包
  • ✅ HTTP Stream方式调用MCP服务器
  • ✅ 提示词规范文档与示例

待完成功能

  • 📋 管理界面更新、新增提示词
  • 📋 用户界面优化与交互改进
  • 📋 更多专业领域提示词模板
  • 📋 代码分析与建议功能增强
  • 📋 多语言支持扩展
  • 📋 性能优化与缓存机制
  • 📋 用户配置文件和个性化设置
  • 📋 插件系统

实现效果

以我最近正在刷type challenges的场景,我常常需要AI帮我分析下代码,但是每次都需要输入一些重复的提示词,Claude或者deepseek才能输出我想要的分析。我这里将提示词以yaml的形式注册为tool,让Cursor或者cherry studio这类mcp客户端自动调用。

yaml 复制代码
name: typescript-challenge-analyzer
description: 我正在刷typescript-challenge,根据我提供的ts代码段,分析这段TypeScript代码中使用的高级类型技术,提供最佳实践和使用场景示例,适合正在学习TypeScript类型系统的开发者

arguments: []

messages:
  - role: user
    content:
      type: text
      text: |
        你是一位专业的TypeScript类型系统专家,擅长分析复杂的TypeScript类型挑战并提供清晰的解释和最佳实践。你的目标是帮助正在学习TypeScript的开发者理解高级类型技术。
        请对这段TypeScript代码进行详细分析,我正在学习TypeScript类型系统,希望你能:
        
        1. 解释这段代码使用了哪些TypeScript类型技术
        2. 分析每个类型技术的作用和工作原理
        3. 提供这些技术的最佳实践(包含示例代码)
        4. 给出这些类型技术的实际使用场景(包含示例代码)
        5. 如果有更优雅的实现方式,请提供改进建议
        6. 根据tool的提示词思考以上几点

总结

当然,还有更多的模板可以预设,比如为开发者产出一个需求文档,或者为准备面试的程序员去发散提问的知识点等等。 欢迎大家一起学习,共同进步。github - mcp-for-programmer

相关推荐
BNTang5 小时前
AI编码焕新:用Context7
mcp
coder_pig6 小时前
Coze+Trae+掘金MCP = "✨掘金微热榜"
coze·mcp·trae
ZHOU_WUYI8 小时前
吴恩达MCP课程(5):research_server_prompt_resource.py
agent·mcp
斯普信专业组9 小时前
Cursor使用最佳实践总结
ai·cursor
后端小肥肠10 小时前
效率核爆!Coze工作流:抖音、小红书对标账号内容秒采飞书 + AI批量二创一条龙(附喂饭级教学)
人工智能·openai·coze
阿里云云原生11 小时前
实现企业级 MCP 服务统一管理和智能检索的实践
mcp
一_个前端12 小时前
Cursor 免费使用(无限邮箱)
cursor
sg_knight12 小时前
大模型连接万物的“万能插座”:深度解析模型上下文协议MCP
人工智能·ai·大模型·agent·ai大模型·mcp·模型上下文协议
新智元15 小时前
Fellou 2.0 震撼发布:你的专属贾维斯,开启 AI 批量化生产新时代
人工智能·openai
山茶君_nlefer18 小时前
cursor中成功调用“即梦AI”实现“编程+页面自由做”!真的酷!
aigc·ai编程·mcp