1. 从“它没有这个能力”说起:MCP 到底是个啥
先说结论:MCP 全称 Model Context Protocol,模型上下文协议,是一套让智能体用统一姿势调用外部工具的开放约定。你可以把它理解成“AI 世界的 USB-C 接口”——以前每个软件接工具都各玩各的,你给 A 写的插件搬到 B 就废了;有了 MCP,工具写一遍,所有支持这套协议的客户端拿来就能用。
我为什么会去折腾它?起因特别朴素。平时写代码、查资料都靠一个桌面端 AI 助手搭把手,用着用着就想让它帮我盯一下服务器。我随口问“看看 C 盘还剩多少空间”,它客客气气回我一句“我没有这个能力”。行吧,复制粘贴大法继续。来回几次我烦了,这活儿不该这么蠢,于是决定自己动手。
扒资料的时候 MCP 这个词反复往外蹦。搞清楚之后发现它的角色拆分很清晰:跑智能体的软件算客户端,提供工具的一方写 MCP 服务器,两边按协议消息来往。对个人开发者来说这个思路太友好了——不用给每个软件单独伺候一遍。
这篇就聚焦一件事:用 Python SDK 从零写一个 stdio 型 MCP 小工具,接给智能体,并且用一次真实的工具调用验证它跑通。全程可复制,环境是 Windows 11 + Python 3.12,其他系统把路径换一下就行。适合谁?会写 Python 函数、想让 AI 帮自己干重复活的人。半天时间足够。
2. 前置准备:装 SDK、拿统一 Key、理清最小结构
动手之前把三件事办了,后面会顺很多。
第一件,装官方 SDK。命令行一行搞定:
pip install "mcp[cli]"装完可以顺手验证一下版本,避免装到太老的包:
pip show mcp第二件,准备一个统一 Key 通道。写 MCP 工具经常要调模型或外部接口,如果每个工具都单独配一套 Key,管理起来很乱。我这边用的是 TaoToken 的统一 Key 通道,一个 Key 走通模型对话和工具调用,省得在多个配置文件里来回改。先去控制台把 Key 建出来:
- 控制台入口:https://taotoken.net/console?utm_source=taotoken_aicg_blog_end&utm_medium=csdn&utm_campaign=rewrite&utm_content=
- API Key 管理:https://taotoken.net/api-keys?utm_source=taotoken_aicg_blog_end&utm_medium=csdn&utm_campaign=rewrite&utm_content=
拿到 Key 之后先别急着写进代码,等会儿在配置片段里统一填。接口地址用https://taotoken.net/api,注意这个不带 UTM 参数,是给程序调用的。
第三件,理清 MCP 服务器的最小结构。一个 stdio 型服务器其实就三块:起一个 FastMCP 实例、用装饰器登记工具函数、最后mcp.run()走 stdio 通道。没有端口,没有路由,比想象中简单太多。
这里有个细节值得单独拎出来说:函数下面的说明文字会原样喂给模型,成为它理解这个工具是干嘛的依据。说明写得含糊,模型要么不理你,要么瞎传参数。我后来才咂摸出这个味道——工具说明文字的质量,直接决定模型用不用你的工具、怎么传参。所以别偷懒,docstring 写清楚。
3. 可复制配置:三十行 server 代码 + 客户端 JSON
先上服务器代码。新建一个文件叫my_mcp_server.py,内容如下:
import shutil import json import urllib.request from mcp.server.fastmcp import FastMCP mcp = FastMCP("my-tools") @mcp.tool() def disk_free(drive: str = "C:") -> str: """查询指定磁盘的剩余空间。drive 参数传盘符,例如 C: 或 D:。""" usage = shutil.disk_usage(drive + "\\") free_gb = usage.free / (1024 ** 3) total_gb = usage.total / (1024 ** 3) percent = usage.used / usage.total * 100 return f"{drive} 剩余 {free_gb:.1f}G,总容量 {total_gb:.1f}G,占用率 {percent:.0f}%" @mcp.tool() def ip_location() -> str: """查询本机公网 IP 的归属地信息。""" with urllib.request.urlopen("https://ipapi.co/json/", timeout=5) as resp: data = json.loads(resp.read().decode()) return f"IP {data.get('ip')} 归属 {data.get('city')}, {data.get('country_name')}" if __name__ == "__main__": mcp.run()两个工具,一个查磁盘,一个查 IP 归属地。@mcp.tool()装饰器负责登记,docstring 负责告诉模型这个工具干嘛用。最后mcp.run()默认走 stdio 通道。
接下来是客户端登记。配置就是一小段 JSON,关键字段两个:command填 Python 解释器的完整路径,args放脚本的具体位置,写到文件名为止。假设你的客户端支持 MCP 配置,片段长这样:
{ "mcpServers": { "my-tools": { "command": "C:/Users/yourname/AppData/Local/Programs/Python/Python312/python.exe", "args": [ "C:/Users/yourname/projects/my_mcp_server.py" ], "env": { "PYTHONUTF8": "1", "TAOTOKEN_API_KEY": "你的统一Key", "TAOTOKEN_BASE_URL": "https://taotoken.net/api" } } } }三个地方要盯紧:command必须是解释器完整路径,不能只写python;JSON 里的反斜杠要么转义要么全写成正斜杠,我干脆全用正斜杠;env里把PYTHONUTF8设成 1,Windows 控制台的中文乱码能少一大半。
如果你用的是 Claude Code 这类工具,配置思路一样,把 Base URL、Key、Model ID 三件套填全就行。Model ID 按你实际用的模型填,别照抄别人的。保存配置后重启客户端,设置页里应该能冒出你登记的那两个工具。
4. 验证请求:一次工具调用确认跑通
配置保存、客户端重启之后,别急着高兴,先做一次真实验证。
打开对话窗口,直接问:“C 盘还剩多少空间?”如果一切正常,模型不会跟你道歉,而是发起一次工具调用,拿到数之后告诉你结果。我这边实测返回的是“C 盘剩余 86G,占用率过七成”,还顺嘴提醒了一句。那一下体验挺奇妙——屏幕对面的模型自己决定调哪个工具、传什么参数,我写的代码在后台被它指挥着跑。
想更严谨一点,可以再问一句“帮我查下公网 IP 归属地”,看它会不会调第二个工具。两个工具都能被正确触发,说明 stdio 通道、工具登记、参数传递这条链路全通了。
如果你还想单独验证模型通道本身,可以走模型对话入口发一条测试请求:
- 模型对话:https://taotoken.net/chat?utm_source=taotoken_aicg_blog_end&utm_medium=csdn&utm_campaign=rewrite&utm_content=
这一步的意义在于把“工具调用”和“模型通道”分开验证。工具没触发,可能是配置问题;模型没响应,可能是 Key 或 Base URL 问题。分开查,定位快很多。
验证通过之后,你可以试着把工具说明改得模糊一点,比如把 docstring 删掉,再问一次同样的问题,大概率模型就不理你了。这个反向实验能让你直观感受到说明文字的分量。
5. 常见报错排查:401、invalid JSON、OAuth 这些坑
跑通过程当然不是一路绿灯,坑都记下来了,对照着查能省不少时间。
报错一:401 Unauthorized。这个基本是 Key 的问题。检查TAOTOKEN_API_KEY有没有填对,有没有多余空格,Base URL 是不是写成了带 UTM 的地址。程序调用要用https://taotoken.net/api,别把浏览器地址粘进去。如果 Key 刚建好,确认一下有没有复制完整。
报错二:invalid JSON 或连接反复断。这个坑我踩得最狠。stdio 通道是拿来做协议通信的,我在代码里顺手print了几行调试日志,通道被污染,客户端直接报 invalid JSON。解决办法很简单:所有日志改走stderr,别用print。比如import sys; print("debug", file=sys.stderr)。
报错三:local proxy failed 或连接超时。先确认你的网络环境正常,再检查客户端配置里的command路径对不对。路径写错、解释器找不到,表现就是连不上。另外确认脚本本身能独立跑起来——命令行直接python my_mcp_server.py,如果不报错、安静地等着,说明服务器没问题。
报错四:OAuth 相关提示。有些客户端在接入远程服务时会走 OAuth 流程。如果你只是本地 stdio 工具,一般用不到;如果确实遇到,检查客户端的认证配置,确认 Key 是通过env传进去的,而不是写死在代码里。
报错五:中文乱码,报错一半是问号。Windows 控制台的老问题。给环境变量加PYTHONUTF8=1,世界清净。这个在配置的env字段里加上就行。
排查顺序建议这样:先确认脚本能独立运行,再确认客户端配置路径正确,然后看 Key 和 Base URL,最后查日志输出有没有污染 stdio 通道。按这个顺序走,大部分问题十分钟内能定位。
6. 跑通之后:把重复劳动甩出去
半天折腾下来,说几句实在的。
门槛比想象中低。会写 Python 函数就能写 MCP 服务器,协议细节 SDK 全包圆了,真正要上心的是工具说明文字写清楚。这可能是智能体生态里少有的公共标准——模型能力各家卷各家的,但工具接入这一层,大家愿意坐下来用一个协议,对开发者是实打实的方便。
我下一步打算也定了,把服务器的告警查询和日志检索包成工具接进去。以后半夜服务器闹脾气,我躺着问一句,让 AI 自己翻日志,不用再爬起来开电脑。
你手头要是也有想甩出去的重复劳动,照这个路子走一遍,半天足够折腾出自己的第一个 MCP 服务器。需要长期跑编码或 Agent 任务的,可以看看 Coding Plan:
- Coding Plan:https://taotoken.net/coding-plan?utm_source=taotoken_aicg_blog_end&utm_medium=csdn&utm_campaign=rewrite&utm_content=
- 接入文档:https://taotoken.net/doc?utm_source=taotoken_aicg_blog_end&utm_medium=csdn&utm_campaign=rewrite&utm_content=
最后留个小技巧:工具别一次写太多,先写一个能跑通的,验证链路没问题再往上加。我第一个工具就查磁盘,跑通之后加第二个、第三个,心里踏实。