使用 MCP 官方 SDK 快速开发流式 HTTP MCP 服务器

1,467 阅读11分钟

前言

上篇文章理论+代码讲解Streamable HTTP MCP服务器原理,拒绝调包从0到1手撕流式 HTTP MCP服务器!为大家详细讲解了Streamable HTTP MCP Server的核心原理,同时不借助官方SDK从0到1手搓流式通信代码让大家直观感受到流式 HTTP MCP 服务器传输并发高、通信稳定、可断线重连以及更容易集成部署的特性。流式 HTTP MCP 服务器也被喻为MCP服务器与客户端异地通信的最佳解决方案,是企业级MCP通信的唯一方式。

上篇文章中分享的代码也让我们感受到流式HTTP MCP 服务器功能的复杂,相关SDK的研发难度也很高。虽然今年3月MCP官方就已经发布了MCP的流式HTTP通信协议,但直到5月9号的1.8.0版本的更新中,才正式在SDK中加入了流式 HTTP MCP 服务器的相关功能支持。自此开发者就可以通过MCP SDK,高效快速开发流式HTTP MCP服务器。

0.png

本篇分享将介绍使用MCP官方SDK快速完成流式服务器开发、测试、部署上线并发布到公网的全流程,大家赶快动手实践起来吧~

一、官方SDK 开发 Streamable HTTP MCP Server

1.1 环境构建

  1. 本项目同样使用anaconda 创建 虚拟环境,执行如下命令创建名为mcp的虚拟环境并激活
conda create -n mcp python=3.12
conda activate mcp

1.png

  1. MCP官方建议使用uv管理python项目,uv使用rust语言开发,不但在依赖包的安装速度上比pip快10-100倍,还提供了强大的项目管理,包括依赖管理、锁文件、工作空间等功能,是当前python项目管理工具的最佳选择,执行如下命令:
pip install uv #安装uv管理工具
uv init mcp-streamable-weather # 创建名为mcp-streamable-weather的项目
cd mcp-streamable-weather #进入mcp-streamable-weather项目目录
uv venv # 创建项目虚拟环境
.venv\Scripts\activate (Linux下执行source .venv/bin/activate) #激活虚拟环境

2.png

  1. 安装python依赖库,本项目需要使用mcp依赖库的流式 HTTP SDK快速开发, 需要使用requests依赖库请求心知天气的云服务获取天气信息(心知天气免费api key的注册可见我的文章:从0到1开发DeepSeek天气助手智能体——你以为大模型只会聊天?Function Calling让它“上天入地”)、
uv add mcp requests

3.png

1.2 项目编写

本次分享的项目目标是作为完整的pip包上传,所以这里采用一定的规范进行项目文件的编排,mcp-streamable-weather项目采用的是src_layer的风格。完整代码在: github.com/TangBaron/m…

  1. 删除mcp-streamable-weather文件夹中的main.py文件, 并创建mcp_weather_http目录:
mkdir -p ./src/mcp_weather_http # 新建mcp_weather_http文件夹
cd ./src/mcp_weather_http # 进入 mcp_weather_http文件夹

src/mcp_weather_http文件夹中创建三个代码文件:__init__.py, __main__.pyserver.py, 完成后的文件目录如下图所示:

4.png

  1. 编写流式 HTTP MCP 天气服务器代码, 在server.py中写入以下程序:

(1)引入各种功能模块,requests库用来对心知天气发起请求,mcp官方库提供了流式服务器编写sdk和大模型调用工具的能力, starlette帮你快速搭建ASGI的网络服务(关于ASGI概念可参考博客zhuanlan.zhihu.com/p/441743099), uvicorn帮你启动starlette搭建的网络服务,click帮你设置命令行参数, 其它模块帮助你处理异步任务、打印日志等。

import requests
import logging
import json
import click
import contextlib
import uvicorn
import mcp.types as types
from collections.abc import AsyncIterator
from mcp.server.lowlevel import Server
from mcp.server.streamable_http_manager import StreamableHTTPSessionManager
from starlette.applications import Starlette
from starlette.routing import Mount

(2) 编写查询天气的函数,通过请求心知天气获取传入城市的实时天气情况,提取响应中的结果利用json解析为string:

# 编写请求天气函数
async def fetch_weather(city: str, api_key):
    try:
        url="https://api.seniverse.com/v3/weather/now.json"
        params={
            "key": api_key,
            "location": city,
            "language": "zh-Hans",
            "unit": "c"
        }
        response = requests.get(url, params=params)
        temperature = response.json()['results'][0]['now']
    except Exception:
        return "error"
    return json.dumps(temperature)

(3) 编写服务器主函数main(), 使用click依赖库可以方便实现命令行参数。 main()函数中app是利用mcp sdk创建的应用服务,使用@app.call_tool()装饰器赋予了大模型可以调用函数fetch_weather的能力, @app.list_tools装饰器告诉大模型包含哪些工具,session_manager创建了MCP的“HTTP 会话处理中心”,用来处理所有http请求,也包括处理/mcp路由的请求, stateless=True表示不保存历史对话,每次都是新请求, json_response=False表示使用流式SSE,详细的细节可见代码中注释:

# 通过clik设置命令行启动参数
@click.command()
@click.option("--port", default=3000, help="Port to listen on for HTTP")
@click.option(
    "--api-key",
    required=True,
    help="心知天气API key",
)
@click.option(
    "--log-level",
    default="INFO",
    help="日志级别(DEBUG, INFO, WARNING, ERROR, CRITICAL)",
)
@click.option(
    "--json-response",
    is_flag=True,
    default=False,
    help="使用JSON响应代替SSE 流式输出",
)
def main(port, api_key, log_level, json_response):
    # ---------------------- 设置日志 ----------------------
    logging.basicConfig(
        level=getattr(logging, log_level.upper()),
        format="%(asctime)s - %(name)s - %(levelname)s - %(message)s",
    )
    logger = logging.getLogger("weather-server")

    # 创建MCP服务端
    app = Server("Weather-Streamable-HTTP-MCP-Server")

    # 工具调用
    @app.call_tool()
    async def call_tool(name, arguments):
        """
        Handle the 'get-weather' tool call.
        """
        ctx = app.request_context
        city = arguments.get("city")
        if not city:
            raise ValueError("'city' is required in arguments")

        # 准备发起天气请求发送日志
        await ctx.session.send_log_message(
            level="info",
            data=f"Fetching weather for {city}…",
            logger="weather",
            related_request_id=ctx.request_id,
        )

        try:
            weather = await fetch_weather(city, api_key)
        except Exception as err:
            #天气请求失败发送日志
            await ctx.session.send_log_message(
                level="error",
                data=str(err),
                logger="weather",
                related_request_id=ctx.request_id,
            )
            raise
        
        # 天气请求成功发送日志
        await ctx.session.send_log_message(
            level="info",
            data="Weather data fetched successfully!",
            logger="weather",
            related_request_id=ctx.request_id,
        )

        return [
            types.TextContent(type="text", text=weather)
        ]

    # 工具列表
    @app.list_tools()
    async def list_tools():
        """
        Expose available tools to the LLM.
        """
        return [
            types.Tool(
                name="get-weather",
                description="查询指定城市的实时天气(心知天气数据)",
                inputSchema={
                    "type": "object",
                    "required": ["city"],
                    "properties": {
                        "city": {
                            "type": "string",
                            "description": "城市名称,如 '北京'",
                        }
                    },
                },
            )
        ]
        
    #----------管理请求会话--------------
    session_manager = StreamableHTTPSessionManager(
        app=app,
        event_store=None, #无状态,不保存历史事件
        json_response=json_response,
        stateless=True
    )
    async def handle_streamable_http(scope, receive, send):
        await session_manager.handle_request(scope, receive, send)

    @contextlib.asynccontextmanager
    async def lifespan(app):
        async with session_manager.run():
            logger.info("Weather MCP server started! 🚀")
            try:
                yield
            finally:
                logger.info("Weather MCP server shutting down…")
    
    # 将MCP服务挂载到/mcp路径上,用户访问整个路径时,就会进入刚才创建的MCP HTTP会话管理器
    starlette_app = Starlette(
        debug=False,
        routes=[Mount("/mcp", app=handle_streamable_http)],
        lifespan=lifespan,
    )

    # 利用uvicorn启动ASGI服务器并监听指定端口
    uvicorn.run(starlette_app, host="0.0.0.0", port=port)

    return 0

if __name__ == "__main__":
    main()
  1. 本项目最终会上传到pypi公网上被大家下载使用,为确保大家能快速调用我们上传的服务器,还需要修改以下文件:

    (1) 在__init__.py中编写如下代码, __init__.pymcp-weather-http目录作为python包,并导出main函数

    from .server import main
    

    (2) 在__main__.py中写入如下代码,从mcp-weather-http包中导出main函数并执行

    from mcp_weather_http import main
    
    main()
    

    (3) 回到主目录,修改项目的配置文件pyproject.toml:

    [build-system]
    requires = ["setuptools>=61.0", "wheel"]
    build-backend = "setuptools.build_meta"
    
    [project]
    name = "mcp-streamable-weather"
    version = "1.1.0"
    description = "输入心知天气-API-KEY,获取实时天气信息。"
    readme = "README.md"
    requires-python = ">=3.12"
    dependencies = [
        "mcp>=1.9.0",
        "requests>=2.32.3",
    ]
    
    [project.scripts]
    mcp-streamable-weather = "mcp_weather_http:main"
    
    [tool.setuptools]
    package-dir = {"" = "src"}
    
    [tool.setuptools.packages.find]
    where = ["src"]
    

至此我们就完成了完整流式 HTTP 天气 MCP Server代码端的开发任务,建议反复阅读server.py中的代码,学习mcp官方流式sdk服务器的开发方式!

二、流式 HTTP MCP Server开启与测试

2.1 开启服务

为确保上线的准确性,需要对编写完成的流式HTTP MCP Server功能进行测试:

  1. 首先开启我们编写的MCP Server, 在mcp-streamable-weather项目目录下执行如下命令:
uv run ./src/mcp_weather_http/server.py --api-key 你注册的心知天气api_key

5.png

  1. 目前mcp官方的Inspector(详细说明可见我的文章基于 MCP Http SSE模式的天气助手智能体开发实战(一文带你了解MCP两种开发模式))也加入流式服务器的测试支持, 执行如下命令开启mcp Inspector:
npx -y @modelcontextprotocol/inspector

7.png

2.2 Inspector测试

在Inspector的Web界面中选择HTTP 流式模式,选择默认运行地址:http://localhost:3000/mcp,然后点击connect, 点击List Tools可以显示当前暴露的所有工具函数,我们服务器只有get-weather函数, 点击该函数并在右边栏输入地名测试,可以正常返回结果,服务器运行正常。

6.png

8、.png

三、流式HTTP MCP 服务器发布流程

3.1 项目发布

测试完成后即可上线发布,本次项目会发布到pypi平台,让大家都能轻松pip安装我们的天气服务器并使用。

回到项目主目录mcp-streamable-weather, 执行如下命令打包并上传python包(注意: pypi网站上传需要注册账号,注册流程可见博客www.cnblogs.com/myworldford…zhuanlan.zhihu.com/p/680216739

uv pip install build twine #安装打包所需库
python -m build # 执行打包命令
python -m twine upload dist/* #将打包后的dist内容上传

10.png

可以在pypi网站上查看我们发布的库:

11.png

3.2 CherryStudio软件项目测试

经常收到后台大家私信:

“我们编写的mcp server 可以 被cherry studio、openwebui等软件使用嘛?” “以往教程中都是通过我们自己编写MCP Client客户端方式测试,可以通过一些通用软件访问吗?”

为响应大家的需求,本次我们就更换一种测试方法使用CherryStudio软件连接我们的MCP服务器并完成工具调用~

  1. Cherry Studio 是一款集多模型对话、知识库管理、AI 绘画、翻译等功能于一体的全能 AI 客户端软件。看文章的小伙伴很多人可能已经在使用了。我们进入官网下载页下载.exe安装包,windows下设置好安装目录一路下一步即可,安装完成后打开界面如下:

12.png

  1. 配置大模型,本次分享使用的是DeepSeek-V3-0324大模型,使用如下步骤设置大模型并进行访问测试:

13.png

14.png

15.png

  1. 本地使用anaconda新建测试环境并安装我们编写的mcp-streamable-weatherpython包, 执行如下代码:
conda create -n test python=3.12 #新建名为test的测试虚拟环境
conda activate test # 激活虚拟环境
pip install uv #在虚拟环境中安装uv管理python 包
pip install mcp-streamable-weather # 安装我们编写上传的mcp-streamable-weather包
uv run mcp-streamable-weather --api-key 你注册的心知天气api

16.png

  1. Cherry Studio配置并连接流式HTTP模式下的MCP服务器,同样点击设置,点击MCP服务器,点击添加服务器,在名称栏填写mcp-streamable-weather, 类型选择可流式传输的HTTP, URL填写http://localhost:3000/mcp

17.png

点击保存后出现服务器更新成功提示,证明已经成功连接MCP服务器,此时CherryStudio服务端命令行界面显示如下:

18.png

19.png

  1. 在前端页面选择MCP服务器, 点击选中我们刚刚添加的mcp-streamable-weather服务器,然后提问“请问今天北京天气”,可以看到CherryStudio准确识别了我们编写的MCP Server,并调用工具返回正确结果!

20.png

21.png

3.3 小贴士

大家在跟随分享完成项目的过程中可以尝试在不同的机器上部署流式 HTTP MCP Server, 并在本地客户端上访问。最好可以与作者以前分享的SSE构建MCP Server方式作对比,直观感受Streamable HTTP MCP Server的优势,笔者这部分就留给大家自行尝试啦(其实是没钱买服务器了,逃~)

四、总结

本篇分享详细介绍了如何利用MCP官方SDK开发流式HTTP MCP服务器,从环境构建到项目编写、测试及发布的全流程,为开发者提供了高效开发流式HTTP MCP服务器的完整指南。主要内容包括:

  1. 环境配置:使用Anaconda创建虚拟环境,通过uv工具管理依赖,安装必要的Python库(如mcprequests)。
  2. 项目开发:编写流式HTTP服务器代码,集成心知天气API查询天气,并通过MCP SDK实现工具调用和会话管理。
  3. 测试与发布:使用MCP Inspector测试服务器功能,最终将项目打包上传至PyPI平台,并通过Cherry Studio验证其兼容性。

希望大家都能学会MCP开发模式,发挥自己的聪明才智,快速编写并分享自己的MCP Server,为AI 智时代贡献自己力量!

本篇分享就这么多内容啦,感兴趣大家点个关注吧。大家也可关注我的同名微信公众号:大模型真好玩,免费分享工作生活中大模型的开发经验和资料~