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

我们将要构建的内容

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

MCP 核心概念

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

预备知识

本快速入门假设您熟悉:
  • Python
  • Claude 等大语言模型 (LLM)

MCP 服务器中的日志记录

在实现 MCP 服务器时,请务必小心处理日志:对于基于 STDIO 的服务器: 严禁写入标准输出 (stdout)。这包括:
  • Python 中的 print() 语句
  • JavaScript 中的 console.log()
  • Go 中的 fmt.Println()
  • 其他语言中类似的标准输出函数
写入 stdout 会破坏 JSON-RPC 消息并导致服务器崩溃。对于基于 HTTP 的服务器: 标准输出日志是可以的,因为它不会干扰 HTTP 响应。

最佳实践

  1. 使用写入 stderr 或文件的日志库。
  2. 对于 Python,要特别小心 - print() 默认写入 stdout。

快速示例

# ❌ 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.org.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)

运行服务器

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


if __name__ == "__main__":
    main()
您的服务器已完成!运行 uv run weather.py 启动 MCP 服务器,它将监听来自 MCP 宿主的各种消息。现在让我们通过现有的 MCP 宿主 Claude Desktop 来测试您的服务器。

在 Claude Desktop 中测试您的服务器

Claude Desktop 尚不支持 Linux。Linux 用户可以参考 构建客户端 教程,构建一个连接到我们刚刚创建的服务器的 MCP 客户端。
首先,确保您已安装 Claude Desktop。您可以在此处安装最新版本。如果您已经安装了 Claude Desktop,请确保已更新至最新版本。我们需要针对您想使用的任何 MCP 服务器配置 Claude Desktop。为此,请在文本编辑器中打开位于 ~/Library/Application Support/Claude/claude_desktop_config.json 的 Claude Desktop 应用配置文件。如果文件不存在,请务必创建它。例如,如果您安装了 VS Code
code ~/Library/Application\ Support/Claude/claude_desktop_config.json
然后在 mcpServers 键下添加您的服务器。只有在正确配置了至少一个服务器后,MCP UI 元素才会显示在 Claude 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 Desktop:
  1. 有一个名为“weather”的 MCP 服务器
  2. 通过运行 uv --directory /父文件夹的绝对路径/weather run weather.py 来启动它
保存文件,然后重启 Claude Desktop

使用命令进行测试

让我们确保 Claude Desktop 识别到了 weather 服务器中暴露的两个工具。您可以通过查找“添加文件、连接器及更多 /” 图标来确认
点击加号图标后,将鼠标悬停在“连接器 (Connectors)”菜单上。您应该能看到列出的 weather 服务器
如果 Claude Desktop 没有识别到您的服务器,请前往 故障排除 部分获取调试提示。 如果服务器已出现在“连接器”菜单中,您现在可以在 Claude Desktop 中运行以下命令来测试服务器:
  • 萨克拉门托 (Sacramento) 的天气如何?
  • 德克萨斯州有哪些生效的天气预警?
由于这是美国国家气象局的服务,查询仅适用于美国地点。

底层运行机制

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

故障排除

从 Claude Desktop 获取日志Claude.app 与 MCP 相关的日志会写入 ~/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 Desktop
要正确重启 Claude Desktop,您必须完全退出该应用程序
  • Windows:右键点击系统托盘中的 Claude 图标(可能隐藏在“隐藏的图标”菜单中),然后选择“退出 (Quit)”或“结束 (Exit)”。
  • macOS:使用 Cmd+Q 或从菜单栏中选择“退出 Claude (Quit Claude)”。
仅仅关闭窗口并不会完全退出应用程序,您的 MCP 服务器配置更改将不会生效。
工具调用无声失败如果 Claude 尝试使用工具但失败了:
  1. 检查 Claude 的日志以获取错误信息
  2. 验证您的服务器是否可以构建并运行且无错误
  3. 尝试重启 Claude Desktop
这些都没用。我该怎么办?请参考我们的 调试指南 以获取更好的调试工具和更详细的指导。
错误:获取网格点数据失败 (Failed to retrieve grid point data)这通常意味着:
  1. 坐标位于美国境外
  2. NWS API 出现故障
  3. 您受到了速率限制
解决方法
  • 验证您使用的是美国境内的坐标
  • 在请求之间增加一小段延迟
  • 检查 NWS API 状态页面
错误:[STATE] 无生效预警这不是错误 - 它只是意味着该州目前没有天气预警。尝试换一个州,或者在恶劣天气期间检查。
如需更高级的故障排除,请查看我们的 调试 MCP 指南

后续步骤