导航菜单

  • 1.什么是MCP
  • 2.架构概览
  • 3.理解MCP服务器
  • 4.理解MCP客户端
  • 5.版本控制
  • 6.连接本地MCP服务器
  • 7.连接远程MCP服务器
  • 8.构建MCP服务器
  • 9.构建MCP客户端
  • 10.SDKs
  • 11.理解MCP中的授权
  • 12.安全最佳实践
  • 13.MCP Inspector
  • 14.规范
  • 15.关键变更
  • 16.架构
  • 17.基础协议概述
  • 18.生命周期
  • 19.传输
  • 20.授权
  • 21.取消
  • 22.Ping
  • 23.进度
  • 24.任务
  • 25.根
  • 26.采样
  • 27.引导
  • 29.提示
  • 30.资源
  • 31.工具
  • 32.补全
  • 33.日志
  • 34.分页
  • 35.模式参考
  • Keycloak
  • 28.服务器功能
  • 1. 你将构建什么?
  • 2. 前置要求
  • 3. STDIO 服务器的日志规则
  • 4. 环境准备
    • 4.1 步骤 1:安装 uv(Python 包管理工具)
    • 4.2 步骤 2:创建项目
  • 5 编写服务器代码
    • 5.1 导入与初始化
    • 5.2 辅助函数
    • 5.3 实现两个工具
    • 5.4 启动服务器
  • 6. 本地测试
  • 7 连接到 Claude Desktop
    • 7.1 步骤 1:打开配置文件
    • 7.2 步骤 2:添加 weather 服务器配置
    • 7.3 步骤 3:重启 Claude Desktop
  • 8. 验证是否成功
  • 9. 幕后原理
  • 10. 故障排除
    • 10.1 问题 1:服务器未在 Claude 中显示
    • 10.2 问题 2:工具调用失败
    • 10.3 问题 3:天气 API 相关错误
  • 11. 本章小结

1. 你将构建什么? #

一个天气 MCP 服务器,提供两个工具:

工具 功能
get_alerts 查询美国某州的天气警报
get_forecast 查询某经纬度的天气预报

说明:本教程使用 Claude for Desktop 作为客户端示例。服务器可连接任何 MCP 客户端,完整代码见 GitHub 仓库。

2. 前置要求 #

要求 说明
Python 3.10+ 需已安装
MCP Python SDK 1.2.0+ 本教程会通过 uv add 安装
基础 Python 知识 能看懂函数、异步、装饰器
了解 LLM 知道 Claude 等模型的基本用法

3. STDIO 服务器的日志规则 #

使用 STDIO 传输时,不要往 stdout 写任何内容,否则会破坏 JSON-RPC 消息,导致服务器崩溃。

方式 是否安全
print("xxx") ❌ 默认写 stdout,会破坏协议
print("xxx", file=sys.stderr) ✅ 写 stderr,安全
logging.info("xxx") ✅ 默认写 stderr,安全

4. 环境准备 #

4.1 步骤 1:安装 uv(Python 包管理工具) #

macOS/Linux:

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

Windows:

powershell -ExecutionPolicy ByPass -c "irm https://astral.sh/uv/install.ps1 | iex"

安装后重启终端,确保能识别 uv 命令。

4.2 步骤 2:创建项目 #

macOS/Linux:

uv init weather
cd weather
uv venv
source .venv/bin/activate
uv add "mcp[cli]" httpx
touch weather.py

Windows:

uv init weather
cd weather
uv venv
.venv\Scripts\activate
uv add "mcp[cli]" httpx
New-Item weather.py

5 编写服务器代码 #

5.1 导入与初始化 #

在 weather.py 顶部添加:

# 导入类型注解模块,用于类型提示
from typing import Any

# 导入 httpx,用于异步 HTTP 请求
import httpx
# 从 MCP SDK 导入 FastMCP,用于快速创建 MCP 服务器
from mcp.server.fastmcp import FastMCP

# 创建名为 "weather" 的 FastMCP 服务器实例
mcp = FastMCP("weather")
# 美国国家气象局 API 的基础 URL
NWS_API_BASE = "https://api.weather.gov"
# 请求时使用的 User-Agent,NWS API 要求必须提供
USER_AGENT = "weather-app/1.0"

提示:FastMCP 会根据函数签名和 docstring 自动生成工具定义,无需手写元数据。

5.2 辅助函数 #

# 定义异步函数:向 NWS API 发起请求,返回 JSON 或 None
async def make_nws_request(url: str) -> dict[str, Any] | None:
    # 函数的文档字符串,说明用途
    """请求 NWS API,带错误处理。"""
    # 设置请求头:User-Agent 为必填项,Accept 指定返回 GeoJSON 格式
    headers = {"User-Agent": USER_AGENT, "Accept": "application/geo+json"}
    # 创建异步 HTTP 客户端上下文
    async with httpx.AsyncClient() as client:
        # 捕获请求过程中的异常
        try:
            # 发起 GET 请求,超时 30 秒
            response = await client.get(url, headers=headers, timeout=30.0)
            # 若 HTTP 状态码为 4xx/5xx 则抛出异常
            response.raise_for_status()
            # 解析并返回 JSON 响应体
            return response.json()
        # 发生任何异常时返回 None,不中断程序
        except Exception:
            return None


# 定义函数:将单条警报数据格式化为可读字符串
def format_alert(feature: dict) -> str:
    # 函数的文档字符串
    """将警报数据格式化为可读字符串。"""
    # 从 GeoJSON feature 中取出 properties 字段
    props = feature["properties"]
    # 使用 f-string 拼接多行文本,包含事件、区域、严重程度、描述和指导
    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")}
    """

5.3 实现两个工具 #

# 使用 @mcp.tool() 装饰器,将该函数注册为 MCP 工具
@mcp.tool()
# 定义异步工具函数:根据州代码获取该州的天气警报
async def get_alerts(state: str) -> str:
    # 文档字符串:说明工具用途及参数
    """获取美国某州的天气警报。

    Args:
        state: 两字母州代码(如 CA, NY)
    """
    # 拼接 NWS 警报 API 的 URL
    url = f"{NWS_API_BASE}/alerts/active/area/{state}"
    # 发起异步请求获取数据
    data = await make_nws_request(url)

    # 若请求失败或响应中无 features 字段,返回提示信息
    if not data or "features" not in data:
        return "无法获取警报或未找到警报。"

    # 若 features 为空列表,说明该州暂无警报
    if not data["features"]:
        return "该州暂无活跃警报。"

    # 用列表推导式将每条警报格式化为可读字符串
    alerts = [format_alert(f) for f in data["features"]]
    # 用分隔符拼接多条警报并返回
    return "\n---\n".join(alerts)


# 使用 @mcp.tool() 装饰器,将该函数注册为 MCP 工具
@mcp.tool()
# 定义异步工具函数:根据经纬度获取天气预报
async def get_forecast(latitude: float, longitude: float) -> str:
    # 文档字符串:说明工具用途及参数
    """获取某经纬度的天气预报。

    Args:
        latitude: 纬度
        longitude: 经度
    """
    # 拼接 NWS 点位 API 的 URL,用于获取该坐标的元数据
    points_url = f"{NWS_API_BASE}/points/{latitude},{longitude}"
    # 请求点位数据,其中包含预报接口的 URL
    points_data = await make_nws_request(points_url)

    # 若点位请求失败,返回提示信息
    if not points_data:
        return "无法获取该位置的预报数据。"

    # 从点位数据的 properties 中取出预报接口 URL
    forecast_url = points_data["properties"]["forecast"]
    # 请求详细预报数据
    forecast_data = await make_nws_request(forecast_url)

    # 若预报请求失败,返回提示信息
    if not forecast_data:
        return "无法获取详细预报。"

    # 从预报数据中取出各时段列表
    periods = forecast_data["properties"]["periods"]
    # 初始化用于存放格式化预报的列表
    forecasts = []
    # 遍历前 5 个时段(如今天、今晚、明天等)
    for period in periods[:5]:
        # 将每个时段的名称、温度、风力、详细描述格式化为字符串并追加
        forecasts.append(f"""
        {period["name"]}:
        Temperature: {period["temperature"]}°{period["temperatureUnit"]}
        Wind: {period["windSpeed"]} {period["windDirection"]}
        Forecast: {period["detailedForecast"]}
        """)

    # 用分隔符拼接各时段预报并返回
    return "\n---\n".join(forecasts)

5.4 启动服务器 #

在文件末尾添加:

# 定义主函数,用于启动 MCP 服务器
def main():
    # 以 stdio 传输方式运行服务器,通过标准输入/输出与客户端通信
    mcp.run(transport="stdio")


# 当脚本被直接执行(而非被导入)时,调用 main 启动服务器
if __name__ == "__main__":
    main()

6. 本地测试 #

在项目目录下运行:

uv run weather.py

若服务器正常启动,会等待来自 MCP 客户端的消息。按 Ctrl+C 可退出。

7 连接到 Claude Desktop #

7.1 步骤 1:打开配置文件 #

  • macOS:~/Library/Application Support/Claude/claude_desktop_config.json
  • Windows:%APPDATA%\Claude\claude_desktop_config.json

若文件不存在,先创建。可用 VS Code 打开:

# macOS/Linux
code ~/Library/Application\ Support/Claude/claude_desktop_config.json

# Windows
code $env:AppData\Claude\claude_desktop_config.json

7.2 步骤 2:添加 weather 服务器配置 #

将 mcpServers 配置为(把路径换成你的实际项目路径):

macOS/Linux:

{
  "mcpServers": {
    "weather": {
      "command": "uv",
      "args": [
        "--directory",
        "/你的绝对路径/weather",
        "run",
        "weather.py"
      ]
    }
  }
}

Windows:

{
  "mcpServers": {
    "weather": {
      "command": "uv",
      "args": [
        "--directory",
        "C:\\你的绝对路径\\weather",
        "run",
        "weather.py"
      ]
    }
  }
}

提示:

  • 路径必须是绝对路径。macOS/Linux 用 pwd 查看,Windows 用 cd 查看
  • Windows 下 JSON 中的路径用双反斜杠 \\ 或正斜杠 /
  • 若 uv 找不到,可在 command 中写 uv 的完整路径(which uv 或 where uv 可查)

7.3 步骤 3:重启 Claude Desktop #

完全退出应用(不是只关窗口):

  • macOS:Cmd+Q 或菜单「Quit Claude」
  • Windows:右键托盘图标 → 退出

然后重新启动 Claude Desktop。

8. 验证是否成功 #

  1. 点击输入框旁的 「+」 或 「/」 按钮
  2. 在 Connectors 菜单中应能看到 weather 服务器

Connectors 菜单

  1. 可尝试提问:
    • 「萨克拉门托的天气如何?」
    • 「德克萨斯州有哪些活跃的天气警报?」

⚠️ 说明:本示例使用美国国家气象局 API,仅支持美国境内位置。

9. 幕后原理 #

当你在 Claude 中提问时:

  1. 客户端把你的问题发给 Claude
  2. Claude 分析可用工具,决定调用哪些
  3. 客户端通过 MCP 向服务器发起工具调用
  4. 服务器执行并返回结果
  5. 结果传回 Claude,Claude 生成自然语言回答
  6. 回答展示给你

10. 故障排除 #

10.1 问题 1:服务器未在 Claude 中显示 #

  1. 检查 claude_desktop_config.json 的 JSON 语法
  2. 确认路径为绝对路径
  3. 完全退出并重启 Claude Desktop(不是只关窗口)

10.2 问题 2:工具调用失败 #

  1. 查看 Claude 日志:~/Library/Logs/Claude/mcp*.log(macOS)或 %APPDATA%\Claude\logs\mcp*.log(Windows)
  2. 在终端手动运行 uv run weather.py,看是否有报错
  3. 尝试重启 Claude Desktop

10.3 问题 3:天气 API 相关错误 #

错误 可能原因 建议
无法检索网格点数据 坐标在美国境外、API 异常、被限流 使用美国坐标、加延迟、查 NWS 状态页
[STATE] 无活跃警报 该州当前无警报 非错误,可换州或改天再试

11. 本章小结 #

  • 工具定义:用 `@mcp.tool()` 装饰器,FastMCP 自动生成元数据
  • STDIO 日志:只写 stderr,不写 stdout
  • 配置:在 claude_desktop_config.json 中指定 command 和 args
  • 路径:必须使用绝对路径,Windows 注意 \\ 转义
← 上一节 7.连接远程MCP服务器 下一节 9.构建MCP客户端 →

访问验证

请输入访问令牌

Token不正确,请重新输入