MCP 实战:用 30 行 Python 给大模型接一个自己的工具
大模型很会"说",但不会"做"。你让它查一下服务器还剩多少磁盘、读一下项目里的配置文件,它只能回你"我无法访问本地环境"。
MCP(Model Context Protocol)解决的正是这件事:它给模型和本地工具之间定了一套标准接口。写一次 Server,任何支持 MCP 的客户端都能直接调用。
三个角色,别搞混
- Host:你用的那个 AI 应用(IDE 插件、桌面客户端)
- Client:Host 内部的连接器,负责和 Server 通信
- Server:你写的程序,暴露工具、资源、提示词
Host 和 Server 之间走两种传输:本地进程用 stdio,远程用 SSE/HTTP。日常开发绝大多数场景是 stdio——客户端把你的程序当子进程拉起来,通过标准输入输出交换 JSON-RPC 消息。
最小可用的 Server
先装官方 SDK:
pip install mcp
然后一个能跑的文件:
from mcp.server.fastmcp import FastMCP
mcp = FastMCP("demo-tools")
@mcp.tool()
def disk_free(path: str = "/") -> str:
"""查询指定路径所在磁盘的剩余空间,返回人类可读的大小。"""
import shutil
total, used, free = shutil.disk_usage(path)
return f"总容量 {total // 2**30}G,已用 {used // 2**30}G,剩余 {free // 2**30}G"
@mcp.tool()
def read_head(path: str, lines: int = 20) -> str:
"""读取文本文件的前 N 行,用于快速预览配置或日志。"""
with open(path, "r", encoding="utf-8", errors="ignore") as f:
return "".join(f.readlines()[:lines])
if __name__ == "__main__":
mcp.run()
保存成 server.py,python server.py 能启动就说明协议层通了。
接进客户端
以桌面客户端为例,配置里加一段:
{
"mcpServers": {
"demo-tools": {
"command": "python",
"args": ["/绝对路径/server.py"]
}
}
}
重启客户端,模型就能看到 disk_free 和 read_head 两个工具。你问"磁盘还剩多少",它会自己去调,而不是让你手动跑命令。
三个容易踩的坑
1. stdout 是协议通道,不能乱打印。
stdio 模式下,标准输出走的是 JSON-RPC。你加一句 print("debug"),客户端立刻解析失败。调试日志一律走 stderr 或写文件。
2. 工具描述决定调用准确率。
模型是看 docstring 决定调不调、怎么调的。写"查询磁盘"不如写"查询指定路径所在磁盘的剩余空间,path 为绝对路径"。描述越具体,误调用越少。
3. 参数一定标类型。
def read(path: str, lines: int = 20) 里的类型注解不是装饰,SDK 靠它生成 JSON Schema。不写类型,客户端看到的参数结构是空的,模型只能瞎猜。
一点判断
MCP 的价值不在协议本身多复杂,而在于它把"给模型接工具"这件事从每家一个私有格式,变成了写一次到处能用。你现在写的一个 Server,本地 IDE 能用,桌面客户端也能用,将来换工具不用重写。
代价是多了个进程要管。所以我的建议是:当你要暴露的工具超过 3 个、或者需要在多个客户端复用,再上 MCP;只有一两个一次性脚本,直接命令行更快。
- 点赞
- 收藏
- 关注作者
评论(0)