☰
MCP服务开发实战:从零编写并挂载私有Python工具到智能体
2026/10/13 7:15:27 网站建设 项目流程

分享一下 MCP(Model Context Protocol)服务开发与私有工具挂载的完整落地过程。最近在接企业内部智能体时,最花时间的其实不是大模型本身,而是怎么把现有 Python 脚本、内部接口、私有数据安全地暴露给智能体,同时还要保证权限可控、调用可追踪。这套方案梳理清楚后,整套流程可以稳定复用到多个业务场景。这篇文章会从 MCP 的核心概念讲起,带大家从零编写一个私有 Python MCP 工具,再把工具挂载到智能体上完成一次真实调用,内容覆盖代码示例、完整配置、常见报错和工程落地建议,前后端开发、运维和算法同学都可以直接参考。

1. 背景与核心概念

1.1 MCP 是什么

MCP 全称 Model Context Protocol,翻译过来是“模型上下文协议”。它解决的核心问题是:大模型如何安全、标准化的访问外部数据和工具。

在没有 MCP 之前,要让大模型调用内部系统,通常需要为每个场景单独写一套工具调用逻辑。比如 A 系统用 HTTP 接口暴露,B 系统用 Python SDK,C 系统只有数据库权限,每一套的接入方式都不一样。大模型应用要对接 N 个系统,就要维护 N 套工具接入层,代码耦合高,权限也不好统一控制。

MCP 的思路是定义一套统一的协议层,把“大模型应用”和“外部工具/数据源”隔离开。大模型应用只需要理解 MCP 协议,工具提供方只需要按照 MCP 规范暴露自己的能力,两者通过标准化的消息格式通信。这样新增一个工具时,不需要改动大模型主程序,只需要在 MCP 服务端注册一个新工具即可。

1.2 私有工具与智能体挂载

这里的“私有工具”指的是不对外公开、只服务于企业内部智能体的工具。它通常包含三种类型:

  • 内部业务接口封装,例如查询订单、创建工单。
  • 私有数据查询,例如读取公司内部数据库。
  • 本地脚本能力,例如执行某个服务器上的运维脚本。

所谓“挂载”,是指把编写好的 MCP 工具注册到智能体运行时中,让大模型在对话中可以根据用户意图自动选择并调用这个工具。挂载完成后,用户用自然语言提问,智能体内部判断需要调用哪个工具,再把结果组织成答案返回给用户。

1.3 为什么需要自己开发私有工具

市面上已经有不少现成的 MCP 工具集,但大部分是面向公开数据源的。企业内部场景往往有很强的定制性,例如:

  • 数据表结构是私有的。
  • 接口鉴权方式特殊。
  • 工具输入参数需要结合业务语义。
  • 需要对接旧系统的非标准协议。

这些情况都必须自己编写 MCP 工具。自研工具的好处是:

  1. 可以完全控制工具的输入输出格式。
  2. 可以复用企业已有的 Python 代码资产。
  3. 可以在工具层加入权限校验、审计日志、限流逻辑。
  4. 不依赖第三方平台的稳定性。

1.4 常见应用场景

场景说明
企业内部知识问答智能体调用搜索工具,从私有知识库中检索答案
工单系统自动处理智能体调用建单工具,根据用户描述自动创建工单
数据报表查询智能体调用 SQL 查询工具,返回业务报表数据
运维自动化智能体调用脚本工具,完成日志检查、配置变更等操作
客户服务助手智能体调用客户信息查询工具,辅助客服快速定位问题

这些场景的共同点是:工具本身已经存在(Python 脚本、API、数据库操作),缺的是连接智能体的通道,而 MCP 就是这条通道。

2. 环境准备与版本说明

2.1 基础运行环境

本文的示例以 Linux / macOS 环境为主,Windows 系统的差异点会在文中单独说明。整体环境如下:

组件说明
操作系统Linux(CentOS 7+)或 macOS 12+
Python3.10 及以上
MCP SDKmcpPython 包(官方 SDK)
智能体运行时支持 MCP 协议的服务端或客户端
IDEVS Code / PyCharm
包管理工具pip / pipenv / poetry 均可

版本说明:MCP 协议目前仍在快速迭代中,不同版本的 SDK 在 API 命名上略有差异。本文的示例以常见的稳定版本写法为准,如果你安装的 SDK 版本较新,建议对照官方文档中的迁移说明调整导入路径和方法名。本文的重点是演示 MCP 工具开发的核心思路,而不是绑定某一个具体版本。

2.2 安装 MCP SDK

创建一个虚拟环境并安装依赖:

# 创建虚拟环境 python3 -m venv venv # 激活虚拟环境(Linux / macOS) source venv/bin/activate # Windows 系统激活命令 # venv\Scripts\activate # 安装 MCP SDK pip install mcp

如果想使用开发版或最新特性,可以安装 nightly 版本:

pip install mcp-nightly

非必要不建议在生产环境使用开发版。MCP SDK 会同时安装mcp命令行工具,安装完成后可以验证:

mcp --version

如果命令输出类似0.x.x或1.x.x的版本号,说明安装成功。

2.3 项目结构说明

本文的示例项目结构如下,后续所有代码都按这个结构组织:

mcp-demo/ ├── venv/ # 虚拟环境 ├── server.py # MCP 服务端入口 ├── tools/ │ ├── __init__.py │ ├── calculator.py # 示例工具:计算器 │ └── order.py # 示例工具:订单查询 ├── requirements.txt # 依赖清单 └── scripts/ └── test_client.py # 简易测试客户端

在实际项目中,你可以把工具按业务模块拆分到不同文件中,但入口文件统一暴露 MCP 服务。

3. MCP 工具开发核心原理

3.1 MCP 服务的基本构成

一个 MCP 服务通常包含三层结构:

  1. 协议层:负责 MCP 协议的握手、消息编解码、会话维护。
  2. 工具层:定义工具的名称、描述、输入参数和输出结构。
  3. 逻辑层:执行工具实际业务逻辑,例如访问数据库、调用 API。

在 Python SDK 中,我们主要关注工具层的编写。SDK 提供了装饰器或类继承两种方式来注册工具。

先看一个最简单的 MCP 服务示例:

# 文件路径:server.py from mcp.server.fastmcp import FastMCP # 创建 MCP 服务实例 mcp = FastMCP("demo-server") @mcp.tool() def hello(name: str) -> str: """简单的问候工具""" return f"Hello, {name}!" if __name__ == "__main__": mcp.run()

这段代码做了三件事:

  1. 创建了一个名为demo-server的 MCP 服务实例。
  2. 注册了一个名为hello的工具,工具接收一个字符串参数name,返回字符串。
  3. 调用mcp.run()启动服务。

FastMCP是官方 SDK 提供的高层封装,对于大多数业务工具开发来说已经足够。它的内部会自动处理协议细节,我们只需要关注工具函数的实现。

3.2 工具函数的输入输出规范

MCP 工具函数的参数是基于函数签名自动推断的。也就是说,函数的参数名、类型注解、默认值、文档字符串都会成为工具协议的一部分。

定义一个带多个参数的工具:

from typing import Optional @mcp.tool() def query_order( order_id: str, customer_name: Optional[str] = None, status: str = "all" ) -> dict: """ 查询订单信息。 Args: order_id: 订单编号。 customer_name: 客户名称,可选项。 status: 订单状态筛选,可选值 pending / paid / shipped / all。 """ # 这里省略实际业务逻辑,返回模拟数据 result = { "order_id": order_id, "customer_name": customer_name or "未知客户", "status": status, "items": [] } return result

这里有几点需要特别注意:

  • 类型注解必须写完整,工具参数的 JSON Schema 是根据类型注解自动生成的。
  • 文档字符串会被用作工具描述,大模型会根据这段描述判断何时调用该工具,所以描述要写清楚用途。
  • 返回值建议统一使用 dict 或符合 JSON 结构的类型,因为大模型消费的是结构化数据。
  • 可选参数使用 Optional 或设置默认值,避免大模型因为缺少参数而调用失败。

3.3 同步工具与异步工具

实际业务中会遇到需要等待外部接口响应的情况。MCP SDK 同时支持同步工具和异步工具,推荐使用异步方式处理 IO 密集型任务。

import asyncio @mcp.tool() async def fetch_remote_data(url: str) -> dict: """模拟调用远程接口获取数据""" await asyncio.sleep(1) # 模拟网络延迟 return {"url": url, "data": "mock data"}

如果业务逻辑是 CPU 密集型,例如复杂的计算任务,可以使用同步函数,让 SDK 自动调度到线程池执行。

@mcp.tool() def compute_hash(data: str) -> str: """计算字符串的 SHA256 哈希值""" import hashlib return hashlib.sha256(data.encode()).hexdigest()

3.4 工具错误处理

工具在执行过程中可能会遇到各种异常,例如:

  • 参数不合法。
  • 数据库连接失败。
  • 第三方接口返回错误。
  • 权限不足。

错误的处理方式会直接影响大模型的行为。如果工具抛出未捕获异常,大模型可能无法理解发生了什么。更推荐的做法是:捕获异常后返回结构化的错误信息。

@mcp.tool() def divide(a: float, b: float) -> dict: """两个数相除""" try: result = a / b return {"success": True, "result": result} except ZeroDivisionError: return { "success": False, "error": "除数不能为零", "code": "DIVIDE_BY_ZERO" } except Exception as e: return { "success": False, "error": f"计算失败: {str(e)}", "code": "UNKNOWN_ERROR" }

这样做的好处是:

  1. 大模型可以从error字段中知道失败原因。
  2. 业务方可以根据code字段做后续处理。
  3. 日志中保留了完整的错误上下文。

3.5 服务启动与连接方式

MCP 服务支持多种传输方式,常用的是:

  • stdio:标准输入输出传输,适合本地集成。
  • HTTP/SSE:通过 HTTP 协议提供服务的流式传输,适合远程部署。

FastMCP的run()方法默认使用 stdio。在本地调试时,可以直接运行脚本,然后用客户端连接:

python server.py

如果希望在网络中提供服务,可以显式指定传输方式。以下是一个 HTTPServer 示例:

# 文件路径:server.py(HTTP 版本) from mcp.server.fastmcp import FastMCP mcp = FastMCP("demo-server", host="0.0.0.0", port=8000) @mcp.tool() def ping() -> str: """探活工具""" return "pong" if __name__ == "__main__": mcp.run(transport="http")

配置传输方式时需要考虑网络环境和安全策略,服务器部署方式在生产环境需要配合访问控制和 TLS。

4. 完整实战:编写私有 Python MCP 工具并挂载到智能体

下面我们从零开始,编写一个带有实际业务含义的 MCP 工具集:一个“订单助手”工具,包含订单查询、订单统计和客户信息查询三个工具。整个过程分为:项目初始化、工具代码编写、服务启动与调试、挂载到智能体、完整调用验证。

4.1 初始化项目

创建项目目录并安装依赖:

mkdir mcp-order-demo && cd mcp-order-demo python3 -m venv venv source venv/bin/activate pip install mcp

创建requirements.txt文件:

mcp

创建项目所需的目录结构:

mkdir tools scripts touch tools/__init__.py

4.2 编写订单工具

先写订单查询工具。在实际业务中,这个工具可以对接内部订单数据库,为了方便演示,我们先用模拟数据代替。

# 文件路径:tools/order.py from typing import Optional # 模拟订单数据 MOCK_ORDERS = [ { "order_id": "ORD20250101", "customer_name": "张三", "amount": 299.00, "status": "paid", "create_time": "2025-01-01 10:00:00" }, { "order_id": "ORD20250102", "customer_name": "李四", "amount": 599.00, "status": "shipped", "create_time": "2025-01-02 11:30:00" }, { "order_id": "ORD20250103", "customer_name": "王五", "amount": 1299.00, "status": "pending", "create_time": "2025-01-03 09:15:00" } ] def query_order(order_id: str) -> dict: """ 根据订单编号查询订单详情。 Args: order_id: 订单编号,例如 ORD20250101。 """ for order in MOCK_ORDERS: if order["order_id"] == order_id: return {"success": True, "order": order} return {"success": False, "error": "订单不存在", "order": None} def list_orders(status: Optional[str] = "all") -> dict: """ 查询订单列表,可按状态筛选。 Args: status: 筛选状态,可选 all / pending / paid / shipped。 """ if status == "all": filtered = MOCK_ORDERS else: filtered = [o for o in MOCK_ORDERS if o["status"] == status] return {"success": True, "total": len(filtered), "orders": filtered} def order_statistics() -> dict: """ 统计订单总额和订单数量。 """ total_amount = sum(o["amount"] for o in MOCK_ORDERS) total_count = len(MOCK_ORDERS) paid_amount = sum(o["amount"] for o in MOCK_ORDERS if o["status"] == "paid") shipped_amount = sum(o["amount"] for o in MOCK_ORDERS if o["status"] == "shipped") pending_amount = sum(o["amount"] for o in MOCK_ORDERS if o["status"] == "pending") return { "success": True, "statistics": { "total_count": total_count, "total_amount": round(total_amount, 2), "paid_amount": round(paid_amount, 2), "shipped_amount": round(shipped_amount, 2), "pending_amount": round(pending_amount, 2) } }

这里把业务逻辑放在tools/order.py中,没有直接使用 MCP 装饰器,这是故意的。这样做的好处是业务函数可以被普通 Python 脚本复用,也可以被单测直接调用。MCP 协议层逻辑单独放在服务入口中。

4.3 编写 MCP 服务入口

在server.py中引入上面的业务函数,并注册为 MCP 工具。

# 文件路径:server.py from mcp.server.fastmcp import FastMCP from tools.order import query_order, list_orders, order_statistics # 创建 MCP 服务实例 mcp = FastMCP("order-assistant-server") @mcp.tool() async def query_order_tool(order_id: str) -> dict: """ 根据订单编号查询订单详情,包括订单状态、金额、客户名称和创建时间。 Args: order_id: 订单编号,例如 ORD20250101。 """ return query_order(order_id) @mcp.tool() async def list_orders_tool(status: str = "all") -> dict: """ 查询订单列表,可按订单状态筛选。 Args: status: 筛选状态,可选 all / pending / paid / shipped。 """ return list_orders(status) @mcp.tool() async def order_statistics_tool() -> dict: """ 统计全部订单的总金额、总数量和不同状态的金额分布。 """ return order_statistics() if __name__ == "__main__": # 默认以 stdio 方式启动,调试时可观察日志输出 mcp.run()

代码说明:

  1. mcp.tool()装饰器接收一个异步函数,自动注册为 MCP 工具。
  2. 每个工具的名称默认等于函数名,例如query_order_tool。
  3. 工具描述直接取函数文档字符串,建议在文档中写清楚参数含义和取值示例。
  4. 实际逻辑仍然调用tools/order.py中的普通函数,保持逻辑层纯净。

4.4 本地启动 MCP 服务

在项目根目录激活虚拟环境后运行:

python server.py

如果一切正常,服务会进入监听状态,但 stdio 模式下不会有明显的输出。此时可以编写一个简单的测试客户端来验证服务是否可用。先安装客户端依赖:

pip install "mcp[cli]"

然后使用mcp命令行工具连接:

mcp dev server.py

mcp dev是 SDK 提供的开发模式命令,会启动一个带调试面板的服务,可以在浏览器中查看工具列表并手动测试工具调用。如果没有图形界面环境,可以通过下面这个简单的 Python 客户端脚本验证:

# 文件路径:scripts/test_client.py import asyncio from mcp import ClientSession, StdioServerParameters from mcp.client.stdio import stdio_client async def main(): # 传入服务端启动命令 server_params = StdioServerParameters( command="python", args=["server.py"] ) async with stdio_client(server_params) as (read, write): async with ClientSession(read, write) as session: # 初始化会话 await session.initialize() # 获取工具列表 tools = await session.list_tools() print("可用工具列表:") for tool in tools.tools: print(f" - {tool.name}: {tool.description}") # 调用订单查询工具 result = await session.call_tool( "query_order_tool", {"order_id": "ORD20250101"} ) print("调用结果:") print(result) if __name__ == "__main__": asyncio.run(main())

运行测试客户端:

python scripts/test_client.py

预期会输出类似下面这样的内容:

可用工具列表: - query_order_tool: 根据订单编号查询订单详情,包括订单状态、金额、客户名称和创建时间。 - list_orders_tool: 查询订单列表,可按订单状态筛选。 - order_statistics_tool: 统计全部订单的总金额、总数量和不同状态的金额分布。 调用结果: content=[TextContent(text='{"success": true, "order": ...}', type="text")]

到这里,本地 MCP 服务已经开发完成,并且可以通过客户端正常调用。

4.5 挂载到智能体运行时

“挂载”这一步取决于你使用的智能体运行时,不同平台的挂载方式不同,但核心逻辑一致:让智能体知道“提供 MCP 服务的进程地址”和“可以调用哪些工具”。

方式一:本地智能体直接连接

如果你的智能体运行在本地,通常支持在配置文件中声明 MCP 服务地址。

例如某智能体客户端的配置文件:

{ "mcpServers": { "order-assistant": { "command": "python", "args": ["/path/to/mcp-order-demo/server.py"], "env": {} } } }

配置完成后,重启智能体,它就会通过 stdio 与你的 MCP 服务建立连接。

方式二:通过 HTTP 方式挂载

如果需要远程挂载,将 MCP 服务改为 HTTP 模式。

修改server.py的启动部分:

if __name__ == "__main__": # 绑定到 0.0.0.0 以允许外部访问,注意生产环境需要配合防火墙和鉴权 mcp.run(transport="http")

启动后服务监听在 8000 端口:

python server.py

在智能体配置中,将 MCP 服务地址指向 HTTP 接口:

{ "mcpServers": { "order-assistant": { "url": "http://your-server-ip:8000/mcp" } } }

需要注意不同智能体运行时对 HTTP 模式下 MCP endpoint 的路径规则可能不同,具体路径要以运行时文档为准。

4.6 智能体会话中的完整调用验证

挂载完成后,使用自然语言与智能体对话。下面是完整会话示例:

用户输入:

帮我查一下订单 ORD20250101 的详情。

智能体内部会执行类似这样的流程:

  1. 理解用户意图,判断需要订单查询工具。
  2. 从可用工具中选择query_order_tool。
  3. 将order_id = "ORD20250101"填入工具参数。
  4. 调用 MCP 服务,获得工具返回结果。
  5. 将工具返回的 JSON 数据整理成自然语言回复。

智能体最终回复:

订单 ORD20250101 详情如下: - 客户姓名:张三 - 订单金额:299.00 元 - 订单状态:paid(已支付) - 创建时间:2025-01-01 10:00:00

再试一个统计类问题:

用户输入:

我们目前的订单总体情况怎么样?

智能体会调用order_statistics_tool,然后回复:

截至目前,共有 3 笔订单,订单总金额为 2197.00 元。其中: - 已支付:299.00 元 - 已发货:599.00 元 - 待处理:1299.00 元

到这里,从编写 Python 工具到挂载智能体,整个流程已经跑通。

4.7 完整代码组织回顾

最后把完整的项目文件梳理一下:

mcp-order-demo/ ├── venv/ ├── server.py ├── requirements.txt ├── tools/ │ ├── __init__.py │ └── order.py └── scripts/ └── test_client.py

server.py完整内容如下:

# 文件路径:server.py from mcp.server.fastmcp import FastMCP from tools.order import query_order, list_orders, order_statistics mcp = FastMCP("order-assistant-server") @mcp.tool() async def query_order_tool(order_id: str) -> dict: """ 根据订单编号查询订单详情,包括订单状态、金额、客户名称和创建时间。 Args: order_id: 订单编号,例如 ORD20250101。 """ return query_order(order_id) @mcp.tool() async def list_orders_tool(status: str = "all") -> dict: """ 查询订单列表,可按订单状态筛选。 Args: status: 筛选状态,可选 all / pending / paid / shipped。 """ return list_orders(status) @mcp.tool() async def order_statistics_tool() -> dict: """ 统计全部订单的总金额、总数量和不同状态的金额分布。 """ return order_statistics() if __name__ == "__main__": mcp.run()

tools/order.py的内容在前面已经给出,这里不再重复。实际项目中,tools/order.py里的模拟数据应该替换为数据库查询或内部 API 调用。

5. 常见问题与排查思路

5.1 工具调用时报错 “Tool not found”

这个错误说明智能体运行时没有找到对应的工具。

可能原因:

  • MCP 服务没有成功注册该工具。
  • 智能体的工具缓存的还是旧列表。
  • 工具名称拼写不一致。

排查步骤:

  1. 确认服务端启动时没有报错。
  2. 使用mcp dev server.py查看当前工具列表。
  3. 检查智能体配置文件中的服务地址是否正确。
  4. 有些智能体运行时会缓存工具列表,重启后再试。

5.2 MCP 服务连接成功但调用超时

可能原因:

  • 工具内部有网络请求,而超时时间设置过短。
  • 工具内部死循环或阻塞调用。
  • 服务端处理能力不足。

解决思路:

  1. 将耗时操作改为异步实现。
  2. 在工具内部设置合理的连接超时。
  3. 将耗时长任务拆分为“提交任务 + 查询结果”两步。
  4. 查看服务端日志确认卡在哪个环节。

5.3 中文参数或返回值乱码

如果工具返回的中文内容出现乱码,通常和编码有关。

排查方向:

  1. 确认终端编码为 UTF-8。
  2. 在服务端脚本开头声明# -*- coding: utf-8 -*-。
  3. 检查数据库连接字符串中是否包含正确字符集参数。

需要注意有些智能体运行时的协议层默认使用 UTF-8,只要服务端返回的 JSON 是 UTF-8 编码,一般不会出现乱码。测试客户端打印result时如果乱码,可以改成repr(result)查看原始结构。

5.4 参数类型不匹配

智能体传给工具的参数有时是字符串,而工具函数期望数字。例如:

@mcp.tool() def divide(a: float, b: float) -> dict: ...

实际调用时可能传入:

{"a": "10", "b": "3"}

解决方案有下面几种:

  1. 在函数内部做类型转换。
  2. 提供更明确的参数 Schema,说明参数类型。
  3. 在工具入口做参数兼容处理。

推荐在工具函数内部统一做一次类型校验和转换:

def safe_float(value): try: return float(value) except (TypeError, ValueError): return None

5.5 常见问题速查表

问题现象常见原因解决思路
MCP 服务启动报 “ModuleNotFoundError”缺少依赖包激活虚拟环境后执行pip install -r requirements.txt
工具注册失败装饰器写法不正确确认使用了@mcp.tool(),且函数在服务实例创建之后定义
智能体无法发现工具挂载配置未刷新重启智能体运行时或清除工具缓存
工具调用结果为空业务函数返回 None确保工具返回 dict 或可序列化对象
异步工具报错Python 版本低于 3.8升级 Python 至 3.10+
Windows 下启动失败路径或编码问题检查 Python 解释器路径,确认工作目录正确

5.6 排查清单

按顺序排查,可以定位大多数问题:

  • [ ] MCP SDK 是否安装成功。
  • [ ] 服务端能否独立启动。
  • [ ] 客户端能否获取工具列表。
  • [ ] 客户端能否直接调用工具。
  • [ ] 智能体配置中的服务地址是否正确。
  • [ ] 智能体工具缓存是否刷新。
  • [ ] 服务端日志是否有异常堆栈。
  • [ ] 工具参数是否与智能体传参匹配。

6. 最佳实践与工程建议

6.1 工具设计规范

工具的设计直接影响智能体的调用成功率。建议遵循以下几点:

  • 工具名称使用动词开头,例如query_order、create_ticket、send_message,让大模型更容易理解工具用途。
  • 工具描述要包含适用场景,描述越具体,大模型越容易在正确场景下调用。
  • 参数尽量少且语义明确,不要设计大量可空参数。参数越多,大模型越容易填错。
  • 返回值结构化,统一使用success、data、error、code这样的字段结构。
  • 一个工具只做一件事,便于测试和复用。

6.2 错误处理策略

工具层是智能体与外部系统的边界,错误处理不能图省事。

推荐做法:

  1. 在工具入口捕获所有异常,避免异常直接抛到协议层。
  2. 错误信息要区分用户可读信息和内部日志信息,不要把敏感错误细节直接返回给用户。
  3. 对数据库、网络等外部依赖设置超时。
  4. 对于操作类工具,要考虑数据写入失败时的回滚。

6.3 安全边界与权限控制

私有 MCP 工具暴露给智能体后,相当于打开了一条新的调用通道。安全必须放在第一位。

# 示例:在工具内部实现简单的调用方校验 import os def check_caller_allowed(caller_token: str) -> bool: expected_token = os.getenv("MCP_CALLER_TOKEN", "") return caller_token == expected_token

更完整的做法是:

  1. MCP 服务启动时使用环境变量管理密钥,不硬编码在代码中。
  2. 工具内部二次校验是否有权限调用。
  3. 操作类工具记录操作人、操作时间、变更内容。
  4. 服务部署在内网环境,不直接暴露到公网。
  5. 通过 HTTP 传输时务必启用 TLS,配合网关鉴权。
  6. 对调用频率做限制,防止智能体在循环调用时打爆后端接口。

6.4 日志与审计

智能体调用的工具往往是业务敏感操作,日志是排障和审计的基础。

每次工具调用建议记录:

  • 调用时间。
  • 工具名称。
  • 入参(脱敏后)。
  • 出参(脱敏后)。
  • 耗时。
  • 调用方来源。

简单实现:

import logging import time logging.basicConfig(level=logging.INFO, format="%(asctime)s %(levelname)s %(message)s") def log_tool_call(tool_name: str, params: dict, result: dict, elapsed_ms: float): logging.info( f"[MCP_TOOL] {tool_name} params={params} " f"success={result.get('success')} elapsed={elapsed_ms:.2f}ms" )

6.5 测试策略

MCP 工具必须有测试,不能只靠智能体手工验证。

至少覆盖:

  • 单元测试:直接测试业务函数,确保逻辑正确。
  • 调用测试:通过 MCP 客户端调用工具,验证协议层正常。
  • 边界测试:空参数、缺少参数、超长字符串、非法值。
  • 故障测试:数据库不可用、接口超时、服务重启后能否恢复。

6.6 版本管理与部署

  • requirements.txt或pyproject.toml固定 SDK 版本范围。
  • 服务代码使用 Git 管理,每次修改都关联工具变更说明。
  • 部署时先在一台测试服务器上启动,用测试客户端验证后再接入生产智能体。
  • 多个智能体共用服务时,在服务层加租户隔离逻辑。

7. 总结与学习路线

这篇文章从 MCP 协议的核心概念出发,完整演示了私有 Python 工具的编写、服务启动、客户端测试和智能体挂载流程。掌握的核心内容包括:MCP 服务的目录结构和启动方式;FastMCP装饰器的工具注册方法;工具函数的输入输出规范与错误处理;本地调试和智能体挂载的完整链路。

接下来可以继续深入的方向包括:

  • 学习 MCP 协议中 Resource 与 Prompt 两种能力类型,补充工具之外的模型上下文能力。
  • 研究 MCP SDK 的底层消息格式,理解协议握手和会话管理。
  • 尝试接入真实 MySQL 或 Redis,让工具操作真实业务数据,补齐事务和连接池管理经验。
  • 熟悉主流智能体运行时的 MCP 挂载配置和权限模型,适配公司内部的平台。
  • 关注 MCP 官方版本迭代,及时跟进接口变更和安全建议。

在实际项目中接入私有 MCP 工具时,优先关注三件事:工具入参出参是否结构化、错误信息是否能被大模型理解、调用链路是否有审计日志。把这三件事做好,开发和排障成本会大幅降低。如果这篇指南对你有帮助,收藏备用,也可以在评论区聊聊你在 MCP 开发中遇到的坑。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询