目录
3.1.1 新建sse_weather_server.py文件
问题一:exceptiongroup.ExceptionGroup: unhandled errors in a TaskGroup (1 sub-exception)
0 环境准备
- ollama已部署推理模型qwen:7b(deepseek目前不支持function calling)
- 已安装miniconda环境
- 具备科学上网条件
1 开发环境准备
1.1 创建项目python环境
通过conda命令创建项目的python开发环境
conda create -n mcp_demo python=3.10
1.2 在pycharm创建项目

- 解释器类型:选择自定义环境
- 环境:选择现有
- 类型:选择conda
- 环境:选择上一步创建的环境
1.3 安装项目依赖
pip install mcp openai python-dotenv
2 资源准备
本项目中需要调用天气接口获取城市的天气信息,因此需要注册天气API网站,并且获取调用key。
2.1 访问openweather
Current weather and forecast - OpenWeatherMap
点击以上连接,使用邮箱注册账号,过程简单,此处不做赘述。
2.2 进入api key页面

2.3 生成apikey

按上图所示,在create key列中输入learn,然后点击generate按钮,就会生成一个名字为learn的key,后续使用就是这个key。如下图:

3 程序逻辑实现
mcp是c/s架构,客户端和服务端是一一对应的。因此在实现基于MCP协议工具调用时,要分别实现客户端和服务端。
3.1 服务端逻辑实现
3.1.1 新建sse_weather_server.py文件

3.1.2 导入相关依赖包
import argparse
import json
import httpx
import uvicorn
from mcp.server import FastMCP, Server
from mcp.server.sse import SseServerTransport
from starlette.applications import Starlette
from starlette.routing import Route, Mount
3.1.3 初始化MCP服务
mcp = FastMCP("sse_WeatherServer")
3.1.4 定义openweather配置
OPENWEATHER_API_KEY = "5082d49dxxxx51163cb"
OPENWEATHER_BASE_URL = "https://api.openweathermap.org/data/2.5/weather"
USER_AGENT = "weather-app/1.0"
注意将APIkey替换为自己申请的key。
3.1.5 定义获取城市天气方法
async def get_weather(city):
"""
从OpenWeather API 获取天气信息
:param city: 城市名称(需要试用英文,如 beijing)
:return: 天气数据字典;若发生错误,返回包含error信息的字典
"""
params = {
"q": city,
"appid": OPENWEATHER_API_KEY,
"units": "metric",
"lang": "zh_cn",
}
headers = {"User-Agent": USER_AGENT}
async with httpx.AsyncClient() as client:
try:
response = await client.get(OPENWEATHER_BASE_URL, params=params, headers=headers, timeout=30)
response.raise_for_status()
return response.json()
except httpx.HTTPStatusError as e:
return {"error": f"HTTP请求错误:{e}"}
except Exception as e:
return {"error": f"发生错误:{e}"}
3.1.6 定义格式话天气数据方法
def format_weather_data(data):
"""
格式化天气数据
:param data: 天气数据字典
:return: 格式化后的字符串;若发生错误,返回包含error信息的字符串
"""
# 如果传入的是字符串,则先转换成字典
if isinstance(data, str):
data = json.loads(data)
if "error" in data:
return data["error"]
weather = data["weather"][0]["description"]
temperature = data["main"]["temp"]
city = data["name"]
country = data["sys"]["country"]
humidity = data["main"]["humidity"]
wind = data["wind"]["speed"]
return f"城市:{city}, {country}\n天气:{weather}\n温度:{temperature}°C\n湿度:{humidity}%\n风速:{wind}m/s"
3.1.7 注册MCP工具
@mcp.tool()
async def get_weather_tool(city: str):
"""
获取城市的天气信息
:param city: 城市名称(需要试用英文,如 beijing)
:return: 天气数据字典;若发生错误,返回包含error信息的字典
"""
weather_data = await get_weather(city)
return format_weather_data(weather_data)
3.1.8 定义sse服务接口
def create_starlette_app(mcp_server: Server, *, debug: bool = False):
"""创建 Starlette 应用能通过sse提供mcp服务"""
sse = SseServerTransport("/messages/")
async def handle_sse(request):
async with sse.connect_sse(
request.scope,
request.receive,
request._send,
) as (read_stream,write_stream):
await mcp_server.run(
read_stream,
write_stream,
mcp_server.create_initialization_options(),
)
return Starlette(
debug=debug,
routes=[
Route("/sse", endpoint=handle_sse),
Mount("/messages/", app=sse.handle_post_message),
],
)
3.1.9 定义main方法
if __name__ == "__main__":
mcp_server = mcp._mcp_server
parser = argparse.ArgumentParser(description='Run MCP SSE-based server')
parser.add_argument("--host", default="0.0.0.0", help="MCP server host")
parser.add_argument("--port", default=18081, type=int, help="MCP server port")
args = parser.parse_args()
starlette_app = create_starlette_app(mcp_server, debug=True)
uvicorn.run(starlette_a


2062

被折叠的 条评论
为什么被折叠?



