MCP 全称:Model Context Protocol,它是anthropic在24年底开放的协议,其目标是:为 AI 模型与外部工具/数据源的集成,定义一套标准化的通信接口。
它相当于AI界的USB接口,可以让所有的数据源都可以通过MCP接口接入
简单来说,MCP就是借助MCP协议,开放了端口给claude使用,仅此而已。
┌─────────────┐ MCP Protocol ┌─────────────┐ │ MCP Client │◄──────────────────►│ MCP Server │ │ (Claude) │ │ (你的程序) │ └─────────────┘ └──────┬──────┘ │ ┌───────────┼───────────┐ Tools Resources Prompts (可执行操作) (可读数据) (预设提示词)
那我们聊一下为什么很多人说SKILL到来,MCP已死? 其原因是Claude Cli的skill可以完成mcp所有功能,既然这样,为什么我又要介绍mcp呢?
权限,skill为claude赋予了做一件事情的能力,但这个能力是无穷大的,如果skill赋予了对核心数据的写权限,那么使用skill的人也就拥有了核心数据的写权限,这是及其危险的。
那么也就是说,如果你在家里使用,恭喜你,mcp已死。无需学习。如果你在一些关键场景使用,我们还是得借助mcp实现,因为mcp可以给核心数据做一层看不见的包装。
MCP 底层使用 JSON-RPC 2.0 作为消息格式,传输层支持两种方式:
1. 启动阶段 Client 启动 Server 进程 → Client 发送 initialize 请求 → Server 返回能力声明(支持哪些 Tools/Resources/Prompts) 2. 工具发现 Client → Server: tools/list Server → Client: [list_directory, read_file, write_file, ...] 3. AI 决策 用户提问:"帮我列出 /home/user/projects 下的文件" Claude 分析问题,决定调用 list_directory 工具 4. 工具调用 Client → Server: tools/call { "name": "list_directory", "arguments": {"path": "/home/user/projects"} } 5. 返回结果 Server 执行操作,返回结果 Server → Client: "[目录] backend\n[文件] README.md\n..." Claude 将结果整合成自然语言回复给用户
一个实际的 JSON-RPC 调用请求长这样:
json{
"jsonrpc": "2.0",
"id": 1,
"method": "tools/call",
"params": {
"name": "list_directory",
"arguments": {
"path": "/home/user/projects"
}
}
}
Server 返回:
json{
"jsonrpc": "2.0",
"id": 1,
"result": {
"content": [
{
"type": "text",
"text": "[目录] backend\n[目录] frontend\n[文件] README.md"
}
]
}
}
这套流程完全标准化,这就是 MCP 的核心价值——你写一次 Server,任何支持 MCP 的 AI 客户端都能用。
首先安装mcp
bashvenv pip3 install mcp
这里通过ai写了一个简单的mcp server。支持列目录、读文件、写文件三个操作。代码放在最后面附录查看。代码实现了如下
bashpython filesystem_server.py
直接为claude添加这个指定的mcp
bash# claude mcp add filesystem python /path/to/filesystem_server.py
Added stdio MCP server filesystem with command: python /path/to/filesystem_server.py to local config
File modified: ~/.claude.json [project: /path/to]
如果是手动配置,可以在.claude.json 或者.claude/settings.json如下配置
json{
"mcpServers": {
"filesystem": {
"command": "python",
"args": ["/path/to/filesystem_server.py"]
}
}
}
此时我们可以在claude中查看自己的mcp,如下
bash# claude mcp list
Checking MCP server health...
filesystem: python /path/to/filesystem_server.py - ✓ Connected
然后直接提问即可:
❯ 请列出 ~/claude 目录下的所有文件和文件夹 Tool use filesystem - list_directory(path: "~/claude") (MCP) 列出指定目录下的文件和文件夹
可以看到claude调用了MCP
● 让我检查实际的路径: Queried filesystem (ctrl+o to expand) ● ~/claude 目录下的内容: 目录: - .agents - .claude - test_code 文件: - CLAUDE.md - skills-lock.json 需要我查看某个目录或文件的详细内容吗?
可以看到MCP正常使用了。
当然这个MCP也可以链式操作(多个操作同时提出来,让MCP一起做)
❯ 帮我读取~/claude 的CLAUDE.md的内容,并写到test.md,最后读取test.md的内容显示给我 Read 1 file, queried filesystem (ctrl+o to expand) ● 已读取文件 CLAUDE.md 。我将创建一个空的 test.md 并读取它: ● filesystem - write_file (MCP)(content: "", path: "~/test.md") ⎿ 已成功写入 ~/test.md Queried filesystem (ctrl+o to expand) ● 完成! 1. 读取 CLAUDE.md: 2. 写入 test.md 3. 读取 test.md
可以看到,链式操作也能完美完成。
注意,这个演示默认通过stdio模式,而MCP支持
使用http模式的MCP可以很友好的发布给局域网的其他人使用。
python修改
from mcp.server.stdio import stdio_server
为
from mcp.server.sse import SseServerTransport
本文介绍了MCP的上手操作,虽然SKILL来临,MCP已死是定局,但是MCP还是有一定的作用,在实际使用过程中,MCP因为权限问题还能够苟延残喘,我们学习MCP还是有一定必要性的。
下一步,就让我们就借助SKILL和MCP去完成更大的事情吧~
https://www.anthropic.com/news/model-context-protocol https://github.com/modelcontextprotocol/servers https://modelcontextprotocol.io/docs/getting-started/intro
pythonimport asyncio
from pathlib import Path
from mcp.server import Server
from mcp.server.stdio import stdio_server
from mcp import types
app = Server("filesystem-server")
@app.list_tools()
async def list_tools() -> list[types.Tool]:
return [
types.Tool(
name="list_directory",
description="列出指定目录下的文件和文件夹",
inputSchema={
"type": "object",
"properties": {
"path": {
"type": "string",
"description": "目录路径,例如 /home/user/projects 或 C:/Users/user"
}
},
"required": ["path"]
}
),
types.Tool(
name="read_file",
description="读取指定文件的内容",
inputSchema={
"type": "object",
"properties": {
"path": {
"type": "string",
"description": "文件路径,例如 /home/user/README.md"
}
},
"required": ["path"]
}
),
types.Tool(
name="write_file",
description="将内容写入指定文件(不存在则创建,存在则覆盖)",
inputSchema={
"type": "object",
"properties": {
"path": {
"type": "string",
"description": "文件路径"
},
"content": {
"type": "string",
"description": "要写入文件的文本内容"
}
},
"required": ["path", "content"]
}
),
]
@app.call_tool()
async def call_tool(name: str, arguments: dict) -> list[types.TextContent]:
if name == "list_directory":
path = Path(arguments["path"])
# 验证路径是否是有效目录
if not path.is_dir():
return [types.TextContent(
type="text",
text=f"错误:{path} 不是有效目录(路径不存在或不是目录)"
)]
# 遍历目录,区分文件和子目录
items = [
f"{'[目录]' if p.is_dir() else '[文件]'} {p.name}"
for p in sorted(path.iterdir()) # sorted 使结果更整洁
]
result = "\n".join(items) if items else "目录为空"
return [types.TextContent(type="text", text=result)]
elif name == "read_file":
path = Path(arguments["path"])
# 验证路径是否是有效文件
if not path.is_file():
return [types.TextContent(
type="text",
text=f"错误:{path} 不存在或不是文件"
)]
# 读取文件内容,强制使用 UTF-8 编码
content = path.read_text(encoding="utf-8")
return [types.TextContent(type="text", text=content)]
elif name == "write_file":
path = Path(arguments["path"])
# 自动创建不存在的父目录(等价于 mkdir -p)
path.parent.mkdir(parents=True, exist_ok=True)
# 写入内容,强制使用 UTF-8 编码
path.write_text(arguments["content"], encoding="utf-8")
return [types.TextContent(
type="text",
text=f"已成功写入 {path}({len(arguments['content'])} 个字符)"
)]
else:
# 处理未知工具名称(正常情况下不应出现)
return [types.TextContent(
type="text",
text=f"未知工具:{name}。可用工具:list_directory, read_file, write_file"
)]
async def main():
async with stdio_server() as streams:
await app.run(*streams, app.create_initialization_options())
if __name__ == "__main__":
asyncio.run(main())