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

相关推荐
AntBlack7 小时前
虽迟但到 :盘一盘 SpringAI 现在发展得怎么样了?
后端·spring·openai
叶庭云8 小时前
一文掌握 CodeX CLI 安装以及使用!
人工智能·openai·安装·使用教程·codex cli·编码智能体·vibe coding 终端
后端小张15 小时前
[AI 学习日记] 深入解析MCP —— 从基础配置到高级应用指南
人工智能·python·ai·开源协议·mcp·智能化转型·通用协议
Tang102417 小时前
Cursor AI 编程工具指南
ai编程·cursor
数据智能老司机20 小时前
使用 OpenAI Agents SDK 构建智能体——记忆与知识
llm·openai·agent
数据智能老司机20 小时前
使用 OpenAI Agents SDK 构建智能体——代理工具与 MCP
llm·openai·agent
Larcher2 天前
n8n 入门笔记:用零代码工作流自动化重塑效率边界
前端·openai
七牛云行业应用2 天前
从API调用到智能体编排:GPT-5时代的AI开发新模式
大数据·人工智能·gpt·openai·agent开发
知其然亦知其所以然2 天前
SpringAI让Java会画画?用Azure OpenAI生成AI图片太惊艳了!
后端·spring·openai
let_code2 天前
初识模型上下文协议-MCP
mcp