引言

你是否曾经让 Claude 帮你查天气、分析文件,或者调用内部 API,却不得不在对话里粘贴整段 JSON,甚至手动解释返回结果?随着大语言模型的能力越来越强,我们不再满足于文本对话,更希望 AI 能像真实的工作助手一样,直接使用我们指定的工具对外交互。过去这种集成往往需要搭建复杂的中间服务,如今有了 Model Context Protocol(MCP),一切都变得简单而优雅。

本文将带你从零开始,理解 MCP 的核心理念,并通过一个完整的天气查询工具实战,为 Claude 客户端(如 Claude Desktop)构建专属的工具服务器。你会看到,仅仅几十行 Python 代码,就能让 Claude 在回答中实时拉取外部数据,甚至执行你定义好的任何操作。读完此文,你将能够:

  • 理解 MCP 的设计思想与核心概念
  • 使用 Python 快速构建一个 MCP Server
  • 注册自定义工具并配置 Claude Desktop 使用
  • 掌握调试、安全与常见避坑要点

让我们直接开始吧。

核心概念:MCP 是什么?

MCP(Model Context Protocol)是由 Anthropic 推出的一种开放协议,它标准化了 AI 模型与外部工具、数据源之间的交互方式。你可以把它想象成 AI 世界的“USB‑C 接口”——只要遵循 MCP 规范,任何客户端(Claude Desktop、IDE 插件、自研应用)都能无缝连接任何服务器,发现并使用其提供的 工具(Tools)资源(Resources)提示(Prompts)

MCP 架构一览

MCP 采用客户端‑服务器(Client‑Server)架构,通信基于 JSON‑RPC 2.0,底层传输可以是标准输入输出(stdio)或 HTTP+SSE。一次典型的交互流程如下:

  1. Server 启动 并暴露自己的能力(例如一个 get_weather 工具)。
  2. Client(如 Claude Desktop) 与 Server 建立连接,进行能力协商。
  3. 用户在对话中提出请求,Claude 判断需要使用某个工具。
  4. Client 向 Server 发送 tools/call 请求,携带工具名和参数。
  5. Server 执行逻辑(调用 API、查询数据库、运行脚本等),并将结果返回。
  6. Claude 将结果自然整合到回答中,呈现给用户。

整个过程对用户透明,仿佛 Claude 天生就拥有这些能力。

三个核心元素

  • 工具(Tools):模型可以调用的函数。每个工具包含名称、描述、输入参数的 JSON Schema。
  • 资源(Resources):模型可以直接读取的数据,如文件内容、数据库记录。
  • 提示(Prompts):预定义的交互模板,帮助用户快速发起特定类型的请求。

本文我们聚焦在最常用、最灵活的 自定义工具 上,构建一个 MCP Server,为 Claude 提供实时天气查询能力。

实战:构建一个天气查询 MCP 工具

我们将创建一个 Python MCP Server,注册 get_weather 工具,它会调用免费的 wttr.in API 获取指定城市的当前天气,并返回格式化后的文本。随后我们配置 Claude Desktop 连接此 Server,在对话中直接询问天气。

环境准备

本示例基于 Python 3.10+ 和官方 MCP Python SDK。首先安装依赖:

pip install "mcp[cli]" httpx
  • mcp[cli]:包含 MCP Server/Client 的基础库及命令行调试工具。
  • httpx:异步 HTTP 客户端,用于请求天气 API。

编写 MCP Server 代码

新建文件 weather_server.py,内容如下:

import json
import httpx
from mcp.server import Server, NotificationOptions
from mcp.server.models import InitializationCapabilities
from mcp.server.stdio import stdio_server
from mcp.types import Tool, TextContent

# 创建 MCP Server 实例
server = Server("weather-server")

# 定义工具的具体实现
async def handle_get_weather(city: str) -> str:
    """
    实际调用 wttr.in API,获取指定城市的天气信息。
    返回简洁的文本描述,便于 Claude 理解。
    """
    # wttr.in 提供多种格式,这里使用 JSON 格式避免解析 HTML
    url = f"https://wttr.in/{city}?format=j1"
    async with httpx.AsyncClient() as client:
        resp = await client.get(url)
        resp.raise_for_status()
        data = resp.json()

    # 提取当前天气数据
    current = data["current_condition"][0]
    weather_desc = current["weatherDesc"][0]["value"]
    temp_c = current["temp_C"]
    humidity = current["humidity"]
    feels_like = current["FeelsLikeC"]

    return (
        f"{city} 当前天气:{weather_desc}\n"
        f"气温:{temp_c}°C(体感 {feels_like}°C)\n"
        f"湿度:{humidity}%"
    )

# 注册工具:定义名称、描述、参数 schema
@server.list_tools()
async def list_tools() -> list[Tool]:
    return [
        Tool(
            name="get_weather",
            description="查询指定城市的实时天气信息,包括天气状况、气温、湿度等。",
            inputSchema={
                "type": "object",
                "properties": {
                    "city": {
                        "type": "string",
                        "description": "城市名称,例如 Beijing, London, Tokyo"
                    }
                },
                "required": ["city"]
            }
        )
    ]

# 处理工具调用请求
@server.call_tool()
async def call_tool(name: str, arguments: dict) -> list[TextContent]:
    if name == "get_weather":
        city = arguments.get("city", "")
        if not city:
            raise ValueError("city 参数不能为空")
        result_text = await handle_get_weather(city)
        return [TextContent(type="text", text=result_text)]
    else:
        raise ValueError(f"未知工具: {name}")

# 主函数:启动 stdio 传输的 Server
async def main():
    async with stdio_server() as (read_stream, write_stream):
        await server.run(
            read_stream,
            write_stream,
            InitializationCapabilities(
                sampling={},      # 不需要采样能力
                experimental={},
            ),
            notification_options=NotificationOptions(),
        )

if __name__ == "__main__":
    import asyncio
    asyncio.run(main())

这段代码清晰地体现了 MCP Server 的三个关键部分:

  1. 工具清单 (list_tools):向客户端声明服务器提供了哪些工具,以及每个工具的参数格式(JSON Schema)。Claude 会根据这里的描述,在对话中准确理解工具的用途和调用方式。
  2. 工具执行 (call_tool):当客户端请求调用某个工具时,该函数被触发,负责解析参数、执行实际业务逻辑(此处为 HTTP 请求),并返回 TextContent 类型的结果。
  3. 传输层:我们使用 stdio 即标准输入输出,这是 MCP 本地使用最方便的方式,无需开放网络端口。

本地测试工具

在正式接入 Claude 前,我们可以用 mcp dev 命令快速测试工具是否正常工作:

mcp dev weather_server.py

这会启动一个交互式测试环境,你可以模拟客户端调用:

>> tools/call get_weather {"city":"Beijing"}

如果一切正常,你会看到类似输出:

Beijing 当前天气:Sunny
气温:22°C(体感 21°C)
湿度:35%

配置 Claude Desktop 使用自定义工具

假设你已安装 Claude Desktop。我们需要编辑其配置文件,告诉它启动我们刚刚编写的 MCP Server。

  1. 找到配置文件
    - macOS: ~/Library/Application Support/Claude/claude_desktop_config.json
    - Windows: %APPDATA%\Claude\claude_desktop_config.json
    如果文件不存在,可以手动创建。

  2. 添加 MCP Server 配置

{
  "mcpServers": {
    "weather-server": {
      "command": "python",
      "args": ["/absolute/path/to/weather_server.py"],
      "env": {}
    }
  }
}

请将路径替换为你的 weather_server.py 的绝对路径。如果你使用的是虚拟环境,记得将 command 设为虚拟环境中的 python 解释器路径。

  1. 重启 Claude Desktop:完全退出后重新打开,Claude 会自动连接配置的 MCP Server。

验证连接:点击输入框旁的工具图标(🔧),你应该能看到 weather-server 及其下的 get_weather 工具,说明集成成功。

在对话中使用天气工具

现在你可以直接向 Claude 提问:

北京今天天气怎么样?

Claude 会判断需要调用 get_weather 工具,自动发送城市名“Beijing”,获取结果后组织回答:

北京当前天气:晴,气温 22°C(体感 21°C),湿度 35%。今天天气不错,适合户外活动哦!

整个过程完全在对话中完成,你再也不需要手动查询天气并粘贴结果了。

增强实战:集成更多工具

了解了基础流程后,你可以轻松扩展服务器,添加更多工具。例如,我们再加一个简单的“日期计算”工具,帮助 Claude 回答“100天后是几号”这类问题。只需在 list_toolscall_tool 中添加新条目:

# 在 list_tools 中追加
Tool(
    name="add_days_to_today",
    description="计算从今天起若干天后的日期(返回 YYYY-MM-DD 格式)。",
    inputSchema={
        "type": "object",
        "properties": {
            "days": {"type": "integer", "description": "增加的天数"}
        },
        "required": ["days"]
    }
)

# 在 call_tool 中添加处理分支
from datetime import date, timedelta
elif name == "add_days_to_today":
    days = int(arguments["days"])
    future = date.today() + timedelta(days=days)
    result_text = future.isoformat()
    return [TextContent(type="text", text=result_text)]

更新服务配置,重启 Claude Desktop 后,它就能同时使用天气和日期计算工具,组合完成任务。

常见问题与注意事项

1. 安全性:谁可以调用我的工具?

MCP 工具运行在本地,使用 stdio 传输时只有本机进程可以访问,相对安全。但如果 Server 通过 HTTP 暴露,务必加上鉴权(目前社区方案使用 OAuth 或 API Key)。切勿在工具中直接执行未经校验的用户输入,避免命令注入。对于敏感操作(如数据库写入),建议在工具内部做二次确认或限制。

2. 调试工具无响应?

  • 确认 weather_server.py 能独立运行,没有 import 错误。
  • 使用 mcp dev weather_server.py 单独测试,查看错误堆栈。
  • 检查配置文件路径、python 命令是否正确。在终端中手动执行命令确认可以启动。
  • Claude Desktop 的日志位于 ~/Library/Logs/Claude/(macOS),可作为排查线索。

3. 工具返回的内容 Claude 不识别

MCP 工具应返回清晰的文本描述,或者符合 TextContent 的结构。如果返回的是复杂 JSON,Claude 也可能处理,但最好在工具描述中说明返回格式。另外,避免过长的文本,必要时截断或分页。

4. 异步与性能

本示例使用了 httpx.AsyncClient 和异步 Server,这是推荐的模式。如果你的工具涉及 I/O 密集操作,务必使用异步库,防止阻塞整个 Server。对于 CPU 密集型任务,可考虑放入线程池执行。

5. 工具过多时的命名冲突

不同 MCP Server 可以注册同名工具吗?可以,但用户在 Claude Desktop 中会看到两个来源的工具,选择时容易混淆。建议为功能明确的 Server 起一个独立的名称,工具名保持简洁且语义唯一。

总结

通过本文,你不仅理解了 MCP 的设计理念,还亲手打造了一个实时天气查询工具,并成功将其接入 Claude Desktop。整个流程无需复杂的前端、鉴权,只需几十行 Python 代码,就让大模型与真实世界的数据流动了起来。

MCP 的出现极大地降低了 AI 集成的门槛,未来我们可以预见会有越来越多的工具服务器涌现,形成繁荣的生态。无论你是想连接企业内部 API、自动化日常任务,还是搭建个人知识库,MCP + Claude 的组合都提供了一个强大且灵活的解决方案。

现在就动手,把你重复性的提问变成工具,让 Claude 成为你真正的智能助手吧!


扩展阅读与资源
- MCP 官方文档
- MCP Python SDK
- wttr.in 天气 API 文档

更多推荐