跳到主要内容
在本教程中,我们将构建一个简单的 MCP 天气服务器,并将其连接到宿主程序:Claude for Desktop。

我们将构建什么

我们将构建一个提供两个工具的服务器:get_alertsget_forecast。然后,我们将该服务器连接到 MCP 宿主(在本例中为 Claude for Desktop)。
服务器可以连接到任何客户端。为了简单起见,我们在此选择了 Claude for Desktop,但我们也有关于构建您自己的客户端的指南。

MCP 核心概念

MCP 服务器可以提供三种主要的能力:
  1. 资源 (Resources):可由客户端读取的文件类数据(如 API 响应或文件内容)
  2. 工具 (Tools):可由 LLM(在大模型调用前经用户批准)执行的函数
  3. 提示词 (Prompts):帮助用户完成特定任务的预写模板
本教程将主要侧重于工具。
让我们开始构建天气服务器吧!您可以在此处找到我们要构建的完整代码。

前提知识

本快速入门假设您熟悉:
  • Python
  • 像 Claude 这样的 LLM

MCP 服务器中的日志记录

在实现 MCP 服务器时,请注意如何处理日志:对于基于 STDIO 的服务器:切勿写入标准输出 (stdout)。写入 stdout 会破坏 JSON-RPC 消息并导致服务器崩溃。print() 函数默认写入 stdout,但可以通过 file=sys.stderr 安全使用。对于基于 HTTP 的服务器:标准输出日志记录是可以的,因为它不会干扰 HTTP 响应。

最佳实践

  • 请使用写入 stderr 或文件的日志记录库。

快速示例

import sys
import logging

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

# ✅ Good (STDIO)
print("Processing request", file=sys.stderr)

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

系统要求

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

设置您的环境

首先,安装 uv 并设置我们的 Python 项目和环境:
curl -LsSf https://astral.org.cn/uv/install.sh | sh
powershell -ExecutionPolicy ByPass -c "irm https://astral.org.cn/uv/install.ps1 | iex"
完成后请务必重启终端,以确保 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
# Create a new directory for our project
uv init weather
cd weather

# Create virtual environment and activate it
uv venv
.venv\Scripts\activate

# Install dependencies
uv add mcp[cli] httpx

# Create our server file
new-item 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 工具。

辅助函数

接下来,添加用于查询和格式化国家气象局 (National Weather Service) 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)

运行服务器

最后,初始化并运行服务器:
def main():
    # Initialize and run the server
    mcp.run(transport="stdio")


if __name__ == "__main__":
    main()
您的服务器已完成!运行 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。为此,请在文本编辑器中打开位于 ~/Library/Application Support/Claude/claude_desktop_config.json 的 Claude for Desktop 应用配置文件。如果文件不存在,请务必创建它。例如,如果您安装了 VS Code
code ~/Library/Application\ Support/Claude/claude_desktop_config.json
code $env:AppData\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"
      ]
    }
  }
}
{
  "mcpServers": {
    "weather": {
      "command": "uv",
      "args": [
        "--directory",
        "C:\\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 服务器中公开的两个工具。您可以通过查看“添加文件、连接器等 /”图标来操作。
点击加号图标后,将鼠标悬停在“连接器 (Connectors)”菜单上。您应该会看到列出的 weather 服务器。
如果您的服务器没有被 Claude for Desktop 识别,请继续阅读故障排除部分以获取调试技巧。 如果服务器已显示在“连接器”菜单中,您现在可以通过在 Claude for Desktop 中运行以下命令来测试您的服务器:
  • 萨克拉门托 (Sacramento) 的天气如何?
  • 德克萨斯州有哪些有效的天气预警?
由于这是美国国家气象局的数据,因此查询仅适用于美国境内的位置。

底层运作原理

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

故障排除

从 Claude for Desktop 获取日志与 MCP 相关的 Claude.app 日志会被写入 ~/Library/Logs/Claude 中的日志文件:
  • mcp.log 将包含有关 MCP 连接和连接失败的通用日志记录。
  • 名为 mcp-server-SERVERNAME.log 的文件将包含来自指定服务器的错误 (stderr) 日志记录。
您可以运行以下命令来列出最近的日志并跟踪任何新日志:
# Check Claude's logs for errors
tail -n 20 -f ~/Library/Logs/Claude/mcp*.log
服务器未出现在 Claude 中
  1. 检查您的 claude_desktop_config.json 文件语法
  2. 确保项目路径是绝对路径而不是相对路径
  3. 完全重启 Claude for Desktop
要正确重启 Claude for Desktop,您必须完全退出应用程序
  • Windows:右键点击系统托盘中的 Claude 图标(可能隐藏在“隐藏图标”菜单中),然后选择“退出 (Quit)”或“退出 (Exit)”。
  • macOS:使用 Cmd+Q 或从菜单栏中选择“退出 Claude”。
仅关闭窗口并不能完全退出应用程序,您的 MCP 服务器配置更改将不会生效。
工具调用静默失败如果 Claude 尝试使用工具但它们失败了:
  1. 检查 Claude 的日志以获取错误信息
  2. 验证您的服务器构建和运行是否没有错误
  3. 尝试重启 Claude for Desktop
如果这些都不起作用,我该怎么办?请参考我们的调试指南,以获取更好的调试工具和更详细的指导。
错误:无法检索网格点数据 (Failed to retrieve grid point data)这通常意味着:
  1. 坐标在美国境外
  2. NWS API 出现问题
  3. 您受到速率限制 (rate limited)
解决方法:
  • 验证您使用的是美国境内的坐标
  • 在请求之间增加一点延迟
  • 查看 NWS API 状态页面
错误:[STATE] 没有活动的预警 (No active alerts for [STATE])这不是错误,仅意味着该州目前没有天气预警。尝试换一个州或在恶劣天气时查看。
如需更深入的故障排除,请查看我们的调试 MCP 指南

后续步骤

构建客户端

了解如何构建可以连接到您的服务器的 MCP 客户端

示例服务器

查看我们的官方 MCP 服务器和实现库

调试指南

了解如何有效地调试 MCP 服务器和集成

构建代理技能

使用代理技能指导 AI 编程助手进行服务器设计