一次编写,被所有客户端共享的工具
MCP 解决的问题
上两篇我们用 Python 实现了工具注册、函数调用、Agent 循环。但这些工具都是"嵌在你自己代码里"的——Claude Desktop 用不到、Cursor 用不到、同事写的 Agent 也用不到。每个客户端想集成这些工具都要重新对接一次,这是 2023、2024 两年 AI 工具生态最大的痛点之一。
MCP(Model Context Protocol) 是 Anthropic 2024 年底开源的一个协议,用一句话总结它在做什么:把"工具"和"使用工具的客户端"之间的接口标准化。你用任何语言实现一个 MCP Server,把它注册到兼容 MCP 的客户端(Claude Desktop、Cursor、Cline、Windsurf 等),客户端的 Agent 就能自动发现、调用你这个 server 暴露的所有工具。类比成熟领域:MCP 之于 AI Agent,类似 LSP 之于 IDE、OpenAPI 之于 HTTP 服务。
本篇不做宏大叙事,直接动手写一个 MCP Server 接进 Claude Desktop,让你切身体会它解决的问题。
协议全貌(够用的简化版)
MCP 基于 JSON-RPC 2.0,通信方式支持两种:
- stdio——客户端启动 server 子进程,通过标准输入输出通信。开发最简单,本地工具首选
- HTTP + SSE——server 是独立服务,客户端通过 HTTP 连接。适合远程/多用户场景
Server 能向客户端暴露三类能力:
- Tools——可调用的函数,和 Function Calling 概念一致
- Resources——可读取的资源,比如文件、数据库表、API 端点
- Prompts——可复用的 Prompt 模板
本篇只用 Tools 这一种,也是实际项目中用得最多的。
用 Python SDK 写第一个 MCP Server
官方 Python SDK 叫 mcp,装上就能用:
pip install mcp
下面实现一个能给 Agent 查询项目 git 信息的 MCP Server:
# mcp_git_server.py
import subprocess
from pathlib import Path
from mcp.server.fastmcp import FastMCP
mcp = FastMCP("git-inspector")
def _run_git(args: list[str], cwd: Path) -> str:
result = subprocess.run(
["git"] + args,
cwd=str(cwd),
capture_output=True,
text=True,
timeout=10,
)
if result.returncode != 0:
return f"错误:{result.stderr.strip()}"
return result.stdout.strip()
@mcp.tool()
def git_status(repo_path: str) -> str:
"""查看指定 Git 仓库的工作区状态。"""
return _run_git(["status", "--short"], Path(repo_path))
@mcp.tool()
def git_log(repo_path: str, limit: int = 10) -> str:
"""查看最近的 N 条提交记录。"""
return _run_git(["log", f"-{limit}", "--oneline"], Path(repo_path))
@mcp.tool()
def git_diff(repo_path: str) -> str:
"""查看当前未暂存的改动。"""
return _run_git(["diff"], Path(repo_path))
@mcp.tool()
def current_branch(repo_path: str) -> str:
"""查看当前分支名。"""
return _run_git(["branch", "--show-current"], Path(repo_path))
if __name__ == "__main__":
mcp.run()
FastMCP 把 MCP 协议细节完全藏了起来。你只需要用 @mcp.tool() 装饰一个函数,SDK 会自动:
- 从函数签名推断参数 schema(利用 Python 的类型注解)
- 从 docstring 生成工具描述
- 处理 JSON-RPC 协议层的请求响应
运行这个 server:
python mcp_git_server.py
默认用 stdio 模式,进程启动后通过 stdin/stdout 等待客户端连接。
接入 Claude Desktop
把 server 注册到 Claude Desktop。找到它的配置文件:
- macOS:
~/Library/Application Support/Claude/claude_desktop_config.json - Windows:
%APPDATA%\Claude\claude_desktop_config.json
编辑成这样(路径换成你自己的):
{
"mcpServers": {
"git-inspector": {
"command": "python",
"args": ["/absolute/path/to/mcp_git_server.py"]
}
}
}
重启 Claude Desktop。在对话框左下角应该能看到工具图标,点开能看到 git_status、git_log 等几个工具已经被识别。这时候你问 Claude "/path/to/my/repo 这个仓库最近有哪些提交?",它会自动调用 git_log 工具。
整个过程你没写一行客户端代码。 Claude Desktop 不知道也不需要知道你的工具是 Python 写的还是 TypeScript 写的,不需要重新部署,也不用往它的二进制里塞代码。这就是协议标准化的直接收益。
Resources:给客户端读的内容
Tools 是"主动调用";Resources 是"被动读取"。比如你希望客户端能把某个配置文件、某段代码片段、某个 API 的响应作为上下文自动加载,用 Resources 表达更合适:
@mcp.resource("config://database")
def database_config() -> str:
"""数据库连接配置。"""
return Path(".env").read_text()
@mcp.resource("file://{path}")
def read_file(path: str) -> str:
"""读取指定路径的文件内容。"""
return Path(path).read_text()
支持模板化的 URI({path} 是参数)。客户端 UI 会把这些 Resources 展示给用户选择,或者自动检索。
实务上多数工具场景 Tools 就够用。Resources 的主要价值是给"用户主动选择上下文"的场景一个标准化入口。
用 Python 写客户端
反过来场景:你想在自己的 Python Agent 里使用 MCP 生态的现成 server(比如文件系统、数据库、各类 SaaS 的 MCP server)。
pip install mcp# mcp_client_demo.py
import asyncio
from mcp import ClientSession, StdioServerParameters
from mcp.client.stdio import stdio_client
async def main():
# 启动一个子进程 server 并连接
server_params = StdioServerParameters(
command="python",
args=["mcp_git_server.py"],
)
async with stdio_client(server_params) as (read, write):
async with ClientSession(read, write) as session:
await session.initialize()
# 列出所有可用工具
tools = await session.list_tools()
for t in tools.tools:
print(f"- {t.name}: {t.description}")
# 调用一个工具
result = await session.call_tool(
"git_status",
{"repo_path": "/your/repo/path"},
)
print(result.content[0].text)
asyncio.run(main())
把这段和上一篇的 Agent 结合,就能让你的 Python Agent 直接消费任何 MCP server 暴露的工具。mcp 官方也提供了把 MCP 工具桥接成 OpenAI Function Calling 工具的适配层,用起来几乎无感。
生态现状和选型建议
2025 年下半年开始 MCP 生态爆发,几百个 server 覆盖了大部分常见场景:
- 官方/社区核心 server——Filesystem、GitHub、Git、Postgres、Fetch、Puppeteer 等基础能力
- SaaS 厂商自建——Cloudflare、Linear、Notion、Sentry 等都上线了官方 MCP server
- 聚合器——Smithery、mcp.so 做了 server 目录,能一键安装
实务选型建议:
- 你要给自己的 Agent 加能力——优先找现成的 MCP server 接入,避免重复造轮子
- 你要对外暴露能力(给客户/同事用)——写一个 MCP Server 比写 REST API 更合适,因为你立刻能被所有 MCP 客户端复用
- 你在做纯后端系统——不需要 MCP,直接用 Function Calling 就好。MCP 的价值在于跨客户端共享
什么时候不值得用 MCP:纯服务端、单一消费者的场景。这时候 MCP 的协议开销没有带来任何跨客户端收益,直接 Function Calling 更简单。
调试 MCP Server 的实用技巧
MCP Server 运行在 stdio 模式时,stderr 会被客户端吞掉,print() 调试会弄乱协议。正确做法:
用 logging 写文件——调试期间把所有日志写本地文件,别污染 stdout:
import logging
logging.basicConfig(
filename="/tmp/mcp_git.log",
level=logging.DEBUG,
format="%(asctime)s %(message)s",
)
用 MCP Inspector——官方出的 GUI 调试工具,可以手动调用任意 tool 查看响应,不用先接 Claude Desktop。
npx @modelcontextprotocol/inspector python mcp_git_server.py
打开模拟模式——写单元测试时用 mcp.server.fastmcp.testing 里的工具跳过真实 stdio 直接 in-process 调用。
本篇要点
- MCP 把"AI Agent 的工具接口"标准化,一次编写被所有兼容客户端复用
- Python SDK
mcp的FastMCP让写 server 只剩一个装饰器的工作量 - stdio 模式开发最快,只要客户端配置文件里把 command 指到你的脚本就自动接入
- Resources 补充了"可读上下文"的场景,但 Tools 仍是最常用的能力
- 生态选型:自用找现成 server 接入,对外输出能力写自己的 server
- 调试避开 stdout,用文件 logging 或 MCP Inspector
下一篇
前面所有内容都依赖调用云端 API,付费也可能有隐私顾虑。第 10 篇转到 本地模型——用 Ollama 在自己的电脑上跑开源 LLM,用 OpenAI SDK 无缝切换云端和本地。对学习、敏感数据处理、边缘部署都是关键能力。
参考资料
- MCP 官方文档
- MCP Python SDK
- MCP Inspector 调试工具
- Smithery MCP Server 目录
- Awesome MCP Servers — 社区整理的 server 清单
版权声明: 如无特别声明,本文版权归 sshipanoo 所有,转载请注明本文链接。
(采用 CC BY-NC-SA 4.0 许可协议进行授权)
本文标题:MCP 协议:Agent 的通用接口
本文链接:https://www.sshipanoo.com/blog/ai/ai-for-python/09-MCP协议/
本文最后一次更新为 天前,文章中的某些内容可能已过时!