Skills的概念最近又火了,这货和MCP有啥区别?怎么用?有哪些坑?本文带你一探究竟。
一、先搞清楚概念:它们到底是什么?
1.1 Skills 是什么
Skills(技能) 是 AI Agent 能够调用的能力单元的统称。
它是一个泛概念,不同框架叫法不同,本质相同:
「给 AI 一个工具,让它能干某件事」
1.2 MCP 是什么
MCP(Model Context Protocol) 是 Anthropic 在 2024 年底发布的开放标准协议。
官方定义:MCP 是一个开放协议,让 AI 应用能以标准化方式连接数据源和工具。
用人话说:MCP 是 Skills 的"USB 标准"。
在 MCP 出现之前,每个框架都有自己的插件格式,互不兼容,就像每个手机厂商用不同充电口一样让人崩溃。
MCP 想做的事就是:定一个标准,让所有 AI 工具都能插上去用。
Claude/GPT/Cursor] -->|MCP Protocol| B[MCP Client] B -->|标准化通信| C[MCP Server] C --> D[工具1: 文件系统] C --> E[工具2: 数据库] C --> F[工具3: API 服务] C --> G[工具N: 任意能力] style A fill:#4A90D9,color:#fff style B fill:#7B68EE,color:#fff style C fill:#50C878,color:#fff
二、Skills vs MCP:到底有什么区别?
这是本文核心,请打起精神。
2.1 一张表说清楚
| 维度 | Skills(泛概念) | MCP(具体协议) |
|---|---|---|
| 性质 | 概念/能力单元 | 通信标准/协议 |
| 范围 | 所有 AI 工具能力 | MCP 协议定义的工具 |
| 类比 | 「充电」这件事 | USB-C 充电标准 |
| 跨框架 | 各自为政,不通用 | 标准化,跨框架复用 |
| 实现方式 | 因框架而异 | Server-Client 架构 |
| 主要使用者 | 各 AI 框架开发者 | 工具开发者 + AI 平台 |
| 典型代表 | LangChain Tools、OpenAI Functions | Claude MCP、Cursor MCP |
2.2 关系图:Skills 包含 MCP
广义技能概念] --> B[OpenAI Function Calling] A --> C[LangChain Tools] A --> D[Semantic Kernel Plugins] A --> E[MCP - Model Context Protocol] E --> E1[MCP Tools
工具调用] E --> E2[MCP Resources
资源读取] E --> E3[MCP Prompts
提示词模板] style A fill:#FF6B6B,color:#fff style E fill:#4ECDC4,color:#fff style E1 fill:#45B7D1,color:#fff style E2 fill:#45B7D1,color:#fff style E3 fill:#45B7D1,color:#fff
结论:MCP 是实现 Skills 的一种标准化方式,Skills 是比 MCP 更广的概念。
三、MCP 架构深度解析
3.1 MCP 的三大核心概念
Tools(工具):AI 可以调用执行某个操作,比如「发送邮件」、「查询数据库」。
Resources(资源):AI 可以读取的数据,比如「某个文件内容」、「数据库记录」。
Prompts(提示词):预定义的提示词模板,供用户/AI 复用。
3.2 MCP 通信流程
(Claude) participant Client as MCP Client participant Server as MCP Server participant Tool as 实际工具
(文件/API/DB) User->>LLM: "帮我查一下今天的天气" LLM->>Client: 我需要调用 weather 工具 Client->>Server: tools/call {name: "get_weather", args: {city: "Dubai"}} Server->>Tool: 调用天气 API Tool-->>Server: {temp: 35, weather: "sunny"} Server-->>Client: 返回结果 Client-->>LLM: 工具执行结果 LLM-->>User: "今天迪拜天气晴,35度,建议防晒"
3.3 传输方式
MCP 支持两种传输方式,这里是第一个容易踩的坑:
简单但只能本地用] end subgraph SSE传输 A2[AI App] -->|HTTP + SSE| B2[MCP Server] B2 -->|SSE推送| A2 note2[网络通信
可以远程部署] end style note1 fill:#FFF3CD,stroke:#856404 style note2 fill:#D4EDDA,stroke:#155724
四、从入门到实践:动手写一个 MCP Server
4.1 环境准备
# Python 方案(推荐新手)
pip install mcp
# Node.js 方案
npm install @modelcontextprotocol/sdk
# 验证安装
python -c "import mcp; print('MCP ready!')"4.2 最简单的 MCP Server(Python)
# my_first_mcp_server.py
# 这是你人生中第一个 MCP Server
# 请对它好一点,它还是个孩子
from mcp.server import Server
from mcp.server.models import InitializationOptions
from mcp.types import Tool, TextContent
import mcp.server.stdio
# 创建 Server 实例
app = Server("my-first-mcp-server")
# 注册工具列表
@app.list_tools()
async def list_tools() -> list[Tool]:
return [
Tool(
name="say_hello",
description="向指定的人打招呼(这是你的第一个 MCP 工具)",
inputSchema={
"type": "object",
"properties": {
"name": {
"type": "string",
"description": "要打招呼的人的名字"
}
},
"required": ["name"]
}
),
Tool(
name="calculate_bmi",
description="计算 BMI 指数(因为我经常去健身房所以加了这个)",
inputSchema={
"type": "object",
"properties": {
"weight_kg": {"type": "number", "description": "体重(公斤)"},
"height_cm": {"type": "number", "description": "身高(厘米)"}
},
"required": ["weight_kg", "height_cm"]
}
)
]
# 实现工具调用
@app.call_tool()
async def call_tool(name: str, arguments: dict) -> list[TextContent]:
if name == "say_hello":
person_name = arguments.get("name", "stranger")
return [TextContent(
type="text",
text=f"你好,{person_name}!欢迎来到 MCP 的世界,从今天起你也是入门选手了 🎉"
)]
elif name == "calculate_bmi":
weight = arguments["weight_kg"]
height = arguments["height_cm"] / 100 # 转换为米
bmi = weight / (height ** 2)
if bmi < 18.5:
status = "偏瘦,多吃点"
elif bmi < 24:
status = "正常,继续保持"
elif bmi < 28:
status = "偏胖,该去健身房了"
else:
status = "肥胖,明天就去健身房!"
return [TextContent(
type="text",
text=f"BMI: {bmi:.1f},状态:{status}"
)]
else:
raise ValueError(f"未知工具: {name}")
# 启动 Server
async def main():
async with mcp.server.stdio.stdio_server() as (read_stream, write_stream):
await app.run(
read_stream,
write_stream,
InitializationOptions(
server_name="my-first-mcp-server",
server_version="0.1.0"
)
)
if __name__ == "__main__":
import asyncio
asyncio.run(main())4.3 配置到 Claude Desktop
在 ~/Library/Application Support/Claude/claude_desktop_config.json(Mac):
{
"mcpServers": {
"my-first-server": {
"command": "python",
"args": ["/path/to/my_first_mcp_server.py"],
"env": {
"PYTHONPATH": "/path/to/your/project"
}
}
}
}配置完重启 Claude Desktop,你应该能看到工具图标出现了。
如果没有出现……恭喜你,开始踩坑了,请往下看。
五、进阶:带 Resources 的 MCP Server
# advanced_mcp_server.py
# 带资源读取功能,比如让 AI 读取你的博客文章
from mcp.server import Server
from mcp.types import Resource, TextContent, EmbeddedResource
import json
import os
app = Server("blog-mcp-server")
# 模拟博客文章数据库
BLOG_POSTS = {
"post-001": {
"title": "从入门到放弃:Go-zero 实战",
"content": "今天我们来聊聊 Go-zero...",
"tags": ["golang", "微服务"]
},
"post-002": {
"title": "支付系统架构设计",
"content": "作为一个每天和支付系统打交道的人...",
"tags": ["支付", "架构"]
}
}
# 注册资源列表
@app.list_resources()
async def list_resources() -> list[Resource]:
resources = []
for post_id, post in BLOG_POSTS.items():
resources.append(Resource(
uri=f"blog://posts/{post_id}",
name=post["title"],
description=f"博客文章:{post['title']}",
mimeType="text/plain"
))
return resources
# 读取资源内容
@app.read_resource()
async def read_resource(uri: str) -> str:
# 解析 URI
if uri.startswith("blog://posts/"):
post_id = uri.replace("blog://posts/", "")
if post_id in BLOG_POSTS:
post = BLOG_POSTS[post_id]
return json.dumps(post, ensure_ascii=False, indent=2)
raise ValueError(f"资源不存在: {uri}")六、真实案例:博客自动发布 MCP Server
这个是我实际在用的,把它集成到 n8n 工作流里,让 Claude 帮我自动发博客:
# blog_publisher_mcp.py
# 博客自动发布 MCP Server
# 警告:请提前配置好各平台的 API Token,否则你会发现发布失败的方式比你想象的多
import httpx
from mcp.server import Server
from mcp.types import Tool, TextContent
app = Server("blog-publisher")
PLATFORM_CONFIGS = {
"juejin": {
"api_url": "https://api.juejin.cn/content_api/v1/article/publish",
"token_env": "JUEJIN_TOKEN"
},
"zhihu": {
"api_url": "https://www.zhihu.com/api/v4/articles",
"token_env": "ZHIHU_TOKEN"
}
}
@app.list_tools()
async def list_tools():
return [
Tool(
name="publish_blog",
description="发布博客文章到指定平台",
inputSchema={
"type": "object",
"properties": {
"title": {"type": "string"},
"content": {"type": "string", "description": "Markdown 内容"},
"platforms": {
"type": "array",
"items": {"type": "string", "enum": ["juejin", "zhihu", "cnblogs"]},
"description": "要发布的平台列表"
},
"tags": {"type": "array", "items": {"type": "string"}}
},
"required": ["title", "content", "platforms"]
}
)
]
@app.call_tool()
async def call_tool(name: str, arguments: dict):
if name == "publish_blog":
results = []
for platform in arguments.get("platforms", []):
try:
# 实际调用各平台 API(伪代码)
result = await publish_to_platform(
platform=platform,
title=arguments["title"],
content=arguments["content"],
tags=arguments.get("tags", [])
)
results.append(f"✅ {platform}: 发布成功,ID={result['id']}")
except Exception as e:
results.append(f"❌ {platform}: 发布失败 - {str(e)}")
return [TextContent(type="text", text="\n".join(results))]
async def publish_to_platform(platform, title, content, tags):
# 实现各平台的具体发布逻辑
# 此处省略若干行让人崩溃的代码
pass七、踩坑大全 🕳️
这一节是本文精华,是用真实的眼泪换来的
坑 1:stdio vs SSE 傻傻分不清
现象:配置了 MCP Server,Claude Desktop 显示连接成功,但工具调用没有任何反应。
原因:Claude Desktop 只支持 stdio 传输,你却用了 SSE 或 HTTP。
解决:Claude Desktop 配置用 stdio,自己的 Client 再改 SSE。
坑 2:JSON Schema 写错导致工具不可用
现象:工具注册了,但 AI 从来不调用它,或者调用时参数解析失败。
原因:inputSchema 格式不对,AI 看不懂。
# ❌ 错误写法
Tool(
name="bad_tool",
inputSchema={
"properties": { # 缺少 "type": "object"
"name": "string" # 这不是合法的 JSON Schema
}
}
)
# ✅ 正确写法
Tool(
name="good_tool",
inputSchema={
"type": "object", # 必须有!
"properties": {
"name": {
"type": "string", # 必须是对象格式
"description": "参数描述要写清楚,AI 靠这个理解"
}
},
"required": ["name"] # 明确必填字段
}
)坑 3:异步陷阱
现象:Server 启动后立刻崩溃,或者工具调用超时。
原因:MCP Python SDK 全程异步,你混用了同步代码。
# ❌ 在 async 函数里用同步 IO
@app.call_tool()
async def call_tool(name, args):
import requests
# 这会阻塞事件循环,导致超时
result = requests.get("https://api.example.com/data")
return [TextContent(type="text", text=result.text)]
# ✅ 用 httpx 异步版本
@app.call_tool()
async def call_tool(name, args):
import httpx
async with httpx.AsyncClient() as client:
result = await client.get("https://api.example.com/data")
return [TextContent(type="text", text=result.text)]坑 4:路径问题(Windows 用户专属噩梦)
现象:Mac/Linux 正常,Windows 上 MCP Server 启动失败。
原因:路径分隔符、Python 环境变量、command 配置方式都不同。
// ❌ Mac 配置直接复制到 Windows 用
{
"command": "python",
"args": ["/Users/joey/mcp/server.py"]
}
// ✅ Windows 正确配置
{
"command": "C:\\Python311\\python.exe",
"args": ["C:\\Users\\Joey\\mcp\\server.py"]
}
// ✅ 更好的方式:用 uv 管理环境
{
"command": "uv",
"args": ["run", "--project", "C:\\mcp-project", "server.py"]
}坑 5:description 写的太烂,AI 不知道何时调用
现象:明明有对应工具,AI 却自己瞎编数据,不去调用工具。
原因:工具描述太模糊,AI 不知道什么时候该用。
# ❌ 描述太烂
Tool(
name="get_data",
description="获取数据"
# AI:获取什么数据?我怎么知道该用你?
)
# ✅ 描述清晰,触发条件明确
Tool(
name="get_payment_status",
description="""查询支付交易状态。
当用户询问某笔交易的状态、支付是否成功、转账进度时调用此工具。
支持通过交易ID(transaction_id)或订单号(order_no)查询。
返回交易状态、金额、时间戳等完整信息。""",
)坑 6:MCP Server 日志去哪了?
现象:出了问题完全不知道哪里错了,因为 Claude Desktop 吞掉了所有输出。
解决:把日志写到文件:
import logging
# 不能用 print,Claude Desktop 会把 stdout 当协议数据
# 必须写文件或者 stderr
logging.basicConfig(
level=logging.DEBUG,
format='%(asctime)s - %(name)s - %(levelname)s - %(message)s',
handlers=[
logging.FileHandler('/tmp/mcp_debug.log'),
logging.StreamHandler(sys.stderr) # stderr 是安全的
]
)
logger = logging.getLogger(__name__)坑 7:工具参数验证不够严格
现象:AI 传了错误格式的参数,Server 报了一个让人看不懂的错误。
最佳实践:用 Pydantic 做参数验证:
from pydantic import BaseModel, validator
class PublishArgs(BaseModel):
title: str
content: str
platforms: list[str]
@validator('platforms')
def validate_platforms(cls, v):
valid = {'juejin', 'zhihu', 'cnblogs'}
invalid = set(v) - valid
if invalid:
raise ValueError(f"不支持的平台: {invalid},有效值: {valid}")
return v
@app.call_tool()
async def call_tool(name: str, arguments: dict):
try:
args = PublishArgs(**arguments)
except Exception as e:
return [TextContent(type="text", text=f"参数错误: {e}")]八、最佳实践 & 设计模式
8.1 MCP Server 设计原则
一个Server管一类功能] A --> C[描述清晰
让AI知道何时调用] A --> D[错误友好
返回人类可读的错误] A --> E[幂等设计
重复调用不出问题] A --> F[超时处理
别让AI傻等] style A fill:#4A90D9,color:#fff style B fill:#50C878,color:#fff style C fill:#50C878,color:#fff style D fill:#50C878,color:#fff style E fill:#50C878,color:#fff style F fill:#50C878,color:#fff
8.2 与 LangChain Tools 的对比
LangChain Tools 优点:简单直接,在 LangChain 生态里无缝集成。
MCP 优点:跨框架复用,一次开发多处使用,社区已有大量现成 Server。
8.3 现成的 MCP Server 生态
不用重复造轮子,这些已经有成熟实现了:
| 类别 | 工具 | 地址 |
|---|---|---|
| 文件系统 | filesystem | 官方维护 |
| 数据库 | sqlite / postgres | 官方维护 |
| Git | git操作 | 官方维护 |
| 浏览器 | puppeteer | 官方维护 |
| 搜索 | brave-search | 官方维护 |
| GitHub | 仓库操作 | 官方维护 |
| Slack | 消息/频道 | 官方维护 |
| 记忆 | memory | 官方维护 |
完整列表:https://github.com/modelcontextprotocol/servers
九、学习路径:从入门到(尝试)精通
使用官方 MCP Server] A2 --> A3[运行 Hello World Server] A3 --> B[📚 基础阶段] B --> B1[实现带 Tools 的 Server] B1 --> B2[添加 Resources 支持] B2 --> B3[错误处理 & 日志] B3 --> C[🚀 进阶阶段] C --> C1[SSE 传输远程部署] C1 --> C2[集成第三方 API] C2 --> C3[构建完整业务 Server] C3 --> D[🏆 精通阶段] D --> D1[多 Server 协作架构] D1 --> D2[性能优化 & 监控] D2 --> D3[开源贡献 & 生态] D3 --> E[😱 放弃阶段] E --> E1[发现新的更好的方案] E1 --> E2[重新入门] E2 --> A style A fill:#90EE90,color:#000 style B fill:#87CEEB,color:#000 style C fill:#DDA0DD,color:#000 style D fill:#FFD700,color:#000 style E fill:#FF6B6B,color:#fff
推荐学习资源:
- MCP 官方文档:https://modelcontextprotocol.io
- 官方 SDK(Python):https://github.com/modelcontextprotocol/python-sdk
- 官方 SDK(TypeScript):https://github.com/modelcontextprotocol/typescript-sdk
- 官方 Server 示例:https://github.com/modelcontextprotocol/servers
十、总结
MCP是具体协议] A --> C[MCP = AI工具的USB标准] A --> D[Server-Client架构
JSON-RPC通信] A --> E[三大核心: Tools/Resources/Prompts] A --> F[踩坑经验
= 宝贵财富] style A fill:#4A90D9,color:#fff style F fill:#FF6B6B,color:#fff
一句话总结:
Skills 是目标,MCP 是实现目标的最优路径之一。
先理解 Skills 的概念,再用 MCP 的标准把它实现出来,就这么简单。
(当然,实际做起来不简单,否则你也不会来看这篇文章)