前面我们已经知道,MCP 能让 AI 调用工具。
这次直接动手:写一个查询工具,接进 Cherry Studio,让 AI 帮我们查项目缺陷。
不需要公司接口,也不需要数据库。先用几条虚构数据,把流程跑通。
一、先准备环境
电脑需要安装 Python 3.11 或更高版本,以及 Cherry Studio。
新建一个文件夹,命名为 mcp-demo。后面的两个代码文件,都放在里面。
Mac 在这个文件夹打开终端,执行:
bash
python3 -m venv .venv
.venv/bin/python -m pip install mcp==2.3.0 pydantic==2.14.1
Windows 在文件夹打开 PowerShell,执行:
powershell
py -3 -m venv .venv
.\.venv\Scripts\python.exe -m pip install mcp==2.3.0 pydantic==2.14.1
这里固定依赖版本,方便大家按同一份代码操作。
二、写一个能查缺陷的工具
新建 run.py,复制下面的完整代码:
python
import json
import sys
from pathlib import Path
from typing import Annotated, Any, Literal
from pydantic import Field
from mcp.server import MCPServer
mcp = MCPServer("team-issues")
ROWS = [
{"id": "DEMO-001", "title": "按钮重复响应", "status": "open"},
{"id": "DEMO-002", "title": "报告标题异常", "status": "open"},
{"id": "DEMO-003", "title": "空列表提示缺失", "status": "closed"},
{"id": "DEMO-004", "title": "筛选条件未保留", "status": "open"},
]
@mcp.tool(description="查询虚构项目的缺陷,只读,不连接真实系统。")
def list_project_issues(
project: Literal["demo-project"],
status: Literal["open", "closed"] = "open",
limit: Annotated[int, Field(strict=True, ge=1, le=20)] = 2,
) -> dict[str, Any]:
matched = [row for row in ROWS if row["status"] == status]
issues = matched[:limit]
return {
"source": "local-fixture",
"project": project,
"issues": issues,
"returned_count": len(issues),
"matched_total": len(matched),
"has_more": len(issues) < len(matched),
}
if __name__ == "__main__":
if "--config" in sys.argv:
print(json.dumps({
"command": sys.executable,
"args": [str(Path(__file__).resolve())],
"env": {
"PYTHONUTF8": "1",
"PYTHONIOENCODING": "utf-8",
},
}, ensure_ascii=False, indent=2))
else:
mcp.run(transport="stdio")
这段代码做了三件事:
- 准备四条演示缺陷。
- 提供
list_project_issues工具。 - 允许按状态查询,并限制返回数量。
open 表示未关闭,closed 表示已关闭。
注意:这里的 has_more 只是提醒"还有记录没展示",这个小示例没有实现翻页。
三、先检查工具能不能调用
别急着接 AI,先确认工具本身没问题。
新建 check.py,和 run.py 放在同一个文件夹:
python
import asyncio
import json
import sys
from pathlib import Path
from mcp import Client, StdioServerParameters
async def main():
params = StdioServerParameters(
command=sys.executable,
args=[str(Path(__file__).with_name("run.py").resolve())],
env={
"PYTHONUTF8": "1",
"PYTHONIOENCODING": "utf-8",
},
)
async with Client(params) as client:
tools = (await client.list_tools()).tools
assert any(t.name == "list_project_issues" for t in tools)
result = await client.call_tool("list_project_issues", {
"project": "demo-project",
"status": "open",
"limit": 2,
})
assert not result.is_error
data = result.structured_content
assert data["source"] == "local-fixture"
assert data["returned_count"] == 2
assert data["matched_total"] == 3
assert data["has_more"] is True
assert [x["id"] for x in data["issues"]] == [
"DEMO-001", "DEMO-002",
]
print(json.dumps(data, ensure_ascii=False, indent=2))
print("检查通过:服务可连接,工具能调用。")
if __name__ == "__main__":
asyncio.run(main())
Mac 执行:
bash
.venv/bin/python check.py
Windows 执行:
powershell
.\.venv\Scripts\python.exe check.py
正常情况下,会显示两条缺陷,并输出:
text
检查通过:服务可连接,工具能调用。
这一步通过,说明本地 MCP 服务能连接、工具能调用。接下来再检查 AI 客户端。
四、生成自己电脑上的配置
Mac 执行:
bash
.venv/bin/python run.py --config
Windows 执行:
powershell
.\.venv\Scripts\python.exe run.py --config
终端会输出三个配置项:
| 配置项 | 含义 |
|---|---|
command |
用哪个 Python 启动服务 |
args |
要启动的 run.py 完整路径 |
env |
中文编码设置 |
直接用自己电脑生成的内容,别照抄别人电脑上的路径。
移动项目文件夹或重建虚拟环境后,需要重新生成。
五、接进 Cherry Studio
在 Cherry Studio 的设置中,找到 MCP 服务器管理,添加本地服务。
按下面填写:
| 配置项 | 填写内容 |
|---|---|
| 名称 | team-issues |
| 类型 | stdio,本地命令方式 |
| 命令 | 刚才输出的 command |
| 参数 | args 中的脚本完整路径 |
| 环境变量 | env 中的两项编码设置 |
命令和参数要分开填。--config 只是用来查看配置,不要把它填进客户端的启动参数。
保存并启用,检查工具列表中有没有:
text
list_project_issues
然后,在当前 Agent 的 MCP 设置中启用 team-issues。不同版本的入口文字可能有调整,重点是:服务添加后,还要给当前使用的 Agent 开启。
入口可参考 Cherry Studio 官方 MCP 指南。
六、让 AI 真正调用一次
选择支持工具调用的模型,新建对话,发送:
text
请调用 list_project_issues 工具:
project 填 demo-project
status 填 open
limit 填 2
告诉我本次返回几条、总共有几条未关闭缺陷。
只根据工具结果回答,不要补充不存在的数据。
正常结果应该是:
text
本次返回 2 条缺陷,共有 3 条未关闭缺陷。
DEMO-001:按钮重复响应
DEMO-002:报告标题异常
还有未展示的记录。
检查时,先看有没有实际的工具调用记录,再看回答内容。
模型只说一句"查询成功",不能证明它真的调用了工具。
七、卡住了,先检查这三处
**服务启动失败:**先运行 check.py,确认代码和依赖没有问题,再检查客户端中的路径。
**服务连接了,但 AI 不调用:**检查当前 Agent 是否启用了工具,以及所选模型是否支持工具调用。
**回答说总共只有两条:**让它区分 returned_count 和 matched_total。前者是本次返回数量,后者是符合条件的总量。
本文代码已在 Mac 上完成本地 stdio 调用检查。Windows 命令、Cherry Studio 界面操作和真实模型调用,仍需在你的电脑上验证。