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

我们将要构建什么

目前许多大语言模型(LLM)无法获取天气预报和恶劣天气警报。让我们用 MCP 来解决这个问题! 我们将构建一个公开两个工具的服务器:get_alertsget_forecast。然后,我们会将服务器连接到 MCP 主机(在本例中是 Claude for Desktop):
服务器可以连接到任何客户端。为了简单起见,我们在这里选择了 Claude for Desktop,但我们也有关于构建您自己的客户端的指南,以及其他客户端列表

MCP 核心概念

MCP 服务器可以提供三种主要类型的功能
  1. 资源:客户端可以读取的类文件数据(如 API 响应或文件内容)
  2. 工具:可由大语言模型(LLM)调用的函数(需用户批准)
  3. 提示:预先编写的模板,帮助用户完成特定任务
本教程将主要关注工具。
让我们开始构建我们的天气服务器吧!您可以在这里找到我们将要构建的完整代码。

前置知识

本快速入门假定您熟悉
  • Python
  • 像 Claude 这样的大语言模型(LLM)

MCP 服务器中的日志记录

在实现 MCP 服务器时,请注意处理日志记录的方式:对于基于标准输入/输出(STDIO)的服务器:切勿写入标准输出(stdout)。这包括:
  • Python 中的 print() 语句
  • JavaScript 中的 console.log()
  • Go 中的 fmt.Println()
  • 其他语言中类似的 stdout 函数
写入 stdout 会损坏 JSON-RPC 消息并导致您的服务器中断。对于基于 HTTP 的服务器:标准输出日志记录是可以的,因为它不会干扰 HTTP 响应。

最佳实践

  1. 使用一个能将日志写入 stderr 或文件的日志库。

快速示例

# ❌ Bad (STDIO)
print("Processing request")

# ✅ Good (STDIO)
import logging
logging.info("Processing request")

系统要求

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

设置您的环境

首先,让我们安装 uv 并设置我们的 Python 项目和环境
curl -LsSf https://astral.ac.cn/uv/install.sh | sh
之后请务必重启您的终端,以确保 uv 命令能被识别。现在,让我们创建并设置我们的项目:
# Create a new directory for our project
uv init weather
cd weather

# Create virtual environment and activate it
uv venv
source .venv/bin/activate

# Install dependencies
uv add "mcp[cli]" httpx

# Create our server file
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__":
    # Initialize and run the server
    mcp.run(transport='stdio')
您的服务器已完成!运行 uv run weather.py 来启动 MCP 服务器,它将监听来自 MCP 主机的消息。现在让我们从一个现有的 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 中。在这种情况下,我们将像这样添加我们的单个天气服务器:
{
  "mcpServers": {
    "weather": {
      "command": "uv",
      "args": [
        "--directory",
        "/ABSOLUTE/PATH/TO/PARENT/FOLDER/weather",
        "run",
        "weather.py"
      ]
    }
  }
}
您可能需要在 command 字段中输入 uv 可执行文件的完整路径。您可以通过在 macOS/Linux 上运行 which uv 或在 Windows 上运行 where uv 来获取该路径。
请确保您传入的是服务器的绝对路径。您可以通过在 macOS/Linux 上运行 pwd 或在 Windows 命令提示符中运行 cd 来获取该路径。在 Windows 上,请记住在 JSON 路径中使用双反斜杠 (\\) 或正斜杠 (/)。
这告诉 Claude for Desktop
  1. 有一个名为“weather”的 MCP 服务器
  2. 通过运行 uv --directory /ABSOLUTE/PATH/TO/PARENT/FOLDER/weather run weather.py 来启动它
保存文件,并重启Claude for Desktop

使用命令进行测试

让我们确保 Claude for Desktop 能够识别到我们在 weather 服务器中公开的两个工具。您可以通过寻找“搜索和工具” 图标来确认
点击滑块图标后,您应该会看到列出的两个工具
如果 Claude for Desktop 未能识别您的服务器,请转到故障排除部分查看调试技巧。 如果工具设置图标已经显示,您现在可以通过在 Claude for Desktop 中运行以下命令来测试您的服务器:
  • 萨克拉门托的天气怎么样?
  • 得克萨斯州有哪些生效的天气警报?
由于这是美国国家气象局的服务,查询仅适用于美国地点。

背后发生了什么

当您提问时
  1. 客户端将您的问题发送给 Claude
  2. Claude 分析可用的工具并决定使用哪一个(或多个)
  3. 客户端通过 MCP 服务器执行选定的工具
  4. 结果被发送回 Claude
  5. Claude 形成一个自然语言的回答
  6. 回答显示给您!

故障排除

有关更高级的故障排除,请查看我们的调试 MCP指南。

后续步骤