Skip to content

MCP 与 SKILL:AI 应用的工具标准化革命

2024 年 11 月 25 日,Anthropic 发布了一篇博客,宣布开源 Model Context Protocol (MCP)。当时的标题是 "Introducing the Model Context Protocol"——没有大张旗鼓,没有发布会,但这条消息在 AI 工程师的圈子里引起了超过 GPT-4 发布时的那种兴奋。

为什么?因为它解决了一个困扰所有 AI 应用开发者的元问题:每个 AI 应用都在重新发明 "怎么让模型连接外部工具" 的轮子。

OpenAI 有 Function Calling、Anthropic 有 Tool Use、Google 有 Function Declarations、开源框架有 LangChain Tools... 这些接口本质上是同一件事——让模型 "调用" 外部函数——但实现方式各不兼容。你想让一个天气查询工具同时被 Claude Desktop、VS Code Copilot、Cursor 和 Continue 使用?对不起,你得为每个平台写一个适配器。

MCP 说:别写了,用同一个协议。

"USB-C for AI"——这个比喻真的很准确

Anthropic 自己在文档里用了 "USB-C for AI applications" 的比喻。USB-C 的价值不是它更快(虽然确实更快),而是 你只需要一根线连接一切。显示器、硬盘、手机、充电器——不需要 Micro-USB、Lightning、Thunderbolt、HDMI 各一根。

MCP 的目标完全一致:你只需要写一个 MCP Server 暴露你的工具/数据,任何 MCP 兼容的 AI 应用都能直接使用。

放到实际项目里,这意味着:你写了一个连接公司内部 CRM 的 MCP Server(暴露 "查客户资料"、"创建工单" 等 Tools),下面这些应用全部可以直接使用:

  • Claude Desktop(用户对话)
  • VS Code / Cursor(代码助手获取业务上下文)
  • 你自研的客服 Agent
  • 第三方的数据分析平台

一次编写,到处使用。 这就是开放协议的力量。

协议层:JSON-RPC 2.0 不是终点,是最低公分母

MCP 选择了 JSON-RPC 2.0 作为底层协议——这个选择透露了它的设计哲学:简单、可读、调试友好、所有语言有成熟库。

一个完整的 MCP 交互分为三个阶段:

1. 初始化握手(Lifecycle Management)

客户端和服务器互相介绍自己,协商协议版本和能力集。

客户端发送:

json
{
  "jsonrpc": "2.0",
  "id": 1,
  "method": "initialize",
  "params": {
    "protocolVersion": "2025-06-18",
    "capabilities": {
      "elicitation": {}           // 客户端支持用户交互
    },
    "clientInfo": {
      "name": "my-ai-agent",
      "version": "1.0.0"
    }
  }
}

服务器回应:

json
{
  "jsonrpc": "2.0",
  "id": 1,
  "result": {
    "protocolVersion": "2025-06-18",
    "capabilities": {
      "tools": {"listChanged": true},   // 服务器有工具,且工具列表可能动态变化
      "resources": {}                   // 服务器有可读资源
    },
    "serverInfo": {
      "name": "weather-service",
      "version": "2.1.0"
    }
  }
}

注意 listChanged: true 这个标志——它告诉客户端 "我的工具列表可能在运行过程中动态变化——新工具上线、旧工具下线——我会主动通知你的"。

2. 能力发现(Capability Discovery)

握手后,客户端可以探索服务器提供什么。MCP 的核心创新在于它把 "外部世界可以提供什么" 抽象成了三类原语(primitives):

Tools(工具)——可执行的操作。客户端发 tools/list,服务器返回:

json
{
  "tools": [
    {
      "name": "get_current_weather",
      "description": "获取指定城市的实时天气信息,包括温度、湿度、风速、天气状况",
      "inputSchema": {
        "type": "object",
        "properties": {
          "city": {"type": "string", "description": "城市名称,如 'Beijing', 'San Francisco'"},
          "units": {"type": "string", "enum": ["metric", "imperial"], "default": "metric"}
        },
        "required": ["city"]
      }
    },
    {
      "name": "get_forecast",
      "description": "获取指定城市未来 7 天天气预报",
      "inputSchema": {
        "type": "object",
        "properties": {
          "city": {"type": "string"},
          "days": {"type": "integer", "minimum": 1, "maximum": 7, "default": 3}
        },
        "required": ["city"]
      }
    }
  ]
}

Resources(资源)——可被模型读取的结构化数据。比如一个数据库 Server 可以暴露 schema://tables/users 作为 Resource,客户端通过 resources/read 方法按 URI 读取。Resources 有固定的 MIME type(text/json、image/png 等),可以有层级结构(如文件系统般的 schema://tables/usersschema://tables/orders)。

Prompts(提示模板)——预定义的 Prompt 结构,支持参数替换。服务器可以提供一个 code_review Prompt,参数是 language,内容是 "请对以下 {language} 代码进行全面审查..."——客户端调用时填入参数即可获得完整的 prompt。

三类原语的组合构成了完整的上下文感知:Tools 做事、Resources 提供信息、Prompts 控制行为。

3. 工具调用(Tool Invocation)

模型在推理过程中决定 "我现在需要天气信息",AI 应用(MCP Client)代它调用:

json
{
  "jsonrpc": "2.0",
  "id": 3,
  "method": "tools/call",
  "params": {
    "name": "get_current_weather",
    "arguments": {"city": "Beijing", "units": "metric"}
  }
}

服务器执行并返回:

json
{
  "jsonrpc": "2.0",
  "id": 3,
  "result": {
    "content": [
      {
        "type": "text",
        "text": "北京当前天气:晴,28°C,湿度 45%,南风 3 级。"
      }
    ]
  }
}

返回的 content 是一个数组——这意味着一次工具调用可以返回多种格式的内容:文本、图片、结构化 JSON、甚至嵌入的 Resource 引用。这个设计比 OpenAI Function Calling 的纯文本返回要灵活得多。

构建一个 MCP Server:比想象中简单

用 Python SDK 搭建一个天气查询的 MCP Server,核心逻辑不超过 80 行:

python
import asyncio
import httpx
from mcp.server import Server, NotificationOptions
from mcp.server.models import InitializationCapabilities
from mcp.server.stdio import stdio_server

server = Server("weather-service")
WEATHER_API = "https://api.openweathermap.org/data/2.5"

@server.list_tools()
async def handle_list_tools():
    return [
        {
            "name": "get_current_weather",
            "description": "获取指定城市的实时天气",
            "inputSchema": {
                "type": "object",
                "properties": {
                    "city": {"type": "string", "description": "城市名称"},
                    "units": {"type": "string", "enum": ["metric", "imperial"], "default": "metric"}
                },
                "required": ["city"]
            }
        }
    ]

@server.call_tool()
async def handle_call_tool(name: str, arguments: dict):
    if name == "get_current_weather":
        async with httpx.AsyncClient() as client:
            resp = await client.get(f"{WEATHER_API}/weather", params={
                "q": arguments["city"],
                "units": arguments.get("units", "metric"),
                "appid": "your-api-key"
            })
            data = resp.json()
            weather_text = (
                f"{arguments['city']}当前天气:{data['weather'][0]['description']},"
                f"温度 {data['main']['temp']}°C,"
                f"湿度 {data['main']['humidity']}%,"
                f"风速 {data['wind']['speed']} m/s"
            )
            return {"content": [{"type": "text", "text": weather_text}]}
    raise ValueError(f"Unknown tool: {name}")

async def main():
    async with stdio_server() as (read_stream, write_stream):
        await server.run(
            read_stream,
            write_stream,
            InitializationCapabilities(
                sampling={},        # 如果服务器需要反向调 LLM
            ),
        )

if __name__ == "__main__":
    asyncio.run(main())

把这个 Python 脚本配置到 Claude Desktop 的 claude_desktop_config.json

json
{
  "mcpServers": {
    "weather": {
      "command": "python",
      "args": ["path/to/weather_server.py"]
    }
  }
}

重启 Claude Desktop,你就可以直接对话说 "北京今天天气怎么样?"——Claude 会自动通过 MCP 发现 get_current_weather 工具、填充参数、执行调用、把结果整合进回答。

整个过程不需要在 Claude Desktop 的代码里写一行关于天气的逻辑。这是真正的解耦。

SKILL:比 Tool 更高一层的抽象

MCP 提供了 "工具接口标准",但只是接口——怎么用这些工具?在什么场景下用?用什么 workflow 组合多个工具?这些组合逻辑就是 SKILL。

一个 SKILL 是一个可被 Agent 按需加载的专业化能力包,包含:

  • 领域指令(Prompt):详细的 System Prompt,指导模型在特定场景下的行为
  • 关联的 MCP Tools:这个 Skill 需要用到哪些工具
  • 触发条件(Triggers):什么情况下应该激活这个 Skill(用户说 "分析 PDF" → 加载 pdf-analyzer skill)
  • 资源模板:预定义的工作流模板

SKILL 的设计哲学——按需加载(Lazy Loading)——是它跟传统 Function Calling 的关键区别。把所有工具一股脑塞进 System Prompt 会导致:

  • 上下文窗口被工具描述吃满(一个复杂工具的描述 500 token,20 个工具就是 1 万 token——而你的上下文窗口可能只有 128K)
  • 模型面对太多选择时容易出现 "选择困难"——在不需要的工具上浪费推理
  • 安全性风险:模型可以调用任何被暴露的工具

SKILL 的 "先匹配后加载" 模式天然解决了这些问题。类似操作系统的 "按需加载 DLL"——不需要的程序不占内存。

以 Cursor 的 Rules 系统为例——你可以为不同项目定义 .cursor/rules/ 文件,Cursor 根据当前项目上下文自动激活对应的规则集。这就是 Skills 的雏形。类似的,Claude Code 有 "sub-agents" 概念——每个 sub-agent 是一个专门化的小 Agent,有自己的 System Prompt 和工具集——这正是 SKILL 化的 Agent 设计。

MCP 的局限与未来

这是一个还在快速演进的标准(协议版本 2025-06-18 是目前的最新版本)。目前的几个主要痛点:

1. 安全性:MCP Server 可以访问文件系统、网络、数据库——如果被恶意注入(比如通过恶意的 MCP config),后果严重。Anthropic 的策略是在初始化时要求用户显式批准工具权限——但 UX 上还有很多改进空间。

2. 多步骤工具链:目前 MCP 对多步工具调用(先查 A 工具的结果,根据结果决定调 B 工具还是 C 工具)的支持依赖客户端的智能编排,协议层面没有原生支持。tasks 原语正在实验中,旨在为长运行、多步骤操作提供持久化执行封装。

3. 协议成熟度:MCP 不到两岁——API 还在变,SDK 的稳定性还没到生产级别。对于关键业务系统,现在采用 MCP 仍然需要 "拥抱变化" 的心态和版本迁移动态。

4. 生态碎片风险:虽然是开放协议,但如果主要玩家(OpenAI、Anthropic、Google)各自的实现有微妙的语义差异,就会产生新的碎片——这跟 USB-C 的历史一模一样(还记得 "不是所有的 USB-C 线都支持 Thunderbolt" 吗?)。

为什么你应该关注 MCP

MCP 代表了 AI 应用架构的一个趋势:从 "每个模型自建工具生态" 到 "共享的工具协议层"。

这对整个生态的影响是结构性的:

  • 工具开发者(你写了个 PDF 解析 MCP Server)不再需要关心用户用的是 Claude 还是 ChatGPT 还是本地 Llama——只要兼容 MCP 就能用
  • AI 应用开发者(你在做企业 Agent)不需要自己实现文件操作、数据库查询、搜索引擎——去 MCP 市场找现成的 Server 接上即可
  • 模型本身变得更 "薄"——它只负责推理和规划,工具调用和数据访问全部通过 MCP 层代理

这很像 1990 年代的 Web:HTTP 标准化之后,任何浏览器都能访问任何服务器——生态爆炸了。如果 MCP 真的成为行业标准(大公司们都加入了讨论,生态在快速壮大),我们可能会看到 AI Agent 的 "应用商店" 时代——所有的工具、数据源、工作流都能像 App 一样即插即用。

对 AI 工程师来说,现在投入时间学习 MCP 协议和 SDK——即使它还在演进——绝对不是浪费时间。这是未来 AI 应用架构的方向。

基于 VitePress 构建 | 部署于 Cloudflare Pages