08-MCP 协议:Tool Calling 标准化背后的故事
2026/6/6 21:55:36 网站建设 项目流程

MCP 协议:Tool Calling 标准化背后的故事

你写了 10 个工具——天气查询、股票数据、文件操作、数据库访问……想给 Claude 用,得按 Anthropic 格式定义一遍。想换成 OpenAI?再按 OpenAI 格式定义一遍。想换成 Gemini?再来一遍。你开始怀疑:工具调用为什么不能像 USB-C 一样,一个接口通吃?MCP 就是答案。


Tool Calling 的"巴别塔"问题

你一定已经会 Tool Calling 了。用 LangChain 的@tool装饰器,让 LLM 自动调用外部函数:

fromlangchain_core.toolsimporttool@tooldefget_weather(city:str)->str:"""查询城市天气"""returnweather_api.query(city)llm_with_tools=llm.bind_tools([get_weather])# LLM 自动判断何时调用、传什么参数

这很好。但有一个根本问题:工具定义和 LLM 平台是绑定的。

不同平台有不同的 Tool Calling 格式:

OpenAI: tools=[{"type": "function", "function": {"name": "...", "parameters": {...}}}] Anthropic: tools=[{"name": "...", "description": "...", "input_schema": {...}}] Google Gemini: tools=[{"functionDeclarations": [{"name": "...", "parameters": {...}}]}]

如果你有 10 个工具,想给 3 个平台用——维护成本 = 10 × 3 = 30 套工具定义。

而且工具还嵌在你的应用代码里。换了项目就得重新写一遍。没有隔离——工具崩溃可能影响整个应用。没有发现机制——LLM 不知道你新加了什么工具,除非你手动更新配置。

这就是 MCP 要解决的问题。


MCP 是什么:LLM 的"USB-C 接口"

MCP(Model Context Protocol)= LLM 和外部工具的"USB-C 标准"。

USB-C 出现之前,每种设备有自己的接口。USB-C 之后,一个接口通吃所有设备。MCP 对 LLM 工具调用做了一模一样的事——定义了一个统一协议,任何 LLM 平台都能通过这个协议发现和调用任何工具。

三角色架构

┌──────────────────────────────────────┐ │ MCP Host │ ← AI 应用(Claude Desktop、Cursor 等) │ ┌────────────────────────────────┐ │ │ │ MCP Client │ │ ← 协议客户端,管理连接和 Tool 列表 │ │ • 连接 Server │ │ │ │ • 发现 Tool/Resource/Prompt │ │ │ │ • 调用 Tool │ │ │ └────────────┬───────────────────┘ │ └───────────────┼───────────────────────┘ │ JSON-RPC over stdio/SSE ┌───────────────┼───────────────────────┐ │ ┌────────────┴───────────────────┐ │ │ │ MCP Server │ │ ← 工具提供方(独立进程) │ │ • 声明 Tool/Resource/Prompt │ │ │ │ • 执行 Tool 调用 │ │ │ │ • 返回结果 │ │ │ └────────────────────────────────┘ │ └───────────────────────────────────────┘
角色做什么举例
MCP Host运行 LLM 的应用Claude Desktop、Cursor、Claude Code
MCP ClientHost 内部的协议客户端管理连接、转发调用
MCP Server提供工具的服务进程天气服务、文件系统、数据库

通信方式极其简单——JSON-RPC 2.0 over stdio(本地)或 SSE(远程)。Host 启动 Server 作为子进程,通过 stdin/stdout 收发 JSON。

没有 MCP vs 有了 MCP

问题MCP 之前MCP 之后
工具定义每个平台一种格式一次定义,所有平台通用
工具部署嵌入在应用代码里独立进程,即插即用
工具发现硬编码Client 自动从 Server 发现
工具复用每个项目重写一个 Server 多个项目共用
安全隔离同进程,无隔离独立进程,权限隔离

一句话:传统 Tool Calling 是"把工具焊死在 LLM 上",MCP 是"把工具变成可插拔的服务"。


MCP Server 提供三种能力

一个 MCP Server 不只是提供工具,它提供三种元能力:

┌─────────────────────────────────────────┐ │ MCP Server │ │ │ │ Tools Resources Prompts │ │ (执行) (读取) (模板) │ │ ─────── ──────── ─────── │ │ 查天气 读配置文件 周报模板 │ │ 写文件 数据库Schema 代码审查模板 │ │ 调API 日志内容 提交信息模板 │ │ │ │ LLM 主动调用 LLM 被动读取 LLM 请求模板 │ └─────────────────────────────────────────┘

Tool(工具)——LLM 可以调用的"函数"。这是 MCP 最核心的概念。

Tool(name="get_weather",description="获取指定城市的天气信息。当用户询问天气时使用。",inputSchema={"type":"object","properties":{"city":{"type":"string","description":"城市名称"},},"required":["city"],},)

三个要素:name(LLM 用来标识)、description(LLM 用来判断"什么时候用")、inputSchema(LLM 用来生成正确的参数)。description 是最关键的——LLM 完全靠它来决定要不要调你的工具。

Resource(资源)——LLM 可以"读取"的数据源。和 Tool 的区别:

ToolResource
语义事情(执行操作)数据(获取信息)
副作用有(写文件、发邮件)无(只读)
标识函数名URI(如file:///project/readme.md
简单判断功能是"做 X"功能是"获取 X"

Prompt(提示模板)——预定义的提示词模板,让 LLM 快速获取特定场景的提示词。比如 “write_weekly_report” 模板带参数 project 和 highlights,LLM 可以直接获取结构化的周报 Prompt。


实战:构建一个 MCP Server

我们用 Python 构建一个 MCP Server,提供两组工具:文件系统操作(list_directory、read_file、search_files、write_file)和SQLite 数据库查询(db_list_tables、db_describe_table、db_query、db_export_table)。

最小 Server 结构

frommcp.serverimportServerfrommcp.typesimportTool,TextContentfrommcp.server.stdioimportstdio_server server=Server("my-tools-server")# 1. 声明 Tool 菜单(LLM 先调用这个)@server.list_tools()asyncdeflist_tools()->list[Tool]:return[Tool(name="list_directory",description="列出目录内容",inputSchema={...}),Tool(name="read_file",description="读取文件内容",inputSchema={...}),Tool(name="db_query",description="执行 SELECT 查询",inputSchema={...}),# ...]# 2. 实现 Tool 调用(LLM 决定调哪个)@server.call_tool()asyncdefcall_tool(name:str,arguments:dict)->list[TextContent]:ifname=="list_directory":return[TextContent(type="text",text=format_dir(arguments["path"]))]elifname=="read_file":return[TextContent(type="text",text=read_file(arguments["path"]))]elifname=="db_query":return[TextContent(type="text",text=query_db(arguments["sql"]))]# ...# 3. 启动(监听 stdin/stdout)asyncwithstdio_server()as(read_stream,write_stream):awaitserver.run(read_stream,write_stream,...)

就这么简单。三个核心步骤:声明菜单 → 实现调用 → 启动服务。

安全是 MCP 设计的核心

因为 MCP Server 是独立进程,你需要明确控制 LLM 的权限边界。我们的 Server 做了三层安全:

1. 路径穿越防护:

defsafe_path(user_path:str,allowed_root:str)->Path:"""防止 LLM 读取允许目录之外的文件"""resolved=(Path(allowed_root)/user_path).resolve()allowed=Path(allowed_root).resolve()ifnotstr(resolved).startswith(str(allowed)):raisePermissionError(f"拒绝访问:'{user_path}' 超出允许范围")returnresolved# safe_path("../../../etc/passwd") → PermissionError!# safe_path("demo/hello.py") → /home/user/mcp-workspace/demo/hello.py ✅

2. SQLite 只读连接:

# 以只读模式连接,LLM 即使想删表也删不了conn=sqlite3.connect(f"file:{DB_PATH}?mode=ro",uri=True)

3. SQL 注入防护:

defhandle_db_query(args):sql=args["sql"].strip()ifnotsql.upper().startswith("SELECT"):return"只允许 SELECT 查询"forkeywordin["DROP","DELETE","INSERT","UPDATE","ALTER"]:ifkeywordinsql.upper():returnf"查询包含禁止的关键词 '{keyword}'"

这就是 MCP 的权限模型:最小权限原则。只给 LLM 完成任务所需的最小权限。


连接 Claude Code

配置极其简单。在项目根目录创建.mcp.json

{"mcpServers":{"local-tools":{"command":"python3","args":["mcp_server.py"],"env":{"WORKSPACE_DIR":"/path/to/your/workspace","DB_PATH":"/path/to/your/database.db"}}}}

然后直接在 Claude Code 对话中用:

"帮我看看 workspace 下有什么文件" "读取 config.json 的内容" "查询 sales.db 的 orders 表中 2026 年的订单总数"

Claude Code 会自动:发现你的 Server → 获取工具菜单 → 根据用户意图选择工具 → 调用工具 → 把结果融入回答。你什么都不用做。


MCP vs Function Calling vs LangChain Tool

这三个概念容易混淆,一张表说清楚:

OpenAI Function CallingLangChain @toolMCP
范围单次对话内的工具单个应用内的工具跨应用、跨平台的标准
定义方式JSON SchemaPython 装饰器Python 装饰器 + JSON Schema
部署嵌入代码嵌入代码独立进程
传输HTTP API内存调用stdio / SSE
平台绑定仅 OpenAILangChain 生态所有支持 MCP 的平台
工具复用每个项目重新定义可复用但不跨平台一次定义到处使用
安全隔离进程级隔离

它们不是互斥的,而是互补的:

MCP Server → 负责"提供工具"(独立进程,标准化发布) LangChain → 负责"使用工具"(在 Node 里调用 MCP 工具) OpenAI FC → 负责"工具调用格式"(LLM 层面的实现细节) 三者配合:用 @tool 开发 → 用 MCP 发布 → 任何 LLM 都能用

什么时候用 MCP

单个 OpenAI 项目,1-2 个简单工具 → OpenAI Function Calling 够用 LangChain 项目,需要 LLM 组件配合 → LangChain @tool 多平台、多项目、需要复用的工具 → MCP Server 需要进程级安全隔离 → MCP Server 想用社区现成的工具 → MCP(数百个现成的 Server)

MCP 的生态现状

MCP 由 Anthropic 于 2024 年 11 月发布,作为一个开放协议。短短半年多,生态已经相当丰富:

  • Claude Desktop原生支持 MCP
  • Claude Code(你正在用的)内置 MCP Client
  • Cursor / VS Code Copilot支持 MCP
  • 社区已有数百个 MCP Server:文件系统、GitHub、Slack、Postgres、Puppeteer……

这意味着什么?你不需要自己写所有的工具。有人已经写好了 GitHub MCP Server,你直接在.mcp.json里配置一下,Claude Code 就能自动获取 GitHub Issues、创建 PR、查看代码。这是一个"工具市场"的雏形。


总结

MCP 做的事情极其简单,但影响深远:

  1. 标准化—— 一次定义,所有 LLM 平台通用。不再重复造轮子
  2. 解耦—— 工具和 LLM 分离成独立进程,各自独立开发部署
  3. 复用—— 一个 Server 多个项目共用,生态里数百个现成的 Server
  4. 安全—— 进程级隔离,最小权限原则,路径校验、只读连接
OpenAI 发明了 Function Calling → "LLM 能调工具了" LangChain 提供了 @tool 装饰器 → "写工具变简单了" Anthropic 发布了 MCP → "工具可以跨平台复用了" 三者配合:用 @tool 开发 → 用 MCP 发布 → 任何 LLM 平台都能用

去 github.com/barryness/cc-ai-learning 的008-mcp-learning/python mcp_server.py --demo,自测模式会模拟 LLM 调用所有工具,让你直观感受 MCP Server 是怎么工作的。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询