在本教程中,我们将构建一个简单的 MCP 天气服务器,并将其连接到宿主环境 Claude for Desktop。我们将从基本设置开始,然后逐步过渡到更复杂的用例。

我们要构建什么

目前许多 LLM(包括 Claude)都没有获取天气预报和严重天气警报的能力。让我们使用 MCP 来解决这个问题!

我们将构建一个暴露两个工具的服务器:get-alertsget-forecast。然后我们将把服务器连接到一个 MCP 宿主环境(在本例中是 Claude for Desktop):

服务器可以连接到任何客户端。我们在这里选择 Claude for Desktop 是为了简单起见,但我们也有构建自己的客户端的指南以及其他客户端列表

MCP 核心概念

MCP 服务器可以提供三种主要类型的功能:

  1. 资源(Resources):可以被客户端读取的类文件数据(如 API 响应或文件内容)
  2. 工具(Tools):可以被 LLM 调用的函数(需要用户批准)
  3. 提示(Prompts):帮助用户完成特定任务的预写模板

本教程将主要关注工具的使用。

让我们开始构建我们的天气服务器!你可以在这里找到我们将要构建的完整代码。

前置知识

本快速入门假设你熟悉:

  • Python
  • LLM(如 Claude)

系统要求

  • 安装 Python 3.10 或更高版本
  • 必须使用 Python MCP SDK 1.2.0 或更高版本

设置环境

首先,让我们安装 uv 并设置 Python 项目和环境:

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

安装完成后,请确保重启终端以确保 uv 命令可以被正常使用。

现在,让我们创建并设置我们的项目:

# 创建项目目录
uv init weather
cd weather

# 创建并激活虚拟环境
uv venv
source .venv/bin/activate

# 安装依赖
uv add "mcp[cli]" httpx

# 创建服务器文件
touch weather.py

现在让我们深入构建服务器。

构建服务器

导入包和设置实例

将以下内容添加到 weather.py 的顶部:

from typing import Any
import httpx
from mcp.server.fastmcp import FastMCP

# Initialize FastMCP server
mcp = FastMCP("weather")

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

FastMCP 类使用 Python 类型提示和文档字符串自动生成工具定义,使创建和维护 MCP 工具变得容易。

辅助函数

接下来,让我们添加用于查询和格式化国家气象服务 API 数据的辅助函数:

async def make_nws_request(url: str) -> dict[str, Any] | None:
    """Make a request to the NWS API with proper error handling."""
    headers = {
        "User-Agent": USER_AGENT,
        "Accept": "application/geo+json"
    }
    async with httpx.AsyncClient() as client:
        try:
            response = await client.get(url, headers=headers, timeout=30.0)
            response.raise_for_status()
            return response.json()
        except Exception:
            return None

def format_alert(feature: dict) -> str:
    """Format an alert feature into a readable string."""
    props = feature["properties"]
    return f"""
Event: {props.get('event', 'Unknown')}
Area: {props.get('areaDesc', 'Unknown')}
Severity: {props.get('severity', 'Unknown')}
Description: {props.get('description', 'No description available')}
Instructions: {props.get('instruction', 'No specific instructions provided')}
"""

实现工具执行

工具执行处理器负责实际执行每个工具的逻辑。让我们添加它:

@mcp.tool()
async def get_alerts(state: str) -> str:
    """Get weather alerts for a US state.

    Args:
        state: Two-letter US state code (e.g. CA, NY)
    """
    url = f"{NWS_API_BASE}/alerts/active/area/{state}"
    data = await make_nws_request(url)

    if not data or "features" not in data:
        return "Unable to fetch alerts or no alerts found."

    if not data["features"]:
        return "No active alerts for this state."

    alerts = [format_alert(feature) for feature in data["features"]]
    return "\n---\n".join(alerts)

@mcp.tool()
async def get_forecast(latitude: float, longitude: float) -> str:
    """Get weather forecast for a location.

    Args:
        latitude: Latitude of the location
        longitude: Longitude of the location
    """
    # First get the forecast grid endpoint
    points_url = f"{NWS_API_BASE}/points/{latitude},{longitude}"
    points_data = await make_nws_request(points_url)

    if not points_data:
        return "Unable to fetch forecast data for this location."

    # Get the forecast URL from the points response
    forecast_url = points_data["properties"]["forecast"]
    forecast_data = await make_nws_request(forecast_url)

    if not forecast_data:
        return "Unable to fetch detailed forecast."

    # Format the periods into a readable forecast
    periods = forecast_data["properties"]["periods"]
    forecasts = []
    for period in periods[:5]:  # Only show next 5 periods
        forecast = f"""
{period['name']}:
Temperature: {period['temperature']}°{period['temperatureUnit']}
Wind: {period['windSpeed']} {period['windDirection']}
Forecast: {period['detailedForecast']}
"""
        forecasts.append(forecast)

    return "\n---\n".join(forecasts)

运行服务器

最后,让我们初始化并运行服务器:

if __name__ == "__main__":
    # 初始化并运行服务器
    mcp.run(transport='stdio')

你的服务器已经完成!运行 uv run weather.py 来确认一切正常工作。

现在让我们从一个现有的 MCP 宿主环境 Claude for Desktop 测试你的服务器。

使用 Claude for Desktop 测试服务器

Claude for Desktop 目前尚未在 Linux 上提供。Linux 用户可以继续学习构建客户端教程,以构建一个连接到我们刚刚构建的服务器的 MCP 客户端。

首先,确保你已安装 Claude for Desktop。你可以在这里安装最新版本。如果你已经安装了 Claude for Desktop,请确保它已更新到最新版本。

我们需要为你想要使用的 MCP 服务器配置 Claude for Desktop。为此,请在文本编辑器中打开你的 Claude for Desktop 应用程序配置文件,路径为 ~/Library/Application Support/Claude/claude_desktop_config.json。如果该文件不存在,请创建它。

例如,如果你安装了 VS Code

code ~/Library/Application\ Support/Claude/claude_desktop_config.json

然后你需要在 mcpServers 键中添加你的服务器。只有在至少正确配置了一个服务器的情况下,MCP UI 元素才会在 Claude for Desktop 中显示。

在这种情况下,我们将添加我们的单个天气服务器,如下所示:

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

你可能需要在 command 字段中填写 uv 可执行文件的完整路径。你可以通过在 MacOS/Linux 上运行 which uv 或在 Windows 上运行 where uv 来获取。

确保你传入服务器的绝对路径。

这告诉 Claude for Desktop:

  1. 有一个名为 “weather” 的 MCP 服务器
  2. 通过运行 uv --directory /ABSOLUTE/PATH/TO/PARENT/FOLDER/weather run weather 来启动它

保存文件,然后重启 Claude for Desktop

使用命令测试

让我们确保 Claude for Desktop 能够识别我们在 weather 服务器中暴露的两个工具。你可以通过查找锤子 icon:

点击锤子图标后,你应该能看到列出的两个工具:

如果你的服务器没有被 Claude for Desktop 识别,请查看故障排除部分获取调试提示。

如果锤子图标已经显示,你现在可以在 Claude for Desktop 中运行以下命令来测试你的服务器:

  • Sacramento 的天气如何?
  • Texas 有哪些活跃的天气警报?

由于这是美国国家气象服务,查询只适用于美国地区。

幕后发生了什么

当你提出问题时:

  1. 客户端将你的问题发送给 Claude
  2. Claude 分析可用的工具并决定使用哪个工具
  3. 客户端通过 MCP 服务器执行所选工具
  4. 结果返回给 Claude
  5. Claude 形成自然语言响应
  6. 响应显示给你!

故障排除

要获取更高级的故障排除方法,请查看我们的MCP调试指南

下一步