引言
大语言模型(LLM)的能力已经不仅仅局限于对话,越来越多的AI应用需要与外部工具、API和数据源交互。但长期以来,让模型调用自定义工具的过程充满碎片化——每个平台都有自己的函数调用格式,开发者不得不为 ChatGPT、Claude、开源模型分别实现不同的适配逻辑。
Anthropic 推出的Model Context Protocol(MCP)正在改变这一局面。它提供了一套开放标准,让 AI 应用能够通过统一的协议发现和调用工具,无论是本地脚本、远程 API 还是数据库查询,都可以被封装成 MCP 插件(MCP Server)。这意味着你只需编写一次工具,就可以被任何支持 MCP 的客户端(如 Claude Desktop、VS Code 扩展)直接使用。
本文将以实战为导向,带你从零开发一个完整的 MCP 插件。我们会先理解 MCP 的核心架构,然后用 Python 编写一个可运行的天气查询插件,最后探讨生产环境中的常见问题与最佳实践。读完你会发现,构建一个 MCP 插件比你想象的要简单得多。
核心概念:MCP 是如何工作的?
在动手编码前,有必要先理清 MCP 的几个关键角色和通信方式。
MCP 的基本架构
MCP 采用经典的客户端-服务器模型,但针对 AI 场景进行了优化:
- MCP Host:运行 AI 模型的主程序,比如 Claude Desktop、IDE 插件。
- MCP Client:运行在 Host 内部的协议客户端,负责与 MCP Server 建立连接、管理生命周期。
- MCP Server:你将要开发的插件,它对外暴露一组工具(Tools)、资源(Resources)、提示词(Prompts)。
其中,工具(Tool)是 MCP 中最常用的概念,对应一个可以被模型调用的具体功能(例如get_weather)。当用户向 AI 询问“北京今天天气怎么样?”,Host 会通过 MCP Client 请求 Server,Server 执行工具并返回结果,LLM 再将结果组织成自然语言回复。
两种传输方式
MCP 支持两种底层传输协议:
- stdio(标准输入/输出):Server 作为子进程启动,通过标准 I/O 与 Client 通信。适合本地开发、个人工具。
- SSE(Server-Sent Events):基于 HTTP,支持远程 Server,允许多个 Client 共享。适合团队或生产环境。
本文的示例将使用 stdio 传输,因为它零配置、最适合快速上手。
工具定义的要素
一个 MCP 工具需要包含以下信息:
- 名称:唯一标识,如
get_weather - 描述:自然语言说明,模型会依据描述决定何时调用
- 输入参数:JSON Schema 定义,包含参数名、类型、是否必填等
- 执行逻辑:具体的业务代码,返回字符串或结构化数据
清楚了这些之后,我们立刻开始实战。
实战示例:开发一个天气查询 MCP 插件
环境准备
确保你的开发环境满足以下条件:
- Python 3.10 或更高版本
- pip 包管理器
- 一个支持 MCP 的客户端用于测试,推荐使用官方 MCP Inspector 或 Claude Desktop
(如果你还没有 Claude Desktop,可以先用 Inspector 完成测试,安装方法后面会介绍。)
第一步:安装 MCP Python SDK
Anthropic 提供了官方 Python SDKmcp,它内置了FastMCP高层封装,让我们可以像写普通函数一样定义工具。
打开终端,创建项目目录并安装依赖:
mkdir weather-mcp-server cd weather-mcp-server python -m venv .venv source .venv/bin/activate # Windows: .venv\Scripts\activate pip install mcp此时你的环境中就拥有了开发 MCP 插件的全部能力。
第二步:编写 Server 代码
新建文件weather_server.py,输入以下完整代码:
from mcp.server.fastmcp import FastMCP # 初始化 FastMCP 实例,参数为插件名称 mcp = FastMCP("Weather") # 使用 @mcp.tool() 装饰器定义工具 @mcp.tool() def get_weather(city: str) -> str: """ 获取指定城市的天气信息。 参数 city: 城市名称,例如 北京、上海。 """ # 模拟天气数据,实际项目中可以调用天气 API weather_data = { "北京": "晴,气温 25°C,湿度 40%", "上海": "多云转阴,气温 28°C,湿度 65%", "深圳": "阵雨,气温 30°C,湿度 80%", "纽约": "晴,气温 22°C,湿度 55%", } result = weather_data.get(city) if result is None: return f"抱歉,未找到城市“{city}”的天气信息" return f"{city}天气:{result}" if __name__ == "__main__": # 启动服务器,默认使用 stdio 传输 mcp.run()代码说明:
FastMCP("Weather")创建了一个名为Weather的 MCP Server,它将被客户端识别。@mcp.tool()将下方的函数注册为一个工具。函数名get_weather自动成为工具名。- 函数文档字符串(docstring)会作为工具的描述信息,模型依赖该描述理解工具功能。
- 参数
city: str定义了输入模式,MCP SDK 会自动生成 JSON Schema,无需手动编写。 - 主程序入口调用
mcp.run()启动 stdio 服务器,监听来自客户端的请求。
这就是一个完整的 MCP 插件!没有复杂的配置,没有额外的注册步骤,你只需专注业务逻辑。
第三步:测试你的插件
使用 MCP Inspector 测试
MCP Inspector 是官方提供的调试工具,可以图形化地测试本地的 MCP Server。
首先全局安装 Inspector:
npx @modelcontextprotocol/inspector(需要 Node.js 环境,如果没有请先安装。)
启动 Inspector 并连接到你的 Server:
npx @modelcontextprotocol/inspector python weather_server.py浏览器会自动打开 http://localhost:5173,你将看到一个交互界面:
- 左侧列出了检测到的工具get_weather
- 点击工具,填写参数{"city": "北京"},点击执行
- 右侧会显示返回的结果
看到预期的天气信息,说明你的插件工作正常。
集成到 Claude Desktop
如果你安装了 Claude Desktop(目前支持 macOS 和 Windows),可以通过配置文件将我们的插件加入。
找到 Claude Desktop 的配置文件:
- macOS:~/Library/Application Support/Claude/claude_desktop_config.json
- Windows:%APPDATA%\Claude\claude_desktop_config.json
如果没有该文件,手动创建一个。加入以下内容:
{ "mcpServers": { "weather": { "command": "python", "args": ["/你的路径/weather_server.py"] } } }将路径替换为你的weather_server.py绝对路径。重启 Claude Desktop,然后尝试在对话中输入:“北京今天天气怎么样?”,模型会自动调用get_weather工具并给出回复。
进阶:开发一个支持异步和错误处理的生产级插件
上面的示例虽然能跑,但在真实项目中你可能需要调用外部 HTTP API、处理超时、优雅地返回错误。下面展示一个更健壮的版本,它使用httpx异步请求真实天气 API(以 OpenWeatherMap 为例,你需要注册获取 API Key)。
安装额外依赖:
pip install httpx python-dotenv创建weather_pro_server.py:
import os import httpx from mcp.server.fastmcp import FastMCP from dotenv import load_dotenv load_dotenv() # 加载 .env 中的 API_KEY mcp = FastMCP("WeatherPro") @mcp.tool() async def get_weather(city: str, units: str = "metric") -> str: """ 获取指定城市的实时天气数据。 city: 城市名,支持英文如 Beijing、London units: 温度单位,metric(摄氏度) 或 imperial(华氏度) """ api_key = os.getenv("OPENWEATHER_API_KEY") if not api_key: return "错误:未配置天气 API Key,请在 .env 文件中设置 OPENWEATHER_API_KEY" url = "https://api.openweathermap.org/data/2.5/weather" params = { "q": city, "appid": api_key, "units": units, "lang": "zh_cn" } try: async with httpx.AsyncClient(timeout=10.0) as client: resp = await client.get(url, params=params) resp.raise_for_status() data = resp.json() desc = data["weather"][0]["description"] temp = data["main"]["temp"] humidity = data["main"]["humidity"] return f"{city}天气:{desc},温度{temp}°,湿度{humidity}%" except httpx.HTTPStatusError as e: if e.response.status_code == 404: return f"城市“{city}”未找到" return f"API 请求失败,状态码:{e.response.status_code}" except Exception as e: return f"获取天气时发生异常:{str(e)}" if __name__ == "__main__": mcp.run()这个版本展示了:
-异步工具:函数声明为async def,MCP SDK 原生支持异步执行,不会阻塞其他请求。
-环境变量管理:使用.env存储敏感信息,避免硬编码。
-结构化错误处理:针对 HTTP 错误、城市未找到、超时等情况返回有意义的提示,而不是抛出异常让客户端报错。
常见问题与注意事项
1. 工具函数的类型提示是必须的吗?
强烈建议加上。MCP SDK 会利用 Python 的类型注解自动生成 JSON Schema。如果不加类型提示,所有参数都会被当作any类型,客户端无法正确校验参数,模型的调用准确性也会下降。
2. 如何调试错误?
- 查看 Server 的 stderr 输出:当使用 stdio 传输时,打印到
sys.stderr的信息会显示在启动它的终端或客户端日志中。因此建议使用logging模块输出到 stderr。 - 使用 MCP Inspector:它会在页面上显示任何工具执行时抛出的异常。
- 增加详细的返回值:在开发阶段,让工具返回更详细的错误信息,帮助快速定位问题。
3. 可以选择 SSE 传输替代 stdio 吗?
可以。如果你的工具需要部署到服务器并被多个客户端远程调用,使用 SSE 更合适。修改方式很简单,将mcp.run()替换为:
mcp.run(transport="sse", host="0.0.0.0", port=8000)然后客户端通过http://your-server:8000/sse连接。注意 SSE 需要处理跨域和鉴权,生产环境下建议在前面加一层反向代理(如 Nginx)。
4. 如何限制工具的调用权限?
MCP 协议本身不提供细粒度的权限模型。安全控制通常在这些层面实现:
- 在 Server 内部检查调用来源或进行用户认证(例如通过自定义 Header)。
- 如果你使用 Claude Desktop 等 Host,确保配置文件中的 command 和 args 都是安全可信的,防止任意代码执行。
- 对于敏感操作(如删除文件、执行命令),在工具内再次要求用户确认或记录审计日志。
5. 工具可以返回图片、文件等非文本内容吗?
MCP 资源(Resources)专门用于暴露文件、图片、数据库表等结构化内容。工具通常返回文本,但资源可以返回二进制数据。你可以同时暴露工具和资源,让模型根据需求选择调用。示例:
from mcp.server.fastmcp import FastMCP from mcp.types import Resource mcp = FastMCP("ImageProvider") @mcp.resource("image://{filename}") def get_image(filename: str) -> bytes: with open(f"./images/{filename}", "rb") as f: return f.read()资源定义使用mcp.resource()装饰器,路径可以是 URI 模板。
总结
通过本文,我们完整地走过了从概念到代码的 MCP 插件开发流程。你学到了:
- MCP 的客户端-服务器架构及 stdio、SSE 两种传输方式
- 使用 FastMCP 定义工具、资源和异步处理
- 用 MCP Inspector 和 Claude Desktop 进行测试
- 生产环境中处理错误、安全、远程部署的注意事项
MCP 正在成为 AI Agent 与外部世界交互的“USB 接口”,越来越多的工具和平台开始原生支持。现在,你可以将任何 Python 函数封装成标准化的 MCP 工具,无论是本地脚本、企业内部 API 还是云端服务,都能无缝接入 AI 工作流。
下一步,你可以尝试将手头常用的脚本(数据库查询、文件操作、通知推送)改造成 MCP 插件,或者为团队开发一套标准化的研发工具集。整个协议是开放的,生态也在快速成长,早一步掌握 MCP 开发,就能早一步享受到统一接口带来的效率红利。
完整代码已同步在 GitHub Gist,欢迎取用并部署你的第一个 MCP 插件!