拓十年匠心定制 · 商业建站与技术教学双线并行 咨询热线:400-886-1026 service@lmnt.cn
ARTICLE DETAIL

资讯详情

深耕网站建设与运营推广的一线实战洞察。

MCP协议与Skill入门:AI Agent能力标准化交付指南

MCP协议与Skill入门:AI Agent能力标准化交付指南

1. 这不是新名词,而是软件能力交付方式的底层重构

你刷到“MCP”和“Skill”这两个词时,大概率是在某个AI工具的插件市场、开发文档角落,或者技术群聊里突然冒出来的——有人贴出一行链接wss://api.xiaozhi.me/mcp/?token=...,配文“已接入”,底下立刻有人问:“这玩意儿到底干啥的?跟Playwright、Chrome DevTools、Burp Suite有啥关系?”

这不是又一个营销造出来的概念。MCP(Model Control Protocol)本质是一套轻量级、面向AI Agent能力调用的通信协议规范;Skill则是按该协议封装的、可被任意支持MCP的运行时环境识别并执行的最小功能单元。它既不是硬件协议(比如USB或PCIe那种物理层定义),也不是传统意义上的软件协议(如HTTP或gRPC那种通用传输层抽象),而是一种语义层协议(Semantic Protocol):它不规定数据怎么传,而规定“能力”该怎么被描述、发现、协商与执行。

举个生活化类比:

HTTP是快递单模板——告诉你寄件人、收件人、物品名称、重量;
MCP则是“家电安装服务工单模板”——它强制要求写明:服务类型(装空调/修冰箱)、设备型号(格力KFR-35GW/NhAa1BAj)、安装条件(墙体承重≥200kg/m²)、验收标准(制冷15分钟出风口温度≤18℃)、失败回滚动作(若打孔遇钢筋,自动切换膨胀螺栓方案)。
Skill就是按这个工单模板填好的、盖了章的完整服务包。你把这份工单交给任何一家接入MCP标准的家政平台(比如WorkBuddy、Trae IDE、Cursor),它们不用改代码,就能直接调度师傅、派发工具、校验结果。

所以当你看到“Cursor怎么安装Skill”“Codex无法找到MCP”“Burp Suite搭载MCP Server”,背后的真实逻辑是:开发者正把原本散落在脚本、CLI命令、GUI操作、API调用里的“能力”,统一打包成标准化的、带元数据描述的、可跨平台调度的执行单元。这不是功能增强,而是交付范式的迁移——从“我写个Python脚本调用Playwright”变成“我注册一个Skill,声明‘我能自动化测试Web登录流程’,然后让AI Agent在需要时自动发现并调用它”。

关键词“入门科普”之所以重要,是因为当前所有公开资料都卡在两个断层上:

  • 一端是协议文档(如MCP v0.3 spec),满篇capability_manifest.json字段定义、execute_request消息结构,但没人告诉你“为什么必须定义input_schema而不是直接传JSON字符串”;
  • 另一端是实操教程(如“Ruoyi-Vue-Pro合并MCP功能”),直接甩出npm install @mcp/core和三行配置,却跳过最关键的环节:“你的Spring Boot后端如何安全暴露一个Skill Endpoint?Token怎么校验?超时怎么设?”

这篇内容就站在断层中间,用真实踩过的坑、调试过的日志、跑通的最小闭环,把MCP和Skill从“热搜词”还原成“可触摸的技术实体”。适合三类人:

  • 想给自家工具加AI能力的前端/后端工程师(比如你正在做IDE插件或低代码平台);
  • 正在评估Agent架构的AI产品经理(需要判断“接入MCP”是否真能降低技能集成成本);
  • 被“Skill开发指南”标题吸引但打开就懵的新手(本文会从curl -X POST开始,不假设你会Node.js)。

我们不讲“MCP将重塑AI生态”,只说清楚一件事:当你在浏览器控制台输入navigator.mcp?.listSkills()返回空数组时,问题90%出在WebSocket握手阶段的Origin头校验,而不是你的Skill没注册成功。这才是入门该知道的第一课。

2. 协议本质:MCP不是传输协议,而是能力契约的语法糖

很多人第一次接触MCP时,下意识把它当成类似WebSocket或HTTP的通信协议——这是最大的认知偏差。MCP本身不定义传输层。它的RFC文档(虽然还没正式发布)开篇就写明:“MCP operates over any bidirectional transport that supports message framing, such as WebSocket, HTTP/2 server-sent events, or even local IPC.” 换句话说,MCP只管“说什么”,不管“怎么送”。你可以用WebSocket(最常见),也可以用HTTP POST轮询(调试时更友好),甚至用本地Unix Socket(桌面应用内进程通信)。

那MCP到底定义了什么?它定义了一套能力契约(Capability Contract)的描述语言与交互流程。核心就三件事:

  1. 能力发现(Discovery):客户端如何知道“这个环境里有哪些Skill可用?”
  2. 能力协商(Negotiation):客户端和Skill之间如何就输入参数、输出格式、执行约束达成一致?
  3. 能力执行(Execution):实际调用时,消息体长什么样?错误怎么反馈?进度怎么上报?

我们拆解一个真实抓包记录(来自Trae IDE接入Burp Suite MCP Server的场景):

# 客户端发起能力发现请求(WebSocket Message) { "type": "list_skills", "id": "req_7f3a1b2c" }
# Skill Server返回的响应(注意:不是简单罗列名称,而是带完整契约) { "type": "skills_list", "id": "req_7f3a1b2c", "skills": [ { "id": "burp-active-scan", "name": "Burp Active Scan", "description": "Execute active scanning on a target URL with configurable scope and scan policy", "input_schema": { "type": "object", "properties": { "target_url": { "type": "string", "format": "uri" }, "scope": { "type": "string", "enum": ["in-scope", "out-of-scope"] }, "policy": { "type": "string", "default": "balanced" } }, "required": ["target_url"] }, "output_schema": { "type": "object", "properties": { "scan_id": { "type": "string" }, "status": { "type": "string", "enum": ["running", "completed", "failed"] } } }, "capabilities": ["http://mcp.dev/capabilities/async_execution"] } ] }

看到这里,你应该意识到:MCP的input_schema不是为了做JSON Schema校验玩的。它是运行时环境生成UI表单的依据。当Cursor检测到这个Skill时,会自动生成一个带URL输入框、Scope下拉菜单、Policy单选按钮的弹窗——用户根本不用看文档,界面就告诉ta要填什么。而capabilities字段则决定了环境能否调度它:如果环境不支持async_execution(比如一个纯同步的CLI工具),它就会把这个Skill标记为“不可用”,避免调用后卡死。

再看执行阶段的关键设计:

# 客户端发送执行请求(带唯一trace_id用于链路追踪) { "type": "execute_skill", "id": "exec_9a4b5c6d", "skill_id": "burp-active-scan", "input": { "target_url": "https://example.com/login", "scope": "in-scope", "policy": "aggressive" }, "trace_id": "trc_1234567890abcdef" }
# Skill Server分阶段返回(体现MCP对长任务的支持) # 阶段1:接受任务,返回初始状态 { "type": "execute_response", "id": "exec_9a4b5c6d", "status": "accepted", "execution_id": "exec_9a4b5c6d_001" } # 阶段2:执行中上报进度(可选,但强烈建议实现) { "type": "execution_progress", "execution_id": "exec_9a4b5c6d_001", "progress": 0.35, "message": "Crawling /login endpoint..." } # 阶段3:执行完成,返回结果 { "type": "execute_response", "id": "exec_9a4b5c6d", "status": "completed", "output": { "scan_id": "scan_abc123", "status": "completed" } }

这个分阶段响应机制,正是MCP区别于传统REST API的核心。REST里你只能等200 OK或504 Gateway Timeout,而MCP允许Skill主动推送进度、中断请求、甚至要求用户提供额外信息(比如"type": "request_input"消息)。这也是为什么“Playwright MCP”和“Browser Use MCP”的区别在于:前者是封装Playwright脚本为Skill,后者是让浏览器原生支持MCP协议——后者能直接触发DevTools的Page.captureScreenshot而不经过JS沙箱,延迟更低。

提示:很多新手在实现Skill Server时,习惯性把整个执行逻辑塞进一个HTTP Handler里,等结果出来再返回。这会导致MCP客户端长时间等待,最终超时。正确做法是:收到execute_skill后立即返回status: accepted,然后在后台线程/进程异步执行,并通过WebSocket连接主动推送execution_progress和最终execute_response。Trae IDE的源码里,mcp-server-core包的AsyncExecutor类就是干这个的。

3. Skill不是脚本,而是带身份、带契约、带生命周期的独立服务单元

把一个Python脚本或Shell命令打个包叫“Skill”,是当前最大的实践误区。真正的Skill必须满足三个硬性条件:可发现性(Discoverable)、可验证性(Verifiable)、可管理性(Manageable)。缺一不可。

先说可发现性。你写了个login_test.py,放在服务器/opt/skills/目录下,这不算Skill。Skill必须通过标准接口暴露其元数据。最简实现方式是提供一个HTTP端点:

GET /mcp/skills # 返回上面提到的skills_list结构

但生产环境必须用WebSocket握手时的capabilities字段声明自身支持哪些能力。比如你的Skill需要访问数据库,就必须在capabilities里声明"http://mcp.dev/capabilities/db_access",否则像Cursor这类安全敏感的IDE会直接屏蔽它——这是MCP内置的权限模型,比Linux文件权限更细粒度。

再看可验证性。MCP要求每个Skill必须附带数字签名(非强制但强烈推荐)。签名不是为了防篡改,而是解决“这个Skill到底是谁发布的”问题。签名流程如下:

  1. Skill开发者用私钥对skill_manifest.json(含id、name、input_schema等)生成SHA256哈希;
  2. 将哈希和公钥ID一起嵌入Manifest;
  3. 运行时环境(如WorkBuddy)用预置的公钥列表验证签名有效性。

为什么重要?看这个真实案例:某团队在内部部署了codex-skill-blue-lake(对接蓝湖设计稿),但上线后发现AI总是把按钮颜色识别错。抓包发现,调用的Skill其实是另一个团队发布的同名版本,只是input_schema里color_format字段从"hex"悄悄改成了"rgb"。没有签名机制,运行时环境无法区分哪个才是可信源。

最后是可管理性。Skill不是一次注册永久有效。MCP定义了完整的生命周期事件:

  • skill_registered:Skill首次被发现;
  • skill_updated:Manifest更新(比如input_schema变更);
  • skill_unavailable:Skill服务宕机或主动下线;
  • skill_removed:管理员手动移除。

这些事件必须通过WebSocket广播给所有监听客户端。我在调试RuoYi-Vue-Pro集成MCP时,就遇到过一个坑:前端页面缓存了旧版Skill列表,但后端Skill Server重启后发布了新版,由于没实现skill_updated事件推送,前端一直用着过期的input_schema,导致用户填的参数被后端拒绝。解决方案很简单:在Skill Server启动时,向所有已连接客户端发送skill_updated事件,并附带新旧Schema的diff摘要。

一个合格的Skill工程目录结构应该长这样:

burp-mcp-skill/ ├── manifest.json # MCP必需:id/name/description/input_schema等 ├── signature.sig # 签名文件(可选但推荐) ├── server/ # Skill Server实现(Node.js/Python/Java任选) │ ├── index.js # WebSocket服务入口 │ ├── handlers/ # 各能力处理逻辑 │ │ └── active-scan.js # Burp主动扫描的具体实现 │ └── utils/ # 公共工具(如Token校验、日志埋点) ├── client/ # 可选:供前端直接调用的SDK │ └── burp-skill-sdk.js └── tests/ # 必须包含契约测试(验证manifest与实际行为一致) └── test-contract.js

特别注意tests/test-contract.js:它不是测功能,而是测契约一致性。比如测试input_schema里声明target_url是必填项,那么当传入{}时,Skill Server必须返回明确的validation_error,而不是静默忽略或抛出500错误。这类测试用Jest+Supertest几行就能写完,但能避免90%的集成故障。

注意:很多教程教你怎么用@mcp/core库快速启动一个Skill Server,却漏掉最关键的一句——这个库默认开启CORS和Origin校验,而浏览器环境下的MCP客户端(如Cursor)发送的WebSocket请求Origin头是file://或vscode-webview://,必须显式配置allowedOrigins: ['*']或白名单。我在Trae IDE里调试时,花了3小时才定位到这个问题:控制台报WebSocket connection to 'wss://...' failed,但后端日志完全没记录,最后发现是Koa的@koa/cors中间件在握手阶段就拒掉了请求。

4. 从零搭建第一个MCP Skill:用Python实现一个天气查询服务

理论讲完,现在动手。我们用最简技术栈(Python + Flask + WebSocket)实现一个weather-skill,它接收城市名,返回当前温度和天气状况。目标:让任何支持MCP的客户端(比如你用curl模拟)都能发现并调用它。全程不依赖任何MCP官方SDK,只用基础库,让你看清协议本质。

4.1 第一步:定义Skill契约(manifest.json)

创建manifest.json,这是Skill的身份证:

{ "id": "weather-query", "name": "Weather Query", "description": "Get current weather condition and temperature for a city", "input_schema": { "type": "object", "properties": { "city": { "type": "string", "minLength": 2, "maxLength": 50 }, "unit": { "type": "string", "enum": ["celsius", "fahrenheit"], "default": "celsius" } }, "required": ["city"] }, "output_schema": { "type": "object", "properties": { "city": { "type": "string" }, "temperature": { "type": "number" }, "condition": { "type": "string", "enum": ["sunny", "cloudy", "rainy", "snowy"] }, "timestamp": { "type": "string", "format": "date-time" } } }, "capabilities": ["http://mcp.dev/capabilities/sync_execution"] }

关键点解析:

  • id必须全局唯一,建议用<domain>-<function>格式(如example-weather),避免和别人冲突;
  • input_schema里unit字段的default值,决定了客户端生成UI时的默认选项;
  • capabilities声明sync_execution,因为我们这个Skill执行很快(<1秒),不需要异步流程。

4.2 第二步:实现Skill Server(Flask + Flask-SocketIO)

新建app.py:

from flask import Flask, jsonify, request from flask_socketio import SocketIO, emit, disconnect import json import time from datetime import datetime import os app = Flask(__name__) app.config['SECRET_KEY'] = 'your-secret-key-change-in-prod' socketio = SocketIO(app, cors_allowed_origins="*", async_mode='threading') # 加载manifest(生产环境应从文件读取) SKILL_MANIFEST = json.load(open('manifest.json')) # 模拟天气数据(真实项目应调用OpenWeatherMap API) WEATHER_DATA = { "beijing": {"temperature": 22.5, "condition": "cloudy"}, "shanghai": {"temperature": 28.1, "condition": "sunny"}, "guangzhou": {"temperature": 31.7, "condition": "rainy"} } @app.route('/mcp/skills', methods=['GET']) def list_skills(): return jsonify({ "type": "skills_list", "skills": [SKILL_MANIFEST] }) @app.route('/mcp/skills/<skill_id>', methods=['GET']) def get_skill(skill_id): if skill_id != SKILL_MANIFEST['id']: return jsonify({"error": "Skill not found"}), 404 return jsonify(SKILL_MANIFEST) @socketio.on('connect') def handle_connect(): print('Client connected:', request.sid) # 发送能力列表(模拟list_skills响应) emit('skills_list', { "type": "skills_list", "skills": [SKILL_MANIFEST] }) @socketio.on('list_skills') def handle_list_skills(data): emit('skills_list', { "type": "skills_list", "skills": [SKILL_MANIFEST] }) @socketio.on('execute_skill') def handle_execute_skill(data): try: # 1. 校验skill_id if data.get('skill_id') != SKILL_MANIFEST['id']: raise ValueError(f"Unknown skill_id: {data.get('skill_id')}") # 2. 解析input(严格按input_schema校验) input_data = data.get('input', {}) if not isinstance(input_data, dict): raise ValueError("Input must be an object") if 'city' not in input_data: raise ValueError("Missing required field: city") city = input_data['city'].lower().strip() unit = input_data.get('unit', 'celsius') # 3. 执行业务逻辑 if city not in WEATHER_DATA: raise ValueError(f"Unknown city: {city}") weather = WEATHER_DATA[city] temp = weather['temperature'] if unit == 'fahrenheit': temp = temp * 9/5 + 32 # 4. 构建响应 result = { "city": city.title(), "temperature": round(temp, 1), "condition": weather['condition'], "timestamp": datetime.utcnow().isoformat() + 'Z' } # 5. 发送成功响应 emit('execute_response', { "type": "execute_response", "id": data.get('id', f"exec_{int(time.time())}"), "status": "completed", "output": result }) except Exception as e: # 发送错误响应(MCP要求明确的error结构) emit('execute_response', { "type": "execute_response", "id": data.get('id', f"exec_{int(time.time())}"), "status": "failed", "error": { "code": "VALIDATION_ERROR" if "Missing required field" in str(e) else "EXECUTION_ERROR", "message": str(e) } }) if __name__ == '__main__': socketio.run(app, host='0.0.0.0', port=5000, debug=True)

4.3 第三步:用curl验证契约(绕过浏览器限制)

浏览器环境受限多,先用curl验证协议层:

# 1. 获取Skill列表(HTTP方式) curl -X GET http://localhost:5000/mcp/skills # 2. 模拟WebSocket连接并发送execute_skill(用wscat工具) # 安装:npm install -g wscat wscat -c "ws://localhost:5000/socket.io/?EIO=4&transport=websocket" # 连接成功后,粘贴以下JSON(注意:wscat会自动加Socket.IO协议头,我们忽略) {"type":"execute_skill","id":"test_001","skill_id":"weather-query","input":{"city":"beijing"}} # 你应该看到类似响应: {"type":"execute_response","id":"test_001","status":"completed","output":{"city":"Beijing","temperature":22.5,"condition":"cloudy","timestamp":"2024-06-15T10:20:30.123Z"}}

4.4 第四步:接入真实客户端(Cursor为例)

Cursor官方文档明确支持MCP。在Cursor设置里启用MCP后,添加自定义Skill:

  1. 打开Settings → MCP → Add Custom Skill;
  2. 填入URL:ws://localhost:5000/socket.io/;
  3. 保存后,新建文件,输入/weather,Cursor会自动弹出城市输入框——这就是input_schema生效的证明。

实操心得:我在测试时发现Cursor对WebSocket路径很敏感。它默认尝试/mcp路径,但我们的Flask-SocketIO服务在根路径。解决方案有两个:一是修改Cursor配置指定路径,二是用Nginx反向代理把/mcp请求转到/socket.io/。后者更稳妥,因为避免了客户端兼容性问题。另外,manifest.json里的id必须全小写且不含特殊字符,Cursor会把它作为命令前缀(如/weather-query),如果写成WeatherQuery,命令会失效。

5. 生产环境避坑指南:那些文档里绝不会写的12个致命细节

写完第一个Skill只是开始。真正上生产,会撞上一堆协议文档里刻意回避的“灰色地带”。以下是我在Trae IDE、WorkBuddy、Cursor三个主流平台实测总结的12个致命细节,每个都曾让我加班到凌晨:

5.1 Token校验不是可选项,而是安全底线

wss://api.xiaozhi.me/mcp/?token=eyjhbgcioijfuzi1niisinr5cci6ikpxvcj9...这种带Token的URL,不是为了“鉴权”,而是为了建立信任链。MCP协议本身不定义Token格式,但所有主流平台(包括Cursor和Trae)都采用JWT。你的Skill Server必须:

  • 解析JWT的aud(Audience)字段,确保值为你的Skill ID(如weather-query);
  • 校验exp(Expiration)时间,建议设为24小时,避免长期有效Token泄露;
  • 检查iss(Issuer)是否在白名单内(如cursor.sh、trae.ai)。

漏掉任何一项,攻击者就能伪造Token调用你的Skill。我在测试Burp Suite MCP Server时,就因没校验aud,导致任何人用curl -H "Authorization: Bearer xxx"就能触发扫描——这相当于把渗透测试工具裸奔在公网。

5.2 WebSocket心跳间隔必须小于客户端超时阈值

MCP客户端(如Cursor)默认WebSocket心跳间隔是45秒。如果你的Skill Server心跳设为60秒,连接会在第46秒被客户端主动断开,然后疯狂重连。解决方案:

  • 在Flask-SocketIO里设置ping_interval=30;
  • 或在Nginx反向代理里加:
    location /socket.io/ { proxy_pass http://backend; proxy_http_version 1.1; proxy_set_header Upgrade $http_upgrade; proxy_set_header Connection "upgrade"; proxy_read_timeout 60; # 必须 > 客户端心跳间隔 }

5.3 输入校验必须严格,但错误提示要友好

MCP要求execute_response里error.message字段必须对用户可见。如果你返回"Internal Server Error",Cursor会直接显示这个字符串,用户一脸懵。正确做法:

  • 对ValidationError,返回"City name is required";
  • 对ExecutionError,返回"Failed to fetch weather data for Beijing. Please check network.";
  • 绝对不要暴露堆栈、路径、数据库名等敏感信息。

5.4 Skill ID不能动态生成,必须硬编码在manifest里

有人想用UUID生成Skill ID来“保证唯一”,这是大忌。MCP客户端会把Skill ID作为缓存键。如果每次启动都变,客户端永远无法复用之前获取的manifest,导致重复网络请求和UI重建。ID必须是稳定字符串,如com.example.weather。

5.5 异步Skill必须实现execution_cancel

长任务(如视频转码)必须支持取消。MCP定义了cancel_execution消息类型。你的Skill Server收到后,必须:

  • 终止后台进程(如os.kill(pid, signal.SIGTERM));
  • 发送{"type":"execute_response","status":"cancelled"};
  • 清理临时文件。
    否则用户点取消,任务还在后台跑,资源泄漏。

5.6 日志必须包含trace_id,且格式统一

所有日志行必须以trace_id=xxx开头。MCP客户端在发送execute_skill时会带trace_id,你的Skill Server必须透传到下游服务(如调用OpenWeatherMap API时,在Header里加X-Trace-ID: xxx)。否则排查问题时,你根本分不清哪条日志属于哪个用户请求。

5.7 CORS配置要精确,不能简单设为*

cors_allowed_origins="*"在开发时方便,但生产环境必须白名单:

  • Cursor:https://cursor.sh
  • Trae:https://app.trae.ai
  • WorkBuddy:https://workbuddy.ai
    否则浏览器会拦截WebSocket连接。

5.8 Manifest版本号必须随变更递增

manifest.json里加version: "1.0.0"字段。每次input_schema变更,版本号必须升级(如1.0.1)。客户端会对比版本号决定是否刷新缓存。否则用户更新了Skill,前端还是用旧Schema。

5.9 技能图标(icon)必须是SVG,且尺寸为64x64

Cursor和Trae都要求Skill图标是SVG格式,内联base64编码在manifest里:

"icon": "data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iNjQiIGhlaWdodD0iNjQiIHZpZXdCb3g9IjAgMCA2NCA2NCIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIj48cGF0aCBkPSJNMzIgMTZjLTguODMgMC0xNiA3LjE3LTE2IDE2czcuMTcgMTYgMTYgMTZjOC44MyAwIDE2LTcuMTcgMTYtMTZTMzkuODMgMTYgMzIgMTZ6bTAgMzJjLTQuNDIgMC04LTMuNTgtOC04czMuNTgtOCA4LThjNC40MiAwIDggMy41OCA4IDhTMzYuNDIgNDggMzIgNDh6IiBmaWxsPSIjZmZmIi8+PC9zdmc+"

PNG或JPG会被忽略,尺寸不对会拉伸变形。

5.10 错误码必须用MCP标准枚举

不要自创ERROR_DB_CONNECT这种码。必须用MCP定义的标准码:

  • VALIDATION_ERROR:输入不符合schema;
  • AUTHORIZATION_ERROR:Token无效;
  • RATE_LIMIT_EXCEEDED:调用频次超限;
  • SERVICE_UNAVAILABLE:后端依赖服务宕机。
    客户端会根据码做不同处理(如RATE_LIMIT_EXCEEDED会自动退避重试)。

5.11 环境变量必须区分开发/生产

.env文件里:

MCP_ENV=production MCP_JWT_SECRET=your-prod-secret-change-now MCP_ALLOWED_ORIGINS=https://cursor.sh,https://app.trae.ai

开发环境用MCP_ENV=development,关闭JWT校验,方便调试。

5.12 健康检查端点必须返回MCP标准格式

加一个GET /health端点,返回:

{ "status": "ok", "timestamp": "2024-06-15T10:20:30Z", "service": "weather-skill", "version": "1.0.0" }

Kubernetes等编排工具会轮询这个端点,决定是否重启Pod。

这些细节,没有一条写在MCP协议文档里,但每一条都决定了你的Skill是“能跑”,还是“能稳定服务”。我见过太多团队卡在第5.1条(Token校验)和第5.7条(CORS),花一周时间debug,最后发现只是Nginx配置少了一行。入门科普的价值,就在于帮你绕过这些本不该踩的坑。

6. 技术选型决策树:什么时候该用MCP,什么时候该坚持传统API?

看到这里,你可能会问:既然MCP这么复杂,为什么不用现成的REST API?这个问题直指核心——MCP不是银弹,它解决的是特定场景下的特定问题。下面这张决策树,是我帮7个团队做技术选型后总结的,帮你30秒判断是否该上MCP:

你的需求场景是否适合MCP关键原因替代方案
需要让AI Agent自动发现并组合多个工具(如:先查天气,再订机票,最后发邮件)✅ 强烈推荐MCP的list_skills和标准化input_schema让Agent能理解各Skill能力边界,自主编排流程自研Orchestration引擎(成本高,难维护)
已有成熟CLI工具,只想让Cursor一键调用⚠️ 谨慎评估MCP封装CLI很简单,但需额外维护WebSocket服务。如果只是单次调用,用Cursor的shell命令更轻量Cursor内置Shell命令
构建企业级低代码平台,需统一管理100+内部工具✅ 推荐MCP的契约驱动模式,让非技术人员也能通过UI配置Skill参数,大幅降低使用门槛OpenAPI 3.0 + Swagger UI(缺乏执行上下文感知)
实时性要求极高(<100ms),且调用频繁❌ 不推荐WebSocket握手、消息序列化、JSON Schema校验带来额外开销。HTTP/2 gRPC更优gRPC + Protocol Buffers
技能涉及敏感操作(如数据库删库、服务器重启)⚠️ 必须配合RBACMCP本身无权限模型,需在Skill Server层集成企业LDAP/AD,且capabilities字段要精细控制自研RBAC网关 + REST API

举个真实案例:某金融公司要做“AI备课Skill”,让教师用自然语言生成教案。他们最初想用MCP封装Python脚本,但很快发现:

  • 教案生成耗时2-5秒,MCP的WebSocket长连接在此场景下资源浪费严重;
  • 教师需要看到每一步生成过程(“正在分析教材大纲…”),MCP的execution_progress不如SSE(Server-Sent Events)流式响应直观;
  • 最关键的是,他们已有成熟的OAuth2.0体系,强行套MCP Token会增加审计复杂度。

最终方案:放弃MCP,用标准REST API + SSE流式响应 + OAuth2.0 Bearer Token。上线后QPS提升3倍,运维成本降为零。

再看另一个成功案例:某IDE厂商要集成Playwright自动化测试。他们用MCP的原因很实在——

  • Playwright脚本本身是黑盒,不同团队写的脚本参数千差万别;
  • 通过MCPinput_schema,IDE能自动生成统一UI(URL输入框、超时滑块、截图开关);
  • 当AI Agent说“帮我测试登录页”,IDE无需硬编码规则,直接list_skills找到playwright-login-test,填参执行。

所以,“1分钟搞懂MCP和Skill”的终点,不是学会怎么写代码,而是建立一个判断框架:当你的场景需要‘能力可发现、可组合、可被AI理解’时,MCP是基础设施;当你的场景只需要‘快速调用一个函数’时,REST API仍是王者。技术选型没有高下,只有是否匹配。

我在实际项目中,通常这样决策:先画一张“能力地图”——横轴是现有工具列表(Burp、Playwright、Vivado),纵轴是使用角色(开发者、测试、产品经理)。如果同一工具被3个以上角色以不同方式调用,且存在组合需求(如“用Playwright截图,再用OCR识别,最后存到Notion”),那就值得投入MCP。否则,老老实实写API,省下的时间够你喝三杯咖啡。

返回列表