想让 AI 查询业务数据,可以先把一个确定、只读、容易验收的函数公开成 MCP 工具。本文用演示库存表实现 get_stock:按商品编码返回数量,再通过独立客户端验证正常查询、库存为零和未知商品。它是工具接入层的开发实战,不包含模型自主规划,也不会创建订单。
环境与验收目标
以下代码已在 Windows、Python 3.11、官方 mcp==2.2.0 环境执行。Python SDK v2 的入口是 from mcp.server import MCPServer。如果原项目使用 v1 的 FastMCP,请先检查依赖版本,不要把两套 API 混着复制。

库存字典是人为设计的演示数据:PEN-001 有 18 件,BOOK-002 有 0 件。我们的目标是准确区分“库存为零”与“商品不存在”,不能把查不到的编码悄悄当成零库存。
mkdir C:\mcp-demo
cd C:\mcp-demo
python -m venv .venv
.\.venv\Scripts\python.exe -m pip install "mcp==2.2.0"
编写服务器:输入、行为和失败边界都要明确
把下面代码保存为 inventory_server.py:
from mcp.server import MCPServer
from mcp.server.mcpserver.exceptions import ToolError
mcp = MCPServer("DemoInventory")
STOCK = {"PEN-001": 18, "BOOK-002": 0}
@mcp.tool()
def get_stock(sku: str) -> dict[str, str | int]:
"""Read demo stock for an exact SKU. Does not create orders or change stock."""
sku = sku.strip().upper()
if sku not in STOCK:
raise ToolError(f"Unknown SKU: {sku}")
return {"sku": sku, "quantity": STOCK[sku]}
if __name__ == "__main__":
mcp.run(transport="stdio")
@mcp.tool() 注册工具。sku: str 声明字符串输入;函数说明明确查询是只读的,不能下单或改库存。编码先去空格、转大写,再与演示表精确匹配。未知编码抛出官方 SDK 的 ToolError,使客户端收到可识别的失败结果。
返回字典提供两个字段:sku 是匹配后的编码,quantity 是整数数量。SDK 能基于类型生成输入与输出描述,但类型注解不能替代业务权限控制。如果以后接真实数据库,仍要在服务器里验证查询权限、数据范围及超时。
写一个协议客户端,不只直接调用 Python 函数
新建同目录的 check_stock.py。下面会启动服务器子进程,通过 MCP 发现工具并查询三个编码:
import asyncio
import sys
from pathlib import Path
from mcp import Client
from mcp.client.stdio import StdioServerParameters
async def main():
server = Path(__file__).with_name("inventory_server.py")
params = StdioServerParameters(command=sys.executable, args=[str(server)])
async with Client(params) as client:
listed = await client.list_tools()
print([tool.name for tool in listed.tools])
for sku in ["PEN-001", "BOOK-002", "MISSING"]:
result = await client.call_tool("get_stock", {"sku": sku})
print(sku, result.is_error, result.structured_content)
if result.is_error:
print(result.content)
if __name__ == "__main__":
asyncio.run(main())
.\.venv\Scripts\python.exe .\check_stock.py
sys.executable 使用正在运行客户端的 Python,避免子进程落到另一个未安装 SDK 的解释器。async with Client(...) 管理连接生命周期,退出时清理协议连接;不用自己把 JSON 消息塞进 stdin。
怎样判断这次开发成功
| 测试输入 | 预期结果 | 说明 |
|---|---|---|
| 工具发现 | 列表包含 get_stock,输入描述要求 sku 字符串 |
证明注册与发现有效 |
PEN-001 |
is_error=False,数量为 18 |
正常商品成功查询 |
BOOK-002 |
is_error=False,数量为 0 |
零库存是成功查询的结果 |
MISSING |
is_error=True,包含 Unknown SKU: MISSING |
不存在的商品没有被伪装成零库存 |
正常商品和未知编码已通过实际 stdio 协议调用核对。读者应把三个输入都跑完,再考虑把工具接给模型。只在 Python 中执行 get_stock("PEN-001") 能验证函数逻辑,不能证明 MCP 发现、序列化和错误返回都正常。
接给智能体前,哪些内容还没完成
- 模型编排: 本教程没有调用语言模型,也没有“查询后自动补货”的执行循环。宿主程序需要自己决定何时调用、如何展示结果以及何时停止。
- 真实库存: 字典没有持久化、并发更新或库存时点;真实查询应返回数据来源和更新时间,并明确可售库存口径。
- 权限: 这里只做只读演示。不要把修改库存、创建订单混进同一个模糊工具;具有写入行为的工具需要独立授权和幂等设计。
- 输入范围: 编码规范化是示例规则。如果真实编码区分大小写,应删除转大写逻辑,并以业务实际规则为准。
排查 No module named mcp 时,先核对运行命令是不是虚拟环境里的 Python。发现列表为空时检查装饰器与文件是否保存;返回错误时先看 is_error 和错误内容,别让模型把失败内容改写成成功库存。
相关问答与依据
为什么选择 tool,而不是 resource? 本例要求按显式参数执行一次查询并返回结果,使用工具便于展示输入约束和错误。只读并不自动等于 resource;资源通常通过 URI 表示可读取上下文,具体选择取决于客户端任务。
没有模型账号可以练习吗? 可以。这里的服务端与测试客户端仅在本地收发协议,不使用模型密钥。后续接入具体模型宿主时,再按它的账号与权限要求配置。
参考:官方工具注册和参数说明;官方 ToolError 与错误处理;官方 Client 调用、生命周期和结果字段。
Ai菜鸟网。发布者:AI小管家,转载请注明出处:https://www.alyyhw.com/30213.html