LLM大语言模型六(玩转mcp sse通信模式)

157 阅读4分钟

1.引言

上一篇文章我们搞清楚了什么是mcp,以及mcp的标准化IO通信模式(即本地通信,子进程方式运行服务端模式)。我们说mcp本质上是要实现LLM与外部世界的互联互通,开放互联,要实现这个目标,光本地通信模式肯定不够用,还需要基于http的sse模式。

sse即server send events,大家应都熟悉了,如果不熟悉,建议借助搜索引擎的力量,搜索关键词:sse与websocket的区别,你会找到答案的。

那么,这篇文章我分享两个主题

  • mcp调试工具使用
  • sse通信模式示例演示

2.案例

2.1.mcp调试工具

为了方便mcp应用开发调试,官方提供了配套调试工具:inspector。inspector工具提供了可视化操作界面,包含

  • 服务器连接面板
  • 资源标签
  • 提示标签
  • 工具标签
  • 通知面板

等等。使用该工具,需要安装node.js,直接去官网下载安装即可:nodejs.org/zh-cn

image.png

安装比较简单,我就不演示了。安装后验证命令:node -v

image.png 拿上一篇文章写好的案例验证一下,执行命令

npx -y @modelcontextprotocol/inspector python mymcp/hello_mcp_server.py

image.png

访问:http://127.0.0.1:6274/

image.png

选择右边工具Tools面板,测试一下

image.png

小工具,大用处,通过inspector可以快速实现mcp工具的开发调试,提升了实际应用开发效率!

2.2.sse通信模式示例

这篇文章的重点,是看mcp的sse通信模式如何玩?我将之前天气智能助手的案例,通过mcp进行重构。

2.2.1.服务端代码

import json
import requests
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")

2.2.2.客户端代码

import asyncio
import json
from typing import Optional
from contextlib import AsyncExitStack
from openai import OpenAI

from mcp import ClientSession
from mcp.client.sse import sse_client


class MCPClient:
    def __init__(self):
        """初始化MCP客户端"""
        self.exit_stack = AsyncExitStack()
        self.opanai_api_key = ""  # 调用模型的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: {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 mcp_weather_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__":
    # 启动执行命令:python agent/mcp_weather_client.py http://127.0.0.1:8000/sse
    import sys

    asyncio.run(main())

2.2.3.执行结果

启动服务端

image.png

启动客户端,执行命令:python agent/mcp_weather_client.py http://127.0.0.1:8000/sse

image.png

输入:广州,与大模型愉快的交流

image.png

到此,mcp sse通信模式案例完成。你有没有发现,两种通信模式区别的地方在哪儿?

一个是服务端启动地方

image.png

image.png

一个是客户端连接服务端,协议转换的地方

image.png

image.png