最近掘金热榜被 MCP 和 Skill 刷屏了,什么「写 Skill 行业就死了」「Prompt、Agent、Function Call、Skill、MCP 傻傻分不清楚」,看得我一愣一愣的。
说实话,MCP 这东西没大家想的那么玄乎。我花了一个下午,用 FastMCP 3.0 写了个「开发者工具箱」MCP Server------一个 Python 文件,三个工具,直接接到 Claude Code 里用,效率提升肉眼可见。
今天把过程和踩的坑都写出来,保证你照着做 30 分钟内能跑起来。
先说结论
| 工具 | 功能 | 代码量 | 实用指数 |
|---|---|---|---|
search_pypi |
查 Python 包最新版本和信息 | 15 行 | ⭐⭐⭐⭐⭐ |
github_trending |
抓 GitHub 当日热门仓库 | 20 行 | ⭐⭐⭐⭐ |
port_check |
检查本地端口占用情况 | 10 行 | ⭐⭐⭐⭐ |
三个工具加起来不到 80 行 Python,FastMCP 帮你处理了所有 MCP 协议的脏活。
MCP 是啥?30 秒版本
MCP(Model Context Protocol)是 Anthropic 搞的一个开放协议,简单说就是给 AI 装外挂的标准接口。
以前你想让 Claude 查个天气,得自己写一堆胶水代码。现在有了 MCP,你只要:
- 写一个 Python 函数
- 加个
@mcp.tool装饰器 - 完事了
Claude Code、Cursor、Windsurf 这些 AI 编辑器都支持 MCP,装上就能用。
环境准备
bash
# 装 FastMCP 3.0(需要 Python 3.10+)
pip install fastmcp httpx
# 验证安装
python -c "import fastmcp; print(fastmcp.__version__)"
我用的是 FastMCP 3.1.0,3.0 以上版本都行。如果你还在用 2.x,强烈建议升级,3.0 的 API 简洁太多了。
开搓:dev_toolbox_server.py
工具一:PyPI 包信息查询
写代码时经常要查某个包的最新版本、依赖关系,以前得开浏览器搜。现在直接让 Claude 帮你查:
python
from fastmcp import FastMCP
import httpx
mcp = FastMCP("DevToolbox 🔧")
@mcp.tool
async def search_pypi(package_name: str) -> dict:
"""查询 Python 包的最新版本、简介和依赖信息。
Args:
package_name: PyPI 上的包名,如 'requests'、'fastapi'
"""
async with httpx.AsyncClient() as client:
resp = await client.get(
f"https://pypi.org/pypi/{package_name}/json",
timeout=10
)
if resp.status_code == 404:
return {"error": f"包 '{package_name}' 不存在"}
data = resp.json()
info = data["info"]
return {
"name": info["name"],
"version": info["version"],
"summary": info["summary"],
"author": info["author"],
"license": info["license"],
"python_requires": info["requires_python"],
"homepage": info["home_page"] or info["project_urls"].get("Homepage", ""),
"dependencies": info.get("requires_dist", [])[:10], # 只取前 10 个
}
注意几个点:
- 用
async函数。FastMCP 原生支持异步,网络请求用异步性能好很多 - docstring 很重要。FastMCP 会把它作为工具描述传给 AI,写清楚了 AI 才知道什么时候该调用这个工具
- 类型注解不能省 。
package_name: str这个注解是 FastMCP 生成 JSON Schema 的依据
工具二:GitHub Trending
每天上班第一件事看 GitHub 热榜?让 AI 帮你看:
python
@mcp.tool
async def github_trending(language: str = "", since: str = "daily") -> list[dict]:
"""获取 GitHub 今日热门仓库列表。
Args:
language: 编程语言筛选,如 'python'、'javascript',留空表示所有语言
since: 时间范围,可选 'daily'、'weekly'、'monthly'
"""
url = "https://api.gitterapp.com/repositories"
params = {"since": since}
if language:
params["language"] = language.lower()
async with httpx.AsyncClient() as client:
resp = await client.get(url, params=params, timeout=15)
repos = resp.json()
return [
{
"name": f"{repo['author']}/{repo['name']}",
"description": repo.get("description", ""),
"stars": repo.get("stars", 0),
"today_stars": repo.get("currentPeriodStars", 0),
"language": repo.get("language", "Unknown"),
"url": repo.get("url", ""),
}
for repo in repos[:15] # 取前 15 个
]
这里用的是 gitterapp.com 的非官方 API,不需要 token,够用了。如果你需要更稳定的数据源,可以用 GitHub 官方 API(需要 Personal Access Token)。
工具三:端口占用检查
开发时端口冲突是家常便饭,lsof -i :8080 记不住?交给 AI:
python
import subprocess
@mcp.tool
def port_check(port: int) -> dict:
"""检查指定端口是否被占用,如果被占用则显示占用进程信息。
Args:
port: 要检查的端口号,如 8080、3000、5432
"""
try:
result = subprocess.run(
["lsof", "-i", f":{port}", "-P", "-n"],
capture_output=True, text=True, timeout=5
)
if result.returncode != 0 or not result.stdout.strip():
return {"port": port, "status": "available", "message": f"端口 {port} 空闲,可以使用"}
lines = result.stdout.strip().split("\n")
processes = []
for line in lines[1:]: # 跳过 header
parts = line.split()
if len(parts) >= 9:
processes.append({
"command": parts[0],
"pid": parts[1],
"user": parts[2],
"name": parts[8] if len(parts) > 8 else "",
})
return {
"port": port,
"status": "in_use",
"process_count": len(processes),
"processes": processes,
}
except subprocess.TimeoutExpired:
return {"port": port, "status": "timeout", "message": "检查超时"}
注意这个工具是同步函数 (没有 async),FastMCP 同步异步都支持,混着写完全没问题。
启动入口
python
if __name__ == "__main__":
mcp.run()
就这一行。
完整代码
把上面的片段合在一起,完整的 dev_toolbox_server.py 大概 75 行。保存好后先测试一下:
bash
# 用 FastMCP 自带的检查工具验证
fastmcp inspect dev_toolbox_server.py
# 或者直接运行看看有没有语法错误
python dev_toolbox_server.py
接入 Claude Code
在你的项目根目录创建或编辑 .claude/settings.json:
json
{
"mcpServers": {
"dev-toolbox": {
"command": "python",
"args": ["/你的路径/dev_toolbox_server.py"],
"env": {}
}
}
}
重启 Claude Code,输入 /mcp 应该能看到你的三个工具了。
试着问 Claude:「帮我查一下 fastapi 最新版本是多少」,它会自动调用 search_pypi 工具返回结果。
踩坑记录
坑 1:httpx 超时默认太短
httpx 默认超时 5 秒,GitHub API 有时候响应慢会挂掉。我一开始没设 timeout 参数,动不动就 ReadTimeout。解决 :所有网络请求都显式设 timeout=10 或更长。
坑 2:docstring 写得烂,AI 就不调用
一开始我的 port_check 工具 docstring 只写了「检查端口」三个字。结果 Claude 遇到端口相关的问题压根不调用这个工具,因为它不确定这个工具具体能干啥。
改成详细描述之后,调用率直接翻倍。经验:把 docstring 当作给 AI 看的说明书来写,参数说明越具体越好。
坑 3:subprocess 在 Windows 上翻车
lsof 是 macOS/Linux 的命令,Windows 上跑不了。如果你要跨平台,得用 netstat 做兼容:
python
import platform
if platform.system() == "Windows":
result = subprocess.run(
["netstat", "-ano", "-p", "TCP"],
capture_output=True, text=True
)
# 自己解析 netstat 输出...
我没做这个兼容,因为我只在 Mac 上开发。但如果你要分享给团队用,记得处理。
坑 4:FastMCP 2.x 升 3.0 的 breaking change
如果你之前用的 FastMCP 2.x,升级 3.0 要注意:
@mcp.resource的 URI 格式变了,现在必须是"protocol://path"形式Context从fastmcp.server.context导入,不是以前的fastmcp.contextmcp.run()默认用 stdio 传输,要用 SSE 得手动指定mcp.run(transport="sse")
进阶:加个 Resource
除了 Tool,MCP 还有 Resource 的概念------提供只读数据给 AI 参考。比如加一个展示所有工具说明的 Resource:
python
@mcp.resource("docs://toolbox/readme")
def toolbox_readme() -> str:
"""开发者工具箱使用说明"""
return """
# DevToolbox MCP Server
可用工具:
- search_pypi: 查询 Python 包信息
- github_trending: 获取 GitHub 热门仓库
- port_check: 检查端口占用
使用示例:
- "查一下 requests 包最新版本"
- "今天 Python 圈有什么热门项目"
- "8080 端口被谁占了"
"""
小结
MCP 和 Skill 被热榜吹得挺玄乎,但实际上手发现就是「给函数套个装饰器」的事。FastMCP 3.0 把协议层的复杂度全吃了,你只需要关心业务逻辑。
今天这三个工具是抛砖引玉,你可以按同样的模式扩展更多:
- 接数据库查询
- 接 Jira/飞书 API 查任务状态
- 接 Docker API 管理容器
一个 Python 文件能塞不少东西,我目前自己用的 MCP Server 已经有十几个工具了,日常开发基本不用离开编辑器。
热榜说「写 Skill 越多行业死越快」,我倒觉得恰恰相反------会写工具的开发者,才是这轮 AI 浪潮里最难被替代的。毕竟 AI 再强,也得有人告诉它怎么跟外部世界交互。
本文代码已在 Python 3.12 + FastMCP 3.1.0 环境下测试通过。有问题评论区见。