
引言大语言模型LLM的能力已经不仅仅局限于对话越来越多的AI应用需要与外部工具、API和数据源交互。但长期以来让模型调用自定义工具的过程充满碎片化——每个平台都有自己的函数调用格式开发者不得不为 ChatGPT、Claude、开源模型分别实现不同的适配逻辑。Anthropic 推出的Model Context ProtocolMCP正在改变这一局面。它提供了一套开放标准让 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 请求 ServerServer 执行工具并返回结果LLM 再将结果组织成自然语言回复。两种传输方式MCP 支持两种底层传输协议stdio标准输入/输出Server 作为子进程启动通过标准 I/O 与 Client 通信。适合本地开发、个人工具。SSEServer-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 SDKAnthropic 提供了官方 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。首先全局安装 Inspectornpx modelcontextprotocol/inspector需要 Node.js 环境如果没有请先安装。启动 Inspector 并连接到你的 Servernpx 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.pyimport 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(timeout10.0) as client: resp await client.get(url, paramsparams) 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 fAPI 请求失败状态码{e.response.status_code} except Exception as e: return f获取天气时发生异常{str(e)} if __name__ __main__: mcp.run()这个版本展示了-异步工具函数声明为async defMCP 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(transportsse, host0.0.0.0, port8000)然后客户端通过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 插件