乐于分享
好东西不私藏

技能、插件和 MCP——Claude Code 如何变得无限强大

技能、插件和 MCP——Claude Code 如何变得无限强大

前面几篇拆了启动链路、工具系统、上下文管理。但如果你只看这些,会觉得 Claude Code 是一个封闭的系统——功能就这么多,用完就没了。

实际上不是。Claude Code 有三套独立的扩展机制,让它的能力边界可以无限延伸。

今天拆这套扩展体系。

三套机制,三个层次

先看全景:

机制
作用
类比
技能系统(Skills)
给 AI 注入专业知识和行为规范
操作手册
插件系统(Plugins)
扩展功能模块,含技能和钩子
浏览器扩展
MCP 协议
接入外部工具服务器
USB 接口

三者层层递进:技能是最轻量的扩展,插件是中等粒度的功能包,MCP 是最底层的协议级接入。

技能系统:用 Markdown 写”操作手册”

技能系统的设计非常克制。一个技能就是一个目录,里面一个 SKILL.md 文件:

.claude/skills/my-skill/└── SKILL.md

SKILL.md 里用 frontmatter 定义元数据,用 Markdown 写具体内容:

---name: my-skilldescription: 做某件事的专业指南---当用户需要做 X 的时候,按以下步骤操作:1. 先做 A2. 再做 B3. 最后做 C

就这么简单。没有代码,没有配置文件,就是一个 Markdown 文件。

技能的加载机制也反映了这种克制:

async function loadSkillsFromSkillsDir(basePath, source) {  const entries = await fs.readdir(basePath)  return Promise.all(    entries.map(async (entry) => {      const skillDirPath = join(basePath, entry.name)      const skillFilePath = join(skillDirPath, 'SKILL.md')      const content = await fs.readFile(skillFilePath, { encoding: 'utf-8' })      const { frontmatter, content: markdownContent } = parseFrontmatter(content)      return createSkillCommand({        ...parseSkillFrontmatterFields(frontmatter, markdownContent, entry.name),        skillName: entry.name,        markdownContent,        source,        baseDir: skillDirPath,        loadedFrom: 'skills',      })    }),  )}

扫描目录、读 Markdown、解析 frontmatter、创建技能对象。整个技能系统本质上就是一个 Markdown 文件管理器。

技能可以来自多个位置:

  • • 项目级:.claude/skills/
  • • 用户级:~/.claude/skills/
  • • 内置:代码里打包的 bundled/ 目录
  • • 插件:随插件自动安装
  • • MCP:从外部 MCP 服务器获取

内置技能里有几个值得注意的:

export function initBundledSkills() {  registerUpdateConfigSkill()   // 配置管理  registerKeybindingsSkill()     // 快捷键  registerVerifySkill()         // 代码验证  registerDebugSkill()          // 调试  registerSimplifySkill()       // 代码简化  registerLoopSkill()           // 循环任务  if (feature('BUILDING_CLAUDE_APPS')) {    registerClaudeApiSkill()    // Claude API 开发  }}

registerLoopSkill 就是你在 Claude Code 里用 /loop 命令时触发的东西——定时循环执行某个任务。registerSimplifySkill 是代码审查和简化。

技能还有一个条件触发机制:

// 条件技能:根据文件路径自动激活const paths = splitPathInFrontmatter(frontmatter.paths)activateConditionalSkillsForPaths(filePaths, cwd)

在 frontmatter 里声明 paths 字段,当 AI 操作匹配路径的文件时,技能会自动激活。不用手动调用,AI 自己知道什么时候该用。

这个设计让我想到一个理念:最好的扩展是用户感知不到的扩展。 技能不是让用户去”使用”的,而是让 AI 自动”遵循”的。

插件系统:带依赖管理的功能包

插件比技能复杂得多。一个插件可以包含多个技能、多个钩子、多个 MCP 服务器配置。

export interface BuiltinPluginDefinition {  name: string  description: string  version: string  defaultEnabled?: boolean  isAvailable?: () => boolean  skills?: BundledSkillDefinition[]  hooks?: HooksSettings  mcpServers?: MCP[]}

注意 isAvailable 字段——插件可以声明自己的运行条件。比如某个插件只在 macOS 上可用,或者只在某个 Feature Flag 开启时才激活。

插件加载有一套完整的生命周期:

export async function loadPlugins(): Promise<LoadedPlugin[]> {  // 1. 从 marketplace 加载  const marketplacePlugins = await loadMarketplacePlugins()  // 2. 从 --plugin-dir 加载  const sessionPlugins = await loadSessionPlugins()  // 3. 构建依赖图  const pluginGraph = buildPluginDependencyGraph([    ...marketplacePlugins,    ...sessionPlugins,  ])  // 4. 拓扑排序  const sortedPlugins = topologicalSort(pluginGraph)  // 5. 加载所有组件  return await loadAllPluginComponents(sortedPlugins)}

插件之间有依赖关系,需要拓扑排序确定加载顺序。这不是简单的”按字母排序”,而是真正的图算法——插件 A 依赖插件 B,那 B 必须先加载。

还有一个热重载机制:

export function startPluginHotReloader() {  const watcher = chokidar.watch(pluginPaths, {    ignored: /(^|[\/\\])\../,    persistent: true  })  watcher.on(&#x27;change&#x27;, async (filePath) => {    clearPluginCaches()    const updatedPlugins = await reloadPluginsForPath(filePath)    notifyPluginsUpdated(updatedPlugins)  })}

用 chokidar 监听文件变化,插件文件一改就自动重新加载。开发插件的时候不用重启 Claude Code,改完文件直接生效。

这个体验很重要。 做过插件开发的人都知道,每次改代码都要重启宿主应用有多烦。Claude Code 把这个痛点解决了。

MCP:让外部世界接入 AI

MCP(Model Context Protocol)是整个扩展体系里最底层、最强大的机制。

简单说,MCP 是一个标准协议,让 AI 工具能调用外部服务器提供的工具和资源。任何语言、任何平台都可以实现 MCP 服务器,只要遵循协议,Claude Code 就能调用它。

export class McpClient {  private transport: Transport  private client: Client  private tools: Tool[] = []  async initialize(serverConfig) {    // 1. 创建传输层(stdio、HTTP等)    this.transport = await createTransport(serverConfig)    // 2. 初始化 MCP 客户端    this.client = new Client({ name: &#x27;claude-code&#x27;, version: &#x27;1.0.0&#x27; })    this.client.connect(this.transport)    // 3. 发现可用工具    this.tools = await this.client.listTools()    // 4. 发现可用资源    this.resources = await this.client.listResources()  }}

MCP 客户端做的事很清晰:连接服务器 → 发现工具和资源 → 调用工具。

关键的桥接在 MCPTool.ts 里。外部 MCP 服务器提供的工具会被自动注册到 Claude Code 的工具池里:

// 在 tools.ts 的工具组装过程中export function assembleToolPool(permissionContext, mcpTools) {  const builtInTools = getTools(permissionContext)  const allowedMcpTools = filterToolsByDenyRules(mcpTools, permissionContext)  return uniqBy(    [...builtInTools].sort(byName).concat(allowedMcpTools.sort(byName)),    &#x27;name&#x27;,  )}

内置工具和 MCP 工具合并,按名称去重,内置优先。对外部工具来说,它和内置工具在 AI 眼里没有区别。 AI 不需要知道一个工具是内置的还是 MCP 提供的,调用方式完全一样。

这意味着什么?意味着你可以用任何语言写一个 MCP 服务器,提供任意工具,Claude Code 就能直接使用。数据库查询、API 调用、数据分析、图像处理——只要你有 MCP 服务器,Claude Code 就有这个能力。

MCP 让 Claude Code 的能力边界从”Anthropic 提供了什么”变成”社区提供了什么”。

MCP 还支持会话过期检测和自动重连:

export function isMcpSessionExpiredError(error: Error): boolean {  if (error.message.includes(&#x27;Session not found&#x27;)) return true  return false}

外部服务器可能随时断开连接,Claude Code 会自动检测并尝试恢复。这种容错设计在生产环境中很必要——你不能指望外部服务永远稳定。

钩子系统:悄无声息的拦截器

除了上面三套机制,还有一个容易被忽略的系统——钩子(Hooks)。

钩子可以在特定事件发生时自动执行命令:

type HookEvent =  | &#x27;PermissionRequest&#x27;    // 权限请求时  | &#x27;PreToolUse&#x27;           // 工具使用前  | &#x27;PostToolUse&#x27;          // 工具使用后  | &#x27;PostToolUseFailure&#x27;   // 工具使用失败后  | &#x27;Stop&#x27;                 // 会话结束时  | &#x27;Notification&#x27;         // 通知时  | &#x27;SessionStart&#x27;         // 会话开始时

钩子可以阻止操作继续执行。比如 PreToolUse 钩子检查到某个危险操作,可以直接返回 deny,工具就不会被调用。

钩子可以来自多个地方:用户配置、项目 CLAUDE.md、插件。执行时按优先级排序,任一钩子返回 deny 就立即终止。

这套系统让用户可以在不修改任何代码的情况下,定制 Claude Code 的行为。比如”每次修改文件后自动跑测试”、”每次会话结束时自动提交 git”——都是通过钩子实现的。

三个系统如何协作

这三套机制不是孤立的,而是形成了一条链路:

用户写了一个 SKILL.md(技能)  → 技能里引用了一个 MCP 工具    → MCP 工具由一个插件提供      → 插件通过钩子自动配置了环境

举个例子:你安装了一个数据库插件。插件提供了:

  • • 技能:告诉 AI 如何安全地执行 SQL 查询
  • • MCP 服务器:提供数据库连接和查询工具
  • • 钩子:在每次查询前检查 SQL 是否包含危险操作

三个系统各司其职,组合在一起就是一个完整的解决方案。

彩蛋:Buddy 宠物系统

buddy/ 目录里藏着一个彩蛋——电子宠物陪伴系统。

function rollRarity(rng): Rarity {  const total = Object.values(RARITY_WEIGHTS).reduce((a, b) => a + b, 0)  let roll = rng() * total  for (const rarity of RARITIES) {    roll -= RARITY_WEIGHTS[rarity]    if (roll < 0) return rarity  }  return &#x27;common&#x27;}

每个用户根据 userId 确定性地生成一个宠物——有稀有度、有属性值、有峰值和谷值。而且是确定性的:同一个用户每次生成的宠物都一样。

用确定性随机,意味着宠物不会每次打开都变。这不像其他应用里随机开箱的玩法,更像是”你的专属伙伴”。

在一个51万行代码的严肃工程项目里,有人花时间做了这个。我觉得挺好的。工程师文化不只是代码质量,还有在严肃中保留一点乐趣的能力。

对开发者的真正启发

看完整个扩展体系,三个启发:

第一,扩展能力应该分层设计。 不是所有扩展都需要同等重量级的机制。一个简单的操作指南用 Markdown 就够了(技能),一个完整的功能包才需要插件,一个外部系统集成才需要 MCP。按需选择扩展粒度,而不是一刀切。

第二,协议比平台更重要。 Anthropic 推出 MCP 协议,本质上是在做”AI 工具界的 USB 接口”。与其自己实现所有功能,不如定义好标准,让社区来填充生态。这个思路和 Google 推出 Android、Apple 推出 App Store 是一样的——平台的终局不是自己做什么,而是让别人能在你上面做什么。

第三,AI 时代的软件架构正在变化。 传统的软件扩展靠 API 和插件。AI 工具的扩展靠协议(MCP)、提示词(技能)和钩子(行为控制)。三种机制对应三种不同的扩展维度——能力扩展、知识扩展、行为扩展。这个框架未来很可能会成为 AI 应用的标准架构模式。

最后一篇,我会做整个系列的总结——读完这些代码,对程序员 AI 转型的真实启发。


上一篇:在你开口之前,AI已经知道了很多——Claude Code上下文管理拆解

下一篇:读完51万行代码后,我找到了程序员 AI 转型的三个关键信号

我是贾昆,14年全栈程序员,正在 AI 转型路上。如果你也在纠结往哪个方向走,欢迎看看这篇:一个14年程序员的清醒与行动