跳到主要内容
极客日志极客日志面向AI+效率的开发者社区
首页博客GitHub 精选镜像工具UI配色美学隐私政策关于联系
搜索内容 / 工具 / 仓库 / 镜像...⌘K搜索
注册
博客列表
PythonAI算法

基于 MCP 协议的智能体落地示例:天气预报工具实现

综述由AI生成基于 MCP 协议构建智能体扩展大模型实时能力的实战方案。通过 Python 开发 MCP 服务器提供天气查询工具,连接至 Claude for Desktop 客户端,演示了工具注册、调用及结果反馈的全链路流程。文章解析了 MCP 如何解决多 API 集成中的 Prompt 构建与维护难题,展示了智能体自动发现和使用标准化工具的核心机制。

laoliangsh发布于 2026/2/10更新于 2026/6/223 浏览
基于 MCP 协议的智能体落地示例:天气预报工具实现

1. 背景

在介绍 Model Context Protocol (MCP) 的概念、架构及解决的问题后,为了进一步理解整套 MCP 框架如何落地,本文基于官方示例——获取天气预报,演示 MCP 落地的整条链路。

2. MCP 示例

该案例是构建一个简单的 MCP 天气预报服务器,并将其连接到主机(如 Claude for Desktop)。大模型虽然能力强大,但其内容往往是非实时的。例如,它无法直接获取最新的天气预报和严重天气警报。使用 MCP 可以解决这一问题。

构建一个服务器,提供两个工具:get-alerts(获取警报)和 get-forecast(获取预报)。然后将该服务器连接到 MCP 主机。

环境配置

(1)安装 uv

curl -LsSf https://astral.sh/uv/install.sh | sh 

安装完成后会提示安装成功。

(2)安装所需的依赖包

需安装 mcp 及相关 HTTP 库。

(3)构建服务端代码

在 server.py 中构建相应的 get-alerts 和 get-forecast 工具。

from typing import Any
import asyncio
import httpx
from mcp.server.models import InitializationOptions
import mcp.types as types
from mcp.server import NotificationOptions, Server
import mcp.server.stdio

NWS_API_BASE = "https://api.weather.gov"
USER_AGENT = "weather-app/1.0"

server = Server("weather")

@server.list_tools()
async def handle_list_tools() -> list[types.Tool]:
    """List available tools."""
    return [
        types.Tool(
            name="get-alerts",
            description="Get weather alerts for a state",
            inputSchema={
                "type": "object",
                "properties": {
                    "state": {: , : ,},
                },
                : [],
            },
        ),
        types.Tool(
            name=,
            description=,
            inputSchema={
                : ,
                : {
                    : {: , : ,},
                    : {: , : ,},
                },
                : [, ],
            },
        ),
    ]

  () -> [, ] | :
    
    headers = {: USER_AGENT, : }
    :
        response =  client.get(url, headers=headers, timeout=)
        response.raise_for_status()
         response.json()
     Exception:
         

 () -> :
    
    props = feature[]
     (
        
        
        
        
        
        
    )


  () -> [types.TextContent | types.ImageContent | types.EmbeddedResource]:
    
      arguments:
         ValueError()

     name == :
        state = arguments.get()
          state:
             ValueError()
        state = state.upper()
         (state) != :
             ValueError()

          httpx.AsyncClient()  client:
            alerts_url = 
            alerts_data =  make_nws_request(client, alerts_url)
              alerts_data:
                 [types.TextContent(=, text=)]
            features = alerts_data.get(, [])
              features:
                 [types.TextContent(=, text=)]
            formatted_alerts = [format_alert(feature)  feature  features[:]]
            alerts_text =  + .join(formatted_alerts)
             [types.TextContent(=, text=alerts_text)]

     name == :
        :
            latitude = (arguments.get())
            longitude = (arguments.get())
         (TypeError, ValueError):
             [types.TextContent(=, text=)]

          (- <= latitude <= )   (- <= longitude <= ):
             [types.TextContent(=, text=)]

          httpx.AsyncClient()  client:
            points_url = 
            points_data =  make_nws_request(client, points_url)
              points_data:
                 [types.TextContent(=, text=)]

            properties = points_data.get(, {})
            forecast_url = properties.get()
              forecast_url:
                 [types.TextContent(=, text=)]

            forecast_data =  make_nws_request(client, forecast_url)
              forecast_data:
                 [types.TextContent(=, text=)]

            periods = forecast_data.get(, {}).get(, [])
              periods:
                 [types.TextContent(=, text=)]

            formatted_forecast = []
             period  periods:
                forecast_text = (
                    
                    
                    
                    
                    
                )
                formatted_forecast.append(forecast_text)
            forecast_text =  + .join(formatted_forecast)
             [types.TextContent(=, text=forecast_text)]

    :
         ValueError()

  ():
      mcp.server.stdio.stdio_server()  (read_stream, write_stream):
         server.run(
            read_stream,
            write_stream,
            InitializationOptions(
                server_name=,
                server_version=,
                capabilities=server.get_capabilities(notification_options=NotificationOptions(), experimental_capabilities={},),
            ),
        )

 __name__ == :
    asyncio.run(main())
"type"
"string"
"description"
"Two-letter state code (e.g. CA, NY)"
"required"
"state"
"get-forecast"
"Get weather forecast for a location"
"type"
"object"
"properties"
"latitude"
"type"
"number"
"description"
"Latitude of the location"
"longitude"
"type"
"number"
"description"
"Longitude of the location"
"required"
"latitude"
"longitude"
async
def
make_nws_request
client: httpx.AsyncClient, url: str
dict
str
Any
None
"""Make a request to the NWS API with proper error handling."""
"User-Agent"
"Accept"
"application/geo+json"
try
await
30.0
return
except
return
None
def
format_alert
feature: dict
str
"""Format an alert feature into a concise string."""
"properties"
return
f"Event: {props.get('event', 'Unknown')}\n"
f"Area: {props.get('areaDesc', 'Unknown')}\n"
f"Severity: {props.get('severity', 'Unknown')}\n"
f"Status: {props.get('status', 'Unknown')}\n"
f"Headline: {props.get('headline', 'No headline')}\n"
"---"
@server.call_tool()
async
def
handle_call_tool
name: str, arguments: dict | None
list
"""Handle tool execution requests."""
if
not
raise
"Missing arguments"
if
"get-alerts"
"state"
if
not
raise
"Missing state parameter"
if
len
2
raise
"State must be a two-letter code (e.g. CA, NY)"
async
with
as
f"{NWS_API_BASE}/alerts?area={state}"
await
if
not
return
type
"text"
"Failed to retrieve alerts data"
"features"
if
not
return
type
"text"
f"No active alerts for {state}"
for
in
20
f"Active alerts for {state}:\n\n"
"\n"
return
type
"text"
elif
"get-forecast"
try
float
"latitude"
float
"longitude"
except
return
type
"text"
"Invalid coordinates. Please provide valid numbers."
if
not
90
90
or
not
180
180
return
type
"text"
"Invalid coordinates."
async
with
as
f"{NWS_API_BASE}/points/{latitude},{longitude}"
await
if
not
return
type
"text"
f"Failed to retrieve grid point data."
"properties"
"forecast"
if
not
return
type
"text"
"Failed to get forecast URL."
await
if
not
return
type
"text"
"Failed to retrieve forecast data"
"properties"
"periods"
if
not
return
type
"text"
"No forecast periods available"
for
in
f"{period.get('name', 'Unknown')}:\n"
f"Temperature: {period.get('temperature', 'Unknown')}°{period.get('temperatureUnit', 'F')}\n"
f"Wind: {period.get('windSpeed', 'Unknown')} {period.get('windDirection', '')}\n"
f"{period.get('shortForecast', 'No forecast available')}\n"
"---"
f"Forecast for {latitude}, {longitude}:\n\n"
"\n"
return
type
"text"
else
raise
f"Unknown tool: {name}"
async
def
main
async
with
as
await
"weather"
"0.1.0"
if
"__main__"

核心逻辑在于 @server.list_tools() 注册可用工具处理器,以及 @server.call_tool() 注册执行工具调用的处理器。调用函数匹配工具名称,抽取输入参数,发起 API 请求并处理结果。

服务端与客户端交互

测试服务器与 Claude for Desktop。构建 MCP 客户端的核心逻辑如下:

async def process_query(self, query: str) -> str:
    """Process a query using Claude and available tools"""
    messages = [{"role": "user", "content": query}]
    response = await self.session.list_tools()
    available_tools = [{"name": tool.name, "description": tool.description, "input_schema": tool.inputSchema} for tool in response.tools]

    response = self.anthropic.messages.create(
        model="claude-3-5-sonnet-20241022",
        max_tokens=1000,
        messages=messages,
        tools=available_tools
    )

    final_text = []
    assistant_message_content = []
    for content in response.content:
        if content.type == 'text':
            final_text.append(content.text)
            assistant_message_content.append(content)
        elif content.type == 'tool_use':
            tool_name = content.name
            tool_args = content.input
            result = await self.session.call_tool(tool_name, tool_args)
            final_text.append(f"[Calling tool {tool_name} with args {tool_args}]")
            assistant_message_content.append(content)
            messages.append({"role": "assistant", "content": assistant_message_content})
            messages.append({"role": "user", "content": [{"type": "tool_result", "tool_use_id": content.id, "content": result.content}]})

    response = self.anthropic.messages.create(model="claude-3-5-sonnet-20241022", max_tokens=1000, messages=messages, tools=available_tools)
    final_text.append(response.content[0].text)
    return "\n".join(final_text)

启动客户端需要打开配置文件:~/Library/Application Support/Claude/claude_desktop_config.json。如果文件不存在则创建,并配置以下信息(以示例说明,假设 uv init 的是 weather):

{
  "mcpServers": {
    "weather": {
      "command": "uv",
      "args": [
        "--directory",
        "/ABSOLUTE/PATH/TO/PARENT/FOLDER/weather",
        "run",
        "server.py"
      ]
    }
  }
}

保存文件并重新启动 Claude for Desktop,即可识别天气服务器暴露的工具。

3. MCP 到底解决了什么问题

工具是智能体框架的重要组成部分,允许大模型与外界互动并扩展其能力。即使没有 MCP 协议,也可以实现 LLM 智能体,但存在弊端:当有许多不同的 API 时,启用工具使用变得很麻烦,因为任何工具都需要手动构建 prompt,且每当其 API 发生变化时需手动更新。

MCP 解决了当存在大量工具时,能够自动发现并自动构建 prompt 的问题。

整体流程示例:

  1. 工具发现:MCP 主机(如 Claude Desktop)首先调用 MCP 服务器,询问有哪些工具可用。
  2. 工具调用:MCP 客户端接收到列出的可用工具后发给 LLM,LLM 选择使用某个工具,通过主机向 MCP 服务器发送请求,接收结果。
  3. 输出答案:LLM 收到工具处理结果后,向用户输出最终答案。

总结来说,MCP 协议让智能体更容易管理、发现和使用工具。

4. 参考材料

  • For Server Developers - Model Context Protocol
  • For Client Developers - Model Context Protocol

目录

  1. 1. 背景
  2. 2. MCP 示例
  3. 环境配置
  4. 服务端与客户端交互
  5. 3. MCP 到底解决了什么问题
  6. 4. 参考材料
  • 💰 8折买阿里云服务器限时8折了解详情
  • Magick API 一键接入全球大模型注册送1000万token查看
  • 🤖 一键搭建Deepseek满血版了解详情
  • 一键打造专属AI 智能体了解详情
极客日志微信公众号二维码

微信扫一扫,关注极客日志

微信公众号「极客日志V2」,在微信中扫描左侧二维码关注。展示文案:极客日志V2 zeeklog

更多推荐文章

查看全部
  • Windows 本地部署 Stable Diffusion 简易指南:ComfyUI 版
  • Llama API 集成 LlamaIndex 实现文本补全与结构化数据提取
  • 本地电脑部署 DeepSeek-R1 模型实战指南
  • nanobot 通过 webhook 对接钉钉飞书实现跨平台消息同步
  • Z-Image-Turbo 对比 Stable Diffusion 核心优势分析
  • FPGA 开发板 6 层 PCB 设计与绘制流程
  • 前缀和算法实战:和为 K 的子数组与和可被 K 整除的子数组
  • Google 发布 Gemini Embedding 2 及 MuleRun 自进化 AI 助手动态
  • OpenClaw:本地优先开源 AI 智能体部署与实战指南
  • Conda 虚拟环境与安装包路径修改:释放磁盘空间配置指南
  • DeepSeek 新手入门:从零掌握 AI 搜索工具
  • 使用 Web Scraper 插件高效爬取知乎评论数据
  • MySQL 迁移金仓数据库:兼容核心与实操避坑指南
  • ESP32-S3 运行 Linux 指南:RISC-V 模拟器移植与原生方案
  • IDEA 创建 Spring Boot Web 项目教程
  • CSS 盒子模型详解:边框、内边距与外边距
  • 基于 Python Flask 和 Vue 的动漫周边商城系统设计与实现
  • AR/VR 教育应用开发实战指南
  • 群晖NAS搭建Git Server:从零配置到团队协作
  • 斯大林排序:一种 O(n) 时间的独特排序算法

相关免费在线工具

  • 加密/解密文本

    使用加密算法(如AES、TripleDES、Rabbit或RC4)加密和解密文本明文。 在线工具,加密/解密文本在线工具,online

  • RSA密钥对生成器

    生成新的随机RSA私钥和公钥pem证书。 在线工具,RSA密钥对生成器在线工具,online

  • Mermaid 预览与可视化编辑

    基于 Mermaid.js 实时预览流程图、时序图等图表,支持源码编辑与即时渲染。 在线工具,Mermaid 预览与可视化编辑在线工具,online

  • 随机西班牙地址生成器

    随机生成西班牙地址(支持马德里、加泰罗尼亚、安达卢西亚、瓦伦西亚筛选),支持数量快捷选择、显示全部与下载。 在线工具,随机西班牙地址生成器在线工具,online

  • Gemini 图片去水印

    基于开源反向 Alpha 混合算法去除 Gemini/Nano Banana 图片水印,支持批量处理与下载。 在线工具,Gemini 图片去水印在线工具,online

  • curl 转代码

    解析常见 curl 参数并生成 fetch、axios、PHP curl 或 Python requests 示例代码。 在线工具,curl 转代码在线工具,online