基于 MCP Http SSE模式的天气助手智能体开发实战(一文带你了解MCP两种开发模式)

前言

笔者上篇文章 理论+代码一文带你深入浅出MCP 详细分享了MCP的产生背景,设计理念,相比Function Calling的技术优势等内容。 同时使用Python SDK,基于MCP Studio的模式开发了大模型客户端和计算器服务端代码,深入浅出带大家了解了MCP的概念与开发流程。最近有读者在后台私信:现在更流行使用MCP Http SSE模式远程访问服务端,这种开发方式与MCP Stdio方式相比有什么异同呢?本篇文章笔者通过使用基于MCP Http SSE模式开发天气助手智能体的案例,带大家详细了解MCP的两种开发模式和开发技巧~

一、MCP服务端通信方式

MCP(Model Context Protocol, 模型上下文协议)约定了AI Agent智能体的开发规范,AI Agent智能体由大模型客户端扩展大模型能力的服务端 两部分构成。根据MCP协议定义,MCP服务端可以提供三种类型的标准能力:Resources, Tools, Prompts, 每个服务端可同时提供这三种类型能力或其中一种。

  • Resources: 资源,类似于文件数据读取,可以是文件资源或是API响应返回的内容
  • Tools: 工具,第三方服务、功能函数,通过此可控制大模型可调用哪些函数。
  • Prompts: 提示词,为用户预先定义好的完成特定任务的模板。

目前MCP服务端常见的能力是Tools, 通过封装函数和外部请求,扩展大模型的能力。

MCP服务端当前支持两种与客户端的数据通信方式:标准输入输出(stdio)基于Http的服务器推送事件(http sse)

1.1 标准输入输出(stdio)

原理: 标准输入输出是一种用于本地通信的传输方式。在这种模式下,MCP 客户端会将服务器程序作为子进程启动,双方通过约定的标准输入和标准输出(可能是通过共享文件等方法)进行数据交换。具体而言,客户端通过标准输入发送请求,服务器通过标准输出返回响应。。

适用场景: 标准输入输出方式适用于客户端和服务器在同一台机器上运行的场景(本地自行编写服务端或将别人编写的服务端代码pull到本地执行),确保了高效、低延迟的通信。这种直接的数据传输方式减少了网络延迟和传输开销,适合需要快速响应的本地应用。

1.2 基于Http的服务器推送事件(http sse)

原理: 客户端和服务端通过 HTTP 协议进行通信,利用 SSE 实现服务端向客户端的实时数据推送,服务端定义了/see与/messages接口用于推送与接收数据。这里要注意SSE协议和WebSocket协议的区别,SSE协议是单向的,客户端和服务端建立连接后,只能由服务端向客户端进行消息推送。而WebSocket协议客户端和服务端建立连接后,客户端可以通过send向服务端发送数据,并通过onmessage事件接收服务端传过来的数据。

适用场景: 适用于客户端和服务端位于不同物理位置的场景,尤其是对于分布式或远程部署的场景,基于 HTTP 和 SSE 的传输方式更合适。

1.3 可流式传输的HTTP

虽然目前MCP协议的通信方式只有以上两种,但该协议还在不断的进步与完善。近期有开发者在MCP的Github仓库中提交了一项提案,建议采用"可流式传输的 HTTP"来替代现有的 HTTP+SSE 方案。此举旨在解决当前远程 MCP 传输方式的关键限制,同时保留其优势。目前该提案还未被正式采纳。

二、基于Http SSE模式的天气助手智能体开发实战

在本项目开始前强烈建议大家先阅读 理论+代码一文带你深入浅出MCP ,掌握stdio通信方式的编写后,通过基于sse通信方式的开发,认识到两种开发模式的异同点。完整代码在: codecopy.cn/post/72atur

2.1 环境准备

  1. 使用deepseek-v3-0324作为大模型客户端,申请DeepSeek-V3的API Key。 获取当前的天气情况需要调用心知天气的API,申请心知天气API免费私钥。该部分详细的步骤在我的博文 从0到1开发DeepSeek天气助手智能体,这里不再赘述。

  2. MCP开发要求借助uv进行虚拟环境创建和依赖管理。(1)执行pip install uv命令安装uv。(2)执行uv init mcp-weather命令初始化项目。(3)cd mcp-weather进行项目文件夹,执行uv venv命令创建虚拟环境, 执行.venv\Scripts\activate命令激活虚拟环境(Linux下使用source .venv/bin/activate激活)。(4) 执行uv add mcp, uv add requests, uv add openai命令安装开发使用的python包。

2.2 编写MCP服务端代码

  1. mcp-weather项目目录下新建server.py文件并编写如下代码:
python 复制代码
import json
import requests
from typing import Any
from mcp.server.fastmcp import FastMCP

# 初始化MCP服务器
mcp = FastMCP("WeatherServer")

@mcp.tool()
async def get_weather(city: str):
    """
    输入指定城市的名称,返回当前天气情况
    :param city: 城市名称
    :return: json格式的天气信息
    """
    url="https://api.seniverse.com/v3/weather/now.json"
    params={
        "key": "你注册的心知天气私钥",
        "location": city,
        "language": "zh-Hans",
        "unit": "c"
    }
    response = requests.get(url, params=params)
    temperature = response.json()['results'][0]['now']
    return json.dumps(temperature)

if __name__ == "__main__":
    mcp.run(transport="sse")
  1. 再次体会MCP服务端开发的便捷性,使用Function Calling技术开发天气查询功能要编写description, json schema前后大约150行代码,使用MCP SDK开发只需要编写一个请求函数get_weather和函数注释,借助@mcp.tool()装饰器就可以快速添加天气查询功能。

  2. 注意使用http sse模式mcp服务端和使用stdio模式mcp服务端只有启动代码有差别。http sse的启动代码为mcp.run(transport="sse"), stdio的启动代码为mcp.run(transport="stdio")

2.3 编写MCP客户端代码

  1. mcp-weather项目目录下新建client.py文件, 编写如下代码:
python 复制代码
import asyncio
import json
from typing import Optional
from contextlib import AsyncExitStack
from openai import OpenAI

from mcp import ClientSession, StdioServerParameters
from mcp.client.stdio import stdio_client
from mcp.client.sse import sse_client

class MCPClient:
    def __init__(self):
        """初始化MCP客户端"""
        self.exit_stack = AsyncExitStack()
        self.opanai_api_key = "这里填写你的deepseek-api" # 调用模型的api_key
        self.base_url = "https://api.deepseek.com" # 调用模型url, 这里以deepseek作演示
        self.model = "deepseek-chat" # 调用deepseek-v3模型
        self.client = OpenAI(api_key=self.opanai_api_key, base_url=self.base_url)
        self.session: Optional[ClientSession] = None # Optional提醒用户该属性是可选的,可能为None
        self.exit_stack = AsyncExitStack() # 用来存储和清楚对话中上下文的,提高异步资源利用率

    async def connect_to_sse_server(self, server_url): #连接sse服务端,因为是基于http协议的,需要传入url
        sse_transport = await self.exit_stack.enter_async_context(sse_client(server_url))
        self.write, self.read = sse_transport
        self.session = await self.exit_stack.enter_async_context(ClientSession(self.write, self.read))
        await self.session.initialize() # 与服务器建立sse连接
        # 列出MCP服务器上的工具
        response = await self.session.list_tools()
        tools = response.tools
        print("\n已连接到服务器,支持以下工具:", [tool.name for tool in tools])#打印服务端可用的工具
  
    async def process_query(self, query:str)->str:
        """使用大模型处理查询并调用MCP Server可用的MCP工具"""
        messages = [{"role":"user", "content":query}]
        response = await self.session.list_tools()

        available_tools = [{
            "type": "function",
            "function": {
                "name": tool.name,
                "description": tool.description,
                "input_schema": tool.inputSchema
            }
        } for tool in response.tools]

        response = self.client.chat.completions.create(
            model=self.model,
            messages=messages,
            tools=available_tools
        )

        # 处理返回内容
        content = response.choices[0]
        if content.finish_reason == "tool_calls":
            # 返回结果是使用工具的建议,就解析并调用工具
            tool_call = content.message.tool_calls[0]
            tool_name = tool_call.function.name
            tool_args = json.loads(tool_call.function.arguments)
            # 执行工具
            result = await self.session.call_tool(tool_name, tool_args)
            print(f"\n\n[Calling tool {tool_name} with args {tool_args}]\n\n")
            # 将模型返回的调用工具的对话记录保存在messages中
            messages.append(content.message.model_dump())
            messages.append({
                "role": "tool",
                "content": result.content[0].text,
                "tool_call_id": tool_call.id,
            })
            # 将上面的结果返回给大模型用于生产最终结果
            response = self.client.chat.completions.create(
                model=self.model,
                messages=messages
            )
            return response.choices[0].message.content
        return content.message.content
  
    async def chat_loop(self):
        """运行交互式聊天"""
        print("\n MCP客户端已启动!输入quit退出")

        while True:
            try:
                query = input("\n用户:").strip()
                if query.lower() == 'quit':
                    break
                response = await self.process_query(query)
                print(f"\nDeepSeek-V3-0324: {response}")
            except Exception as e:
                print(f"发生错误: {str(e)}")

    async def clean(self):
        """清理资源"""
        await self.exit_stack.aclose()

async def main():
    if len(sys.argv) < 2:
        print("使用方法是: python client.py server_url")
        sys.exit(1)

    client = MCPClient()
    try:
        await client.connect_to_sse_server(sys.argv[1])
        await client.chat_loop()
    finally:
        await client.clean()

if __name__ == "__main__":
    import sys
    asyncio.run(main())
  1. 对比sse客户端代码和stdio客户端代码的区别,发现只是在连接服务函数层面有变化。sse是基于http sse协议的,需要服务端url地址作为参数。
python 复制代码
async def connect_to_sse_server(self, server_url):
    sse_transport = await self.exit_stack.enter_async_context(sse_client(server_url)) #sse_client初始化sse连接参数, 包括读,写流
    self.write, self.read = sse_transport
    self.session = await self.exit_stack.enter_async_context(ClientSession(self.write, self.read)) #依据读写流初始化sesssion
    await self.session.initialize() # 与服务器建立sse连接

stdio将服务端代码作为子进程执行,需要设置启动参数:

python 复制代码
async def connect_to_server(self, server_script_path):
        """连接到MCP服务器并列出MCP服务器的可用工具函数"""
        server_params = StdioServerParameters(
        command="python",
            args=[server_script_path],
            env=None
        ) # 设置启动服务器的参数, 这里是要用python执行server.py文件

        # 启动MCP服务器并建立通信
        stdio_transport = await self.exit_stack.enter_async_context(stdio_client(server_params))
        self.stdio, self.write = stdio_transport
        self.session = await self.exit_stack.enter_async_context(ClientSession(self.stdio, self.write))

        await self.session.initialize() # 与服务器建立stdio连接

2.4 启动服务端和客户端

stdio模式下,服务端作为客户端子进程启动,只需要执行uv run client.py server.py命令同时启动客户端和服务端。但在http sse模式下,MCP 服务端不再作为客户端的子进程,而是独立的服务提供,需要分两步启动。

  1. 激活虚拟环境的mcp-weather目录下执行uv run server.py命令,可以看到服务端开启了8000端口,在浏览器中输入http://localhost:8000/sse进行测试, 当客户端访问该url会触发服务端处理sse连接,同时服务端也不断的ping维持长连接。
  1. 执行uv run client.py http://localhost:8000/sse命令,第二个参数传入服务端sse的url, 用来建立客户端和服务端的连接,连接成功并返回正确结果,我们基于http sse模式的天气助手智能体就开发完成啦!

以上就是我们本篇文章的项目内容,希望大家与上篇文章结合,深刻理解MCP两种通信方式的异同点~

三、加餐:MCP Inspector------MCP服务端测试调用工具

Http Sse开发模式让我们能够访问远程部署的例如高德地图,百度地图,高德天气等各种各样MCP 服务端,不过每次编写相应的客户端程序进行测试访问还是比较麻烦的。

为了让开发者可以快速调用测试MCP服务端,Anthropic提供了一个非常便捷的debug工具:MCP Inspector 。下面就来简单看一下MCP Inspector的用法:

3.1 环境配置

使用MCP Inspector需要node.js环境,从 Node.js官网中下载.msi安装包,安装过程中注意将node添加到环境变量中,一路install即可。

执行npx -v命令查看node.js是否安装成功:

执行如下命令运行MCP Inspector, 即可在本地浏览器查看当前工具运行情况,我这里的地址是http://127.0.0.1:6274

arduino 复制代码
npx -y @modelcontextprotocol/inspector uv run server.py

3.2 MCP Inspector基本使用

使用浏览器打开http://127.0.0.1:6274, 选择 Transport Type为SSE, 输入MCP Server的url, 我们这里使用开发的天气智能体服务端作为测试, 输入http://localhost:8000/sse, 点击connect成功连接~

天气智能体的服务端类别是Tools, 我们点击Tools标签,并点击List Tools按钮,可以看到列出了天气智能体服务端的方法get_weather()及其注释。

点击get_weather()方法,在右侧输入该方法的参数,这里输入北京, 点击Run Tool, 可以看到服务端运行成功返回结果。

除了可以测试sse模式, MCP Inspector还可以测试stdio模式,只需在一开始选择 Transport Type为Stdio, 同时command输入uv, args输入run server.py就可以啦,大家可以使用上篇文章的例子尝试一下~

以上就是MCP Inspector软件的基本使用方法,其它使用技巧就需要屏幕前聪明的你自行探索了~

四、总结

本次分享我们深入浅出地介绍了MCP(模型上下文协议)的两种通信方式:标准输入输出(stdio)和基于HTTP的服务器推送事件(SSE)。通过开发一个天气助手智能体的实战案例,生动展示了两种模式的异同------stdio适合本地高效通信,而SSE则适用于远程分布式场景。本次分享我还着重对比了代码差异,比如SSE需要URL参数,而stdio直接调用子进程。最后,还为大家安利了调试神器MCP Inspector,能一键测试服务端功能,堪称"懒人福音"。

MCP的出现让AI 智能体的开发变得更加简单,目前的分享对编程小白可能不太友好。别着急,下篇文章我将把MCP和大模型自动编程软件cline, trae结合起来,开发出更有趣的案例!感兴趣大家点个关注吧。大家也可关注我的同名微信公众号:大模型真好玩,免费分享工作生活中大模型的开发经验和资料~

相关推荐
橙色小博29 分钟前
PyTorch中的各种损失函数的详细解析与通俗理解!
人工智能·pytorch·python·深度学习·神经网络·机器学习
小森77671 小时前
(三)机器学习---线性回归及其Python实现
人工智能·python·算法·机器学习·回归·线性回归
-XWB-1 小时前
【LLM】使用MySQL MCP Server让大模型轻松操作本地数据库
人工智能·python·自然语言处理
PacosonSWJTU2 小时前
python基础-13-处理excel电子表格
开发语言·python·excel
訾博ZiBo3 小时前
AI日报 - 2025年4月8日
人工智能
James. 常德 student3 小时前
深度学习之微调
人工智能·深度学习
小军要奋进3 小时前
httpx模块的使用
笔记·爬虫·python·学习·httpx
liuyunshengsir3 小时前
chromadb 安装和使用
人工智能·大模型
FIT2CLOUD飞致云3 小时前
全面支持MCP协议,开启便捷连接之旅,MaxKB知识库问答系统v1.10.3 LTS版本发布
人工智能·开源
Johnny_Cheung3 小时前
字符串、列表、元组、字典
开发语言·python