编辑
2026-03-29
记录知识
0
请注意,本文编写于 42 天前,最后修改于 41 天前,其中某些信息可能已经过时。

目录

MCP的作用
MCP原理
完整交互流程
演示
运行
claude调用mcp
总结
参考
附录
MCP示例代码

MCP 全称:Model Context Protocol,它是anthropic在24年底开放的协议,其目标是:为 AI 模型与外部工具/数据源的集成,定义一套标准化的通信接口。

它相当于AI界的USB接口,可以让所有的数据源都可以通过MCP接口接入

MCP的作用

简单来说,MCP就是借助MCP协议,开放了端口给claude使用,仅此而已。

┌─────────────┐ MCP Protocol ┌─────────────┐ │ MCP Client │◄──────────────────►│ MCP Server │ │ (Claude) │ │ (你的程序) │ └─────────────┘ └──────┬──────┘ │ ┌───────────┼───────────┐ Tools Resources Prompts (可执行操作) (可读数据) (预设提示词)
  • MCP Client:发起请求的一方,通常是 AI 模型的宿主环境,例如 Claude Desktop、Claude Code CLI 等。
  • MCP Server:响应请求的一方,就是你要编写的程序,负责暴露工具和数据。

那我们聊一下为什么很多人说SKILL到来,MCP已死? 其原因是Claude Cli的skill可以完成mcp所有功能,既然这样,为什么我又要介绍mcp呢?

权限,skill为claude赋予了做一件事情的能力,但这个能力是无穷大的,如果skill赋予了对核心数据的写权限,那么使用skill的人也就拥有了核心数据的写权限,这是及其危险的。

那么也就是说,如果你在家里使用,恭喜你,mcp已死。无需学习。如果你在一些关键场景使用,我们还是得借助mcp实现,因为mcp可以给核心数据做一层看不见的包装。

MCP原理

MCP 底层使用 JSON-RPC 2.0 作为消息格式,传输层支持两种方式:

  • stdio:通过标准输入/输出通信,适合本地运行的 Server(本文使用这种方式)
  • HTTP + SSE:通过 HTTP 和 Server-Sent Events 通信,适合远程部署

完整交互流程

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

bash
venv pip3 install mcp

这里通过ai写了一个简单的mcp server。支持列目录、读文件、写文件三个操作。代码放在最后面附录查看。代码实现了如下

运行

bash
python filesystem_server.py

claude调用mcp

直接为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支持

  • stdio模式
  • http模式

使用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

附录

MCP示例代码

python
import 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())