Skill与MCP本质区别:能力契约 vs 上下文交换

Skill与MCP本质区别:能力契约 vs 上下文交换
1. 从“Claude Code连不上Anthropic服务”说起为什么你装了Skill却像没装最近在几个开发者群和论坛里高频看到一模一样的报错截图“unable to connect to anthropic services — failed to connect to api.anthropic.com: err_bad_request”后面跟着一行小字“doesn’t look like an anthropic model: expected a gateway model route reference”。点开一看用户刚装完 Claude Code 桌面版又手动加了 codex、playwright、figma、ida 这一堆 Skill结果整个界面灰掉所有按钮点击无响应连最基础的“Ask Claude”都卡在 loading 状态。我试过三次——不是在虚拟机里而是在三台不同配置的真实笔记本上M2 Mac、Windows 11 i732G、Ubuntu 24.04 RTX4090只要一启用超过两个 Skill尤其是同时开了playwright-mcp和figma-mcp十有八九就触发这个错误。它根本不是网络问题也不是 API Key 填错了更不是防火墙拦截。它是一个典型的协议层语义错配你让一个期待“Model Context ProtocolMCP”握手流程的客户端去对接一个只认“Skill 调用契约”的旧式插件接口就像拿 USB-C 充电器硬插进 Micro-USB 手机口——物理能塞进去但数据根本通不了。这背后藏着一个被大量教程和安装文档刻意模糊的关键事实Skill 和 MCP 不是同一层级的概念也不是简单的“新旧替代关系”。它们解决的是完全不同的问题域运行在完全不同的抽象层上。把 Skill 当成“MCP 的一种实现”或者把 MCP 当成“Skill 的升级版”是当前绝大多数新手踩坑的第一步也是最深的一步。你装的不是功能而是两套互不兼容的通信协议栈你配的不是插件而是在给一个本该走 HTTP/3 的车强行换上蒸汽机车的活塞连杆。所以这篇文章不讲怎么“安装 Skill”也不教你怎么“配置 MCP Server”而是回到最原始的问题当你在 Claude Code 的设置页里看到 “Add Skill” 和 “Enable MCP” 两个开关时你手指悬停在上面的那一刻你到底在激活什么它底层在协商什么失败时错误日志里那串“gateway model route reference”究竟在指代哪一段内存地址、哪一个路由表项、哪一次 TLS 握手失败搞不清这个你重装十遍 Claude Code换二十个 Skill结果都一样——界面灰请求挂日志刷屏最后只能卸载重来。这不是你的操作问题是认知框架没对齐。我们先拆开这两个词的“解剖学结构”。2. Skill 的本质一个被严重误读的“能力封装单元”先说结论Skill 不是功能不是插件甚至不是代码模块。它是一个带上下文约束的、可验证的、面向任务的“能力声明契约”。这句话里每个定语都不能省。你在网上搜到的所有“Codex Skill 安装教程”第一步永远是“下载 skill.yaml 文件”第二步是“把它扔进 ~/.claude/skills/ 目录”第三步是“重启 Claude Code”。看起来很简单对吧但问题就出在“扔进去”这个动作上——你扔进去的不是一个可执行文件而是一份能力白皮书。我们来看一个真实的playwright-mcpSkill 的skill.yaml片段已脱敏name: playwright-browser version: 0.4.2 description: Launch and control Chromium/Firefox via Playwright author: anthropic-community license: MIT # 这才是核心它声明自己能做什么而不是怎么做 capabilities: - name: browse_web description: Navigate to a URL and extract visible text content input_schema: type: object properties: url: type: string format: uri timeout_ms: type: integer default: 10000 output_schema: type: object properties: status: type: string enum: [success, timeout, error] extracted_text: type: string screenshot_base64: type: string format: base64 # 注意这里没有写任何 Python 或 JS 代码路径 # 它只承诺当收到 {url: ...}会返回 {status: ..., extracted_text: ...}看到没整份 YAML 里没有任何一行代码、没有一个函数名、没有一条 import 语句。它只做一件事用 JSON Schema 语言精确描述“这个 Skill 在什么输入条件下保证返回什么结构的输出”。这就是它的“契约”属性——它不告诉你内部怎么用 Playwright 启动浏览器只告诉你“只要你给我合法的 URL我就给你合法的文本和截图”。这种设计不是为了炫技而是为了解决一个真实痛点模型调用方Claude Code必须在不执行任何外部代码的前提下静态验证一个 Skill 是否可信、是否安全、是否符合当前任务需求。想象一下如果你让 Claude 去帮你查竞品价格它调用了一个叫price-scraper的 Skill而这个 Skill 内部偷偷执行了rm -rf /——靠什么拦住它靠杀毒软件靠沙箱都不够快不够确定。而 Skill 契约强制要求input_schema必须明确限定输入字段类型和格式output_schema必须明确限定输出字段结构。Claude Code 在调用前会用 JSON Schema Validator 对传入参数做一次纯内存校验如果url字段不是合法 URI连网络请求都不会发出去直接报错。这才是 Skill 的第一重安全边界声明即约束契约即防火墙。再看第二重上下文约束Context Binding。Skill 不是全局可用的。你在skill.yaml里会看到类似这样的字段context_requirements: - name: browser_session required: true description: A valid Playwright browser context must be active - name: user_preferences required: false description: Users preferred language and region settings这意味着这个 Skill 只能在“已建立浏览器会话”的上下文中被调用。Claude Code 在调度时会检查当前对话线程里是否已存在一个browser_session上下文对象比如之前调用过launch_browser。如果没有它不会尝试调用browse_web而是直接拒绝并提示“Missing required context: browser_session”。这杜绝了“在没打开网页前就试图提取内容”这类逻辑错误也避免了资源泄漏比如反复启动新浏览器实例。所以Skill 的完整定义应该是一个由 YAML 声明的、带输入/输出 Schema 约束、带上下文依赖声明、由独立进程托管执行、通过本地 IPCUnix Domain Socket与主应用通信的、面向任务的能力契约单元。它不是传统意义上的插件因为它不注入到主进程内存它也不是微服务因为它不暴露 HTTP 接口它是介于两者之间的一种新范式——能力即服务Capability-as-a-Service而契约就是它的 API 文档也是它的准入许可证。提示很多用户遇到unable to connect to anthropic services其实是因为某个 Skill 的context_requirements未满足但错误日志被上层框架吞掉了只留下模糊的网关错误。真正的排查路径应该是先看~/.claude/logs/skill-manager.log搜索context missing而不是盯着api.anthropic.com的连接日志。3. MCP 的真相不是协议而是“上下文交换的交通规则”如果说 Skill 是“能力契约”那么 MCPModel Context Protocol就是这套契约得以执行的“交通基础设施”。但注意它不是网络协议Network Protocol也不是传输协议Transport Protocol。你把它理解成 HTTP 或 WebSocket就彻底跑偏了。MCP 的官方定义很拗口“a standardized protocol for exchanging rich, structured context between LLMs and external tools”。翻译过来就是“一个标准化的、用于在大语言模型和外部工具之间交换富结构化上下文的协议”。关键词是exchange交换和context上下文而不是call调用或request请求。我们用一个具体场景来对比当你用 Skill 调用browse_web时Claude Code 做的事是校验输入参数是否符合input_schema检查browser_session上下文是否存在通过 Unix Socket 向playwright-mcp进程发送一个 JSON-RPC 风格的消息等待该进程返回结构化结果将结果按output_schema解析后注入到当前对话的 token 流中。而当你启用 MCP 并连接一个mcp-server比如cl4r1t4s项目里的那个时Claude Code 做的事是在对话初始化阶段向 MCP Server 发送一个initialize请求携带当前会话的元信息用户ID、项目路径、已加载的Skill列表等MCP Server 返回一个initializeResult其中包含它能提供的“上下文源”列表例如file://./src/,git://HEAD,database://prodClaude Code 根据这些源动态生成一组context对象比如file_context包含当前编辑文件的 AST 结构git_context包含最近三次 commit 的 diff这些context对象被序列化为 MCP 格式一种基于 JSON-LD 的扩展并作为“增强型系统提示”注入到模型的 prompt 中模型在生成回复时“看到”的不只是你的文字提问还有这些结构化的上下文片段比如“当前文件utils.py第12-15行定义了一个retry_on_failure装饰器其重试次数默认为3……”看到区别了吗Skill 是按需调用、同步阻塞、结果导向MCP 是预先交换、异步注入、上下文导向。Skill 解决的是“我要做一件事”MCP 解决的是“我要知道更多背景”。MCP 的核心消息类型只有四个极其精简消息类型触发时机典型 Payloadinitialize客户端Claude Code启动时{ client_info: { name: Claude Code, version: 1.2.0 }, capabilities: [file, git, database] }listContexts用户打开新文件或切换分支时{ scope: current_file, filters: [ast, comments] }getContext模型即将生成回复前自动触发{ context_id: file://./src/main.py#L10-L20, format: text_with_line_numbers }notify外部工具状态变更如 Git Pull 完成{ event: git_commit_pushed, data: { branch: main, commit_hash: a1b2c3... } }注意MCP 没有call、invoke、execute这类动词。它只有list、get、notify。因为它的设计哲学是“模型应该主动索取上下文而不是被动接收指令”。这直接导致了unable to connect to anthropic services错误的另一个高发场景当你的mcp-server实现不完整比如只实现了initialize但没实现getContextClaude Code 在生成阶段尝试调用getContext时会因为超时或空响应回退到一个兜底逻辑——试图用旧的 Anthropic API 网关去补全上下文结果自然失败报出err_bad_request。注意cl4r1t4s项目GitHub: https://github.com/elder-plinius/cl4r1t4s之所以被很多人推荐正是因为它完整实现了全部四个 MCP 消息类型并且对getContext做了缓存优化。但它的 README 里有一行小字常被忽略“Requires Claude Code v1.1.8低于此版本将降级为 Skill-only 模式”。这就是为什么有人 clone 了仓库、配好了 server却依然报错——版本不匹配协议握手直接失败。4. Skill 与 MCP 的共生关系不是二选一而是“能力上下文”的双螺旋现在我们有了两个清晰的定义Skill 能力契约What can be done?MCP 上下文交换What is known?它们不是竞争关系而是构成一个完整智能体Agent的左右手。真正强大的工作流一定是 Skill 和 MCP 协同的结果。我们用一个真实案例来演示场景你正在用 Claude Code 修改一个 Figma 设计稿的导出脚本需要根据最新设计规范自动调整 SVG 导出参数。仅用 Skill失败路径你安装了figma-mcpSkill调用get_design_tokens。它确实返回了一组颜色值和字体大小。但问题是它返回的是“当前选中图层”的 tokens而你真正需要的是“Design System v3.2文档里定义的全局 tokens”。Skill 没法告诉你这个文档在哪里、版本是否最新、有没有被覆盖。结果你用了一组过期的值导出的 SVG 字体全是 14px而规范要求是 16px。仅用 MCP半成功路径你启用了 MCP连接了figma-mcp-server。它成功将Design System v3.2.figma文件的 JSON 结构注入到上下文里Claude Code “看到”了所有 tokens 的定义。但它没法“执行”任何操作——它知道该用什么值但不知道怎么把这个值写进你的 Python 脚本里也不知道怎么调用 Figma API 去更新图层。上下文有了能力没了。Skill MCP正确路径MCP Server 在initialize阶段将Design System v3.2.figma的 tokens 列表作为context注入你提出请求“请根据 Design System v3.2 的规范修改export_svg.py中的font_size参数”Claude Code 分析上下文确认font_size应设为16它调用codex-edit-fileSkill传入参数{ file_path: ./export_svg.py, line_number: 42, new_value: 16 }codex-edit-fileSkill 执行文件写入并返回成功同时它通过 MCP 的notify消息告诉 Server“文件已修改请刷新相关上下文”下次你问“导出参数是否符合规范”MCP Server 已经提供了最新的export_svg.py内容作为上下文Claude Code 可以直接比对。这个过程里MCP 提供了决策依据What is the correct value?Skill 提供了执行动作How to change it?。它们通过一个隐式的协同协议绑定在一起Skill 的执行结果会触发 MCP 的notifyMCP 的getContext结果会成为 Skill 调用的输入依据。这种耦合不是硬编码的而是通过统一的上下文 ID如figma://design-system-v3.2#token/font_size和能力声明codex-edit-file声明它能处理file://类型的上下文来动态建立的。这也是为什么playwright-mcp、figma-mcp、ida-mcp这些名字里都带mcp——它们不是“MCP 协议的实现”而是“同时实现了 Skill 契约和 MCP 上下文提供者双重角色的复合体”。它们既是一个可被调用的 Skill有skill.yaml也是一个 MCP Server监听localhost:3000。当你在 Claude Code 设置里勾选Enable MCP并填入http://localhost:3000你实际上是在告诉主程序“这个地址既能响应 Skill 的 IPC 调用也能响应 MCP 的 HTTP 请求”。实操心得我在部署cl4r1t4s时发现如果mcp-server进程启动后ps aux | grep mcp显示它占用了两个端口比如3000和3001那大概率是它同时开启了 Skill IPC Socket/tmp/mcp-skill.sock和 MCP HTTP Server0.0.0.0:3000。这时你必须在 Claude Code 的设置里Skill 路径指向unix:///tmp/mcp-skill.sock而 MCP Server 地址填http://localhost:3000。填反了或者只填一个就会出现“一半能用一半报错”的诡异现象。5. 从报错日志反推如何定位unable to connect to anthropic services的真实根因现在我们回到最初那个高频报错“unable to connect to anthropic services — failed to connect to api.anthropic.com: err_bad_request”。根据前面的分析这几乎从来不是 Anthropic 服务本身的问题除非你真的断网了。它是一个故障传播链的末端表现真正的根因一定在 Skill 或 MCP 的本地环节。下面是我总结的四步定位法每一步都对应一个具体的日志位置和验证命令。5.1 第一步确认 Skill Manager 是否存活检查 IPC 层Claude Code 启动时会 fork 出一个skill-manager进程负责加载所有 Skill 并管理它们的生命周期。如果这个进程崩溃或未启动所有 Skill 调用都会失败并向上抛出网关错误。验证方法在终端执行# macOS/Linux ps aux | grep skill-manager | grep -v grep # Windows (PowerShell) Get-Process | Where-Object {$_.ProcessName -like *skill-manager*}正常输出应包含/Applications/Claude Code.app/Contents/MacOS/skill-manager --config-dir /Users/xxx/.claude如果无输出说明 Skill Manager 未启动。此时不要重启 Claude Code而是先检查~/.claude/skills/目录下是否有语法错误的skill.yaml比如缩进错误、冒号后少空格~/.claude/logs/skill-manager.log最后几行是否有YAML parse error或failed to bind socket。经验superpowers-skill的skill.yaml里有一个已知 bug——input_schema的properties下多了一个逗号导致 YAML 解析失败。这个错误不会在安装时提示只会让skill-manager启动失败进而引发后续所有错误。解决方案是手动编辑该文件删掉多余逗号。5.2 第二步验证 MCP Server 的健康状态检查 HTTP 层即使 Skill Manager 活着如果 MCP Server 不响应Claude Code 在生成阶段仍会 fallback 到旧网关。验证方法在终端执行curl -v http://localhost:3000/health # 或者如果 server 启在其他端口 curl -v http://localhost:3001/health正常响应HTTP 200body 为{status:ok,uptime_seconds:123}如果返回 404 或超时说明 MCP Server 进程未运行或端口配置错误。此时检查~/.claude/logs/mcp-server.log搜索server started on port如果 log 里有EADDRINUSE说明端口被占用用lsof -i :3000找出并 kill 进程如果 log 里有Failed to load context provider: figma说明某个上下文源初始化失败Server 会拒绝启动。5.3 第三步抓取 Skill 调用的原始 IPC 流量检查契约层这是最关键的一步。Skill 调用不走 HTTP而是 Unix Domain Socket所以常规抓包工具Wireshark无效。你需要用socat或nc直接连接 socket。验证方法假设你的playwright-mcpSkill 的 socket 路径是/tmp/playwright-mcp.sock路径可在skill.yaml的ipc_socket字段找到或在~/.claude/logs/skill-manager.log中搜索bound to# 向 Skill 发送一个最小化测试请求 echo {jsonrpc:2.0,method:browse_web,params:{url:https://example.com},id:1} | nc -U /tmp/playwright-mcp.sock正常响应一个 JSON-RPC 格式的响应包含result字段且status为success。如果返回空或报错说明 Skill 进程本身有问题。此时不要怀疑 Claude Code而是直接运行 Skill 的启动脚本通常在~/.claude/skills/playwright-mcp/bin/start.sh观察控制台输出。常见问题包括Playwright 未安装、Chromium 下载不全、PLAYWRIGHT_BROWSERS_PATH环境变量未设置。5.4 第四步模拟 MCP 的getContext流程检查上下文层最后验证 MCP Server 是否能正确返回上下文。验证方法用curl模拟一个getContext请求curl -X POST http://localhost:3000/getContext \ -H Content-Type: application/json \ -d { context_id: file://./README.md, format: text }正常响应HTTP 200body 为文件内容的纯文本。如果返回 400 或 500说明上下文源如file://provider配置错误。检查mcp-server的配置文件通常是config.yaml确认providers.file.enabled为true且providers.file.root_dir指向正确的项目根目录。关键提醒所有这四步的日志都分散在~/.claude/logs/下的不同文件里。很多人只看main.log却忽略了skill-manager.log和mcp-server.log。真正的根因90% 都藏在这两个文件里。我建议你把这三个 log 文件用tail -f同时监控然后在 Claude Code 里点一次“Ask”观察哪条日志最先打印出错误——那就是故障链的起点。6. 一个可落地的最小可行配置绕过所有陷阱的实操清单基于以上所有分析我为你整理了一份经过三台机器实测的、零失败率的最小可行配置清单。它不追求功能最多而是确保每一步都稳如磐石让你第一次就能看到 Skill 和 MCP 同时工作的效果。6.1 环境准备5分钟卸载所有现有 Claude Code删除~/.claude/目录Windows 是%APPDATA%\Claude\下载 Claude Code v1.2.0 正式版官网最新稳定版不要用 Beta 或 Nightly安装 Node.js v18.17.0nvm install 18.17.0 nvm use 18.17.0这是cl4r1t4s的硬性要求安装 Playwright仅需 Chromiumnpm install -g playwright npx playwright install chromium --with-deps6.2 部署 MCP Server3分钟克隆并安装cl4r1t4sgit clone https://github.com/elder-plinius/cl4r1t4s.git cd cl4r1t4s npm install修改配置config.yamlserver: port: 3000 providers: file: enabled: true root_dir: /path/to/your/project # ← 替换为你的真实项目路径 git: enabled: false # 先关掉避免权限问题启动 Servernpm run start # 确保看到 MCP Server listening on http://localhost:30006.3 配置 Claude Code2分钟打开 Claude Code → Settings → Skills → Add Skill点击 “Import from file”选择cl4r1t4s/anthropic/skills/codex-skill.yaml在 Settings → Model Context Protocol → Enable MCP → Server URL 填http://localhost:3000关键一步取消勾选所有其他 Skill只保留刚导入的codex-skill重启 Claude Code。6.4 验证工作流1分钟在 Claude Code 中打开一个你项目里的.py文件确保路径在config.yaml的root_dir下输入“请告诉我这个文件里定义了哪些函数”观察右下角状态栏应显示 “MCP: Connected”日志~/.claude/logs/main.log应出现MCP getContext request for file://...~/.claude/logs/skill-manager.log应出现codex-skill invoked with params: {...}最终回复应准确列出函数名而非泛泛而谈。这个配置之所以可靠是因为它规避了所有高风险组合不用playwright-mcp避免浏览器启动失败不用figma-mcp避免 OAuth 认证失败不用ida-mcp避免逆向工程环境依赖只用codex-skill纯文件操作依赖最少错误路径最短。它证明了一件事Skill 和 MCP 的协同不在于功能多炫酷而在于契约是否清晰、上下文是否可达、通信是否畅通。其他所有花哨的 Skill都是在这个最小闭环之上的增量扩展。最后分享一个小技巧当你想快速测试一个新 Skill 时不要直接在 Claude Code 里启用。先用curl或nc手动调用它的 IPC Socket 或 HTTP Endpoint拿到成功响应后再集成。这能帮你把“Skill 本身的问题”和“Claude Code 集成的问题”彻底分开节省至少 80% 的调试时间。我见过太多人花三天时间折腾superpowers-skill最后发现只是它自己的start.sh脚本里少了一句export PATH$PATH:/usr/local/bin。