
我最近把一个内部项目的开发流程从“靠对话记忆”改成了一套固定的扩展栈CLAUDE.md 管项目记忆Skills 管可复用的操作流程插件管交互入口MCP 管外部工具连接。这套组合跑起来之后Claude Code 从一个“每次都要重新解释项目”的对话模型变成了一个真正有点“团队老成员”感觉的开发工具。这篇文章就围绕 Claude Code Extension Stack 这四个层次把我实际配置、开发、踩坑的过程完整拆开讲一遍。不管你是刚接触 Claude Code还是已经用了一段时间但总觉得它“差点意思”这篇内容都适合你。我会从每一层负责什么问题讲起再给出可以直接抄的配置示例、目录结构和排查思路。1. 这套扩展栈是什么四层结构背后的设计逻辑1.1 记忆、技能、交互、连接四层各管什么先给四个组件做一次定位这样后面看任何配置都不会乱。CLAUDE.md 是记忆层。它告诉 Claude“这个项目是什么、目录怎么摆、约定是什么”。放在项目根目录或用户主目录模型启动时会自动加载它相当于入职第一天发给新人的岗位说明。Skills 是能力层。它们是一组结构化的操作手册描述“当遇到某类任务时应该按什么步骤做”。比如“按团队规范创建新 Python 服务”“处理一批图片并生成缩略图”这样的高频场景。它能教会 Claude 做一件具体的事而不是只告诉它项目现状。插件是交互层。它把 Claude Code 的界面能力斜杠命令、自动钩子、子代理做成可编程的模块。比如注册一个/review命令或者让 Claude 在结束回答前自动跑一遍格式化。MCP 是连接层。它把外部工具和数据源标准化接入模型可以通过统一协议调用文件系统、数据库、网页抓取、内部 API。没有这层模型只是一个“很聪明的编辑器”有了这层它才能读到真实数据、执行真实动作。1.2 为什么单独用 CLAUDE.md 差一口气我见过很多人只放了 CLAUDE.md 就用 Claude Code用一段时间后反馈“它还是不懂我们项目的流程”。原因是 CLAUDE.md 描述的是项目和事实但很多工作流的难点在“怎么做”而不是“是什么”。举个例子假设项目要求所有新模块必须带测试、必须跑某个 lint 规则、必须更新某个 docs 索引。这些步骤写在 README 里没问题但模型每次遇到“新增模块”这个任务时都未必会主动去读那一段内容。它可能理解了项目结构却在动作层面漏步骤。Skills 补的正是这个缺口通过 description 与任务场景绑定当模型判断当前任务匹配时会主动加载对应技能并严格按步骤执行。插件再把触发入口固定化MCP 最后解决数据获取的问题。四者缺一个都会出现“知道项目却干不好活”或“活会干却拿不到数据”的断层。1.3 分层设计的版本管理与协作优势这套栈还有一个很实际的好处每一层都能独立维护、独立回滚。项目重构了只需要改 CLAUDE.md某个流程更新了只动 Skill交互方式变了只改插件脚本。它们都放在同一个 Git 仓库里团队成员 fork 后可以针对自己业务场景单独修改。把扩展栈当作工程代码来管理而不是当作“配置随手改”是团队落地时最关键的心态转变。2. CLAUDE.md把项目记忆写进文件而不是每次重新解释2.1 项目级与用户级两种 CLAUDE.md 的职责边界Claude Code 支持两个层级的记忆文件用户级的~/.claude/CLAUDE.md和项目级的项目根目录CLAUDE.md。两者会合并加载项目级的内容优先级更高。用户级适合放你的通用工作习惯比如常用的技术栈偏好例如新项目默认用某个包管理工具对代码风格的特殊偏好通用的命令缩写和别名约定项目级放的是“只有这个项目才成立的事实”仓库的模块划分和目录含义构建、测试、发布命令项目独有的命名规范与约束如果你发现自己在用户的 CLAUDE.md 里写了某个项目的模块名就应该把它挪到项目级去——否则你在做另一个项目时模型会被那些无关记忆干扰。2.2 一份可持续维护的 CLAUDE.md 模板我写 CLAUDE.md 时通常会控制篇幅目标不是“越全越好”而是“高频信息都有低频信息全部指向文档”。一份可长期维护的项目级文件大概长这样# 项目某跨平台系统 ## 一句话定位 这是一个面向内部运营团队的跨平台数据看板系统包含 Web 端和桌面端。 ## 架构速览 - src/web前端界面基于 Vue 3 TypeScript - src/desktop桌面端壳子负责本地文件读写 - src/server本地模拟服务用于开发和测试 - scripts/项目自带的辅助脚本按用途分目录存放 ## 常用命令 - 启动 Web 开发环境pnpm dev:web - 启动桌面端pnpm dev:desktop - 运行全部测试pnpm test - 生成模块文档pnpm docs:gen - 打包pnpm build --platformwin,linux ## 代码约定 - Web 端组件统一用 script setup 语法 - 状态管理只用 Pinia不允许在组件里直接改共享状态 - 提交信息必须符合 feat(scope): desc 格式 ## 高频任务 ### 新增一个数据看板页面 1. 在 src/web/views/dashboard 下创建组件 2. 在路由配置里注册 3. 把数据获取逻辑放进对应的 store 4. 补充测试文件 tests/unit/ 下同名 .spec.ts 5. 跑 pnpm test 确认全部通过 ## 不要这样做 - 不要直接修改 generated/ 目录它由脚本生成 - 不要往 src/server 写业务逻辑那只是模拟服务这段内容控制在 30 行左右所有关键信息都是模型真正高频用到的。测试命令、目录约定、任务步骤是最容易见效的三类内容。2.3 编写与维护的四个关键技巧第一命令语句要写成“模型可以直接执行”的形式。与其写“测试项目”不如给出一行pnpm test --run并注明运行前是否需要启动依赖服务。第二用“不要这样做”的负面约束。模型看到否定句式的警告往往比正面描述执行得更可靠。比如“不要用eval”“不要在测试里访问真实网络”这些规则能有效减少事故。第三让 Claude 帮忙生成初始版本。在新项目里我可以直接输入/init具体命令以你本地版本为准Claude 会自动扫描项目生成一份 CLAUDE.md 初稿。但它生成的内容通常太泛我会在此基础上逐项删改只保留真实约束。第四定期追问模型“你还知道项目哪些信息”。这不是客套话而是验证模型到底加载了什么。如果它回答的内容和 CLAUDE.md 不一致多半是文件没生效或路径找错了。2.4 何时更新项目变动的四个信号CLAUDE.md 没必要天天改但出现下面几种情况时应该立刻更新项目新增了核心目录现有结构说明失效构建工具或测试框架换了命令变了某个流程连续第二次靠人工纠正模型说明模型没掌握这个约定新成员加入时反复问同一个问题意味着信息缺失把 CLAUDE.md 当“活的记忆体”而不是“一次性配置”它才能真正跟上项目演进。3. Skills把“临时做对”变成“长期会做”3.1 Skills 与 CLAUDE.md 的分工陈述性知识与程序性知识用认知科学的分类来说CLAUDE.md 存的是陈述性知识项目是什么Skills 存的是程序性知识任务该怎么做。举个例子。CLAUDE.md 可以写“本项目所有数据库迁移脚本放在migrations/目录下”这解决的是“位置认知”。但如果遇到“新增一张用户表并生成迁移文件”这类任务模型还需要知道先改哪个模型文件、迁移命名规范是什么、是否需要手动补充索引、完成后跑哪个校验命令。这一串先后顺序就是 Skill 的典型内容。Skill 最重要的特性是“触发式加载”当模型判断当前任务与 Skill 描述匹配时会自动读取该 Skill 并按其步骤执行。这意味着你不用在每次对话里反复描述流程。3.2 Skill 目录结构与 SKILL.md 的编写规范一个 Skill 通常是一个目录放在.claude/skills/下.claude/skills/ └── create-python-service/ ├── SKILL.md └── scripts/ └── scaffold.pySKILL.md 是整个技能的核心。开头有一段 YAML frontmatter包含这个技能的元信息--- name: create-python-service description: 当需要创建新的 Python 微服务时使用。可以识别用户要求中的“新服务、新模块、新API”等关键词。不适用于修改已有的服务。 allowed-tools: - bash - edit - write ---name是技能唯一标识尽量用连字符分隔的短名称description决定了模型何时触发这个技能必须写清楚“用在什么场景”以及“不适用于什么”allowed-tools限定技能执行时可用的工具防止它跑偏到不该碰的操作正文部分建议按这个顺序组织前置条件、执行步骤、验收标准、常见陷阱。每一步尽量写成可验证的动作而不是模糊的描述。3.3 一次完整的 Skill 落地从操作记录到可复用技能我以“创建新 Python 服务”为例讲清楚整个开发过程。最初几次我是在对话里手工完成这件事的让 Claude 创建目录、复制一个模板文件、改配置文件、写一个最小测试。操作本身不复杂但步骤很多每次对话都要重复一遍。后来我先在 CLAUDE.md 里加了一段“新建服务”的快速步骤让模型优先参考。效果有改善但步骤还是会出现遗漏。于是我把步骤固化成 Skill。SKILL.md 的正文我写得非常具体# 创建 Python 服务 ## 前置条件 - 确认用户还没有创建同名目录 - 确认服务名符合团队命名规范小写字母数字用连字符分隔 ## 执行步骤 1. 在 services/ 下创建服务目录目录名即服务名 2. 复制 templates/py-service/ 下所有文件作为脚手架 3. 修改 pyproject.toml把 name 和 description 替换为实际服务信息 4. 在 tests/ 创建 test_service_name.py至少覆盖一个健康检查接口 5. 运行 pytest tests/test_service_name.py确认通过 ## 验收标准 - 服务目录只包含脚手架中出现的文件没有多余文件 - 测试可以通过 - 服务可以用 uvicorn service_name.main:app 启动 ## 常见陷阱 - 不要从网上复制大段模板代码 - 不要修改 services/ 下其他服务的文件 - 如果依赖组里没有对应依赖名先询问用户再添加写完之后的效果非常明显一旦对话中出现“新建一个服务”的意图模型会主动加载这个 Skill按顺序执行不再需要我一步步提醒。3.4 description、命名与触发时机的匹配细节触发是否准确90% 取决于 description 写得好不好。写得太泛比如“用于创建服务”模型会在各种场景下尝试触发产生干扰写得太窄比如“当用户提到 python microservice with auth using fastapi”又会漏掉真正的请求。我的经验是description 里同时给“正面触发词”和“反面排除词”。正面触发词帮助模型识别任务比如“新建服务”“创建 API”反面排除词减少误触发比如“不适用于修改已有服务”“不适用于生成页面组件”。还有一个小技巧如果某条 Skill 在最近几周都没被触发过先别急着删优先怀疑是 description 的问题而不是需求不存在。3.5 演进路径先文档、再脚本、最后 Skill我个人总结出的 Skill 落地节奏很简单遇到一个做了三次以上的重复性任务先把它写成 CLAUDE.md 里的操作步骤执行稳定之后再拆成独立脚本最后才封装成带 frontmatter 的 Skill 目录。一来避免过早创建大量 Skill 导致难以维护二来能保证每个 Skill 都是经过实战检验的流程。真正核心的技能是有数的不要为了“看起来很专业”而批量生成。4. 插件把交互入口也变成工程的一部分4.1 插件能做什么斜杠命令、钩子与子代理如果说 CLAUDE.md 和 Skills 解决的是“模型知道什么、会做什么”插件解决的就是“你如何和模型交互”。插件至少提供三类能力斜杠命令是手动入口。比如注册一个/daily-summary输入后执行“读取当天 Git 提交记录按模块生成变更摘要”。手动命令适合那些“不一定每次都需要但需要时非常固定”的操作。钩子Hooks是自动入口。它们监听模型生命周期事件。比如在 Claude 调用 bash 工具之前PreToolUse检查脚本内容在回答结束之前Stop自动跑格式化。钩子适合做“强制规范”它把流程约束从“靠模型自觉”升级为“机制强制”。子代理Agents是独立任务单元。可以理解为给 Claude 派一个“专注执行某项任务”的分身适合长时间运行的独立任务比如“审查这个 PR 的代码逻辑”。4.2 最小插件骨架目录、plugin.json 与命令文件一个插件本质上是一组文件的集合核心入口是一个plugin.json{ name: team-workflow, version: 1.0.0, description: 团队日常开发辅助插件, commands: [ { name: review, description: 启动代码审查流程, script: commands/review.js } ], hooks: [ { name: format-on-stop, matcher: **/*.py, hook: Stop, command: hooks/format.py } ] }目录结构如下team-workflow/ ├── plugin.json ├── commands/ │ └── review.js └── hooks/ └── format.py插件的name最好与目录同名便于管理。每个命令文件都需要有可执行权限很多人第一次配置插件发现命令不出现原因就是权限位没设置。4.3 Hook 实战用 Stop 钩子实现提交前自动检查我团队里最常用的一个钩子是“回答结束前自动检查改动文件是否符合格式化标准”。配置里写{ name: format-check, matcher: **/*.py, hook: Stop, command: hooks/format_check.py }matcher是文件匹配模式Stop是“模型每次回答结束时触发”command是实际执行脚本。脚本本身做三件事收集本次会话改动的 Python 文件运行格式化检查如果不过就返回提示信息并建议模型下一步修正。这样模型在输出完结果后会自动做一道质量闸门而不是把“跑不跑格式化”留给用户追问。Hook 的事件类型在不同版本里会有差异写配置前先查看当前版本的文档或claude --help的输出。我看到有不少人把matcher写得太宽比如**/*导致每次回答结束都触发一堆无关脚本白白浪费时间。4.4 插件调试与团队分发插件写好后常用的调试路径是先确认 plugin.json 能被扫描到再看命令文件能否独立执行。可以用类似/plugin的交互命令查看当前已加载的插件列表确认自己的插件是否在列。分发方面插件目录直接放进公共仓库就可以。团队里每个人 clone 之后放到约定的目录或者通过配置指定插件路径。不同版本对插件加载路径的默认值可能不同建议在团队文档里固定写法避免每台机器行为不一致。5. MCP把 Claude Code 接到真实的工具与数据源5.1 MCP 解决的核心问题协议统一与权限边界MCP 解决的问题很实在每个外部工具都一套 API总不能每接一个就让模型学一种新协议。MCP 把工具、资源和提示词统一成标准接口模型只需要学会一套调用方式剩下的交给 MCP Server 去适配底层系统。对使用者来说MCP 的感知是“多了一组可用工具”。Claude Code 默认支持如下操作添加 MCP Server、列出已注册的 Server、测试连接状态、移除不再使用的 Server。只要 Server 本身按协议实现模型就能看到它暴露的工具列表。MCP 同时把权限边界做得更清晰。模型只能调用 Server 暴露出来的工具不能直接访问 Server 所在机器的任意文件。这个边界非常重要当某个工具需要数据库只读权限时你应该在 Server 侧的配置里限制而不是在对话里交代“不要写”。5.2 在 Claude Code 注册 MCP Serverstdio 与 HTTP 两种传输注册 MCP Server 的命令大致如下具体参数以你的版本输出为准# 本地运行的 stdio 类型 claude mcp add local-fs --transport stdio -- npx modelcontextprotocol/server-filesystem /data # 远程 HTTP 类型 claude mcp add remote-api --transport http --url http://localhost:8080/mcpstdio类型适合只在本机使用、不需要跨网络访问的 Server。它的特点是模型进程直接拉起子进程通过标准输入输出通信延迟低但无法被远程共享。http或 SSE 传输适合部署在独立服务上的场景。例如把某个内部数据查询服务包装成 MCP Server部署在一个内网端口上所有团队成员都连同一个地址。添加之后可以用claude mcp list查看状态或者用claude mcp get server-name确认工具列表是否正常返回。如果你的接口是自研的先单独用其他客户端测通再接入 Claude Code能省掉很多定位问题的时间。5.3 几个实用场景文件系统、网页抓取、数据库查询我实际接入过的三类场景文件系统类 MCP。它能让 Claude 直接读取某个固定目录下的文件而不用通过 shell。好处是路径被固定在配置里模型无法随意访问任意目录对隐私保护更友好。网页抓取类 MCP。给 Server 一个 URL它返回页面正文。这个场景适合处理“需要参考某个在线文档但不想直接让模型盲抓整站”的情况。Server 侧可以加内容过滤、限制重定向次数避免模型陷入无限爬取。数据库查询类 MCP。这是我认为价值最大的方向。数据库工具接入后Claude 可以执行查询、看表结构、分析慢查询甚至生成迁移脚本。但这类工具权限一定要收敛线上的写库动作严格禁止最好在 Server 配置里就锁定为只读连接并在数据库账号层面再限制一次。5.4 权限模式与最小暴露原则Claude Code 提供不同的权限模式来控制工具调用是否需要用户确认。比如某些编辑操作需要逐个批准某些高风险命令直接禁止。我的建议是即使感觉“每次确认很烦”也保持默认的审批机制特别涉及文件删除、命令执行和网络请求时。最小暴露原则体现在两个层面一是 Server 注册时只暴露必要工具不把所有功能一股脑挂上去二是 MCP Server 列表里用不到的 Server 就移除或按需启用。每个 Server 的工具都会占用上下文窗口的描述空间太多工具会让模型“选择困难”反而降低工具命中率。6. 常见问题与排查速查表6.1 配了不生效路径、加载顺序与缓存问题现象常见原因处理方式CLAUDE.md 内容模型完全不知道文件放在了错误的目录确认在项目根目录重启会话后让模型复述项目信息CLAUDE.md 部分内容生效部分没有内容太长模型上下文被截断精简文件高频信息靠前低频信息外链Skill 从来没被触发description 写得太泛或太窄重写 description加入正面/反面触发词插件命令在输入/时不出现plugin.json 语法错误用 JSON 解析器检查文件确认命令文件有执行权限插件启用了但行为没变化钩子匹配模式错误检查 matcher 表达式是否匹配了实际目标文件路径路径问题是第一大类。Claude Code 对项目根目录的判定通常以启动时的工作目录为准。如果你在子目录里启动它项目级 CLAUDE.md 就不会被加载。我习惯每次都在仓库根目录启动避免这类问题。缓存和会话状态是第二大类。改了 CLAUDE.md 之后如果新开一个会话仍然读不到很可能是加载逻辑滞后或路径判断问题可以先用最简单的“让模型复述项目描述”来判断它到底看到了什么。6.2 连不上、调用慢MCP 与插件的连接类故障现象常见原因处理方式MCP Server 一直返回错误Server 进程没有正常启动手动执行启动命令看报错确认依赖安装完整MCP 工具有时能调用有时超时Server 处理慢或网络不稳定检查 Server 日志把同步调用改为异步任务HTTP 类型 MCP 首次连接失败传输地址写错或服务未监听先 curl 检查端口再注册插件 Hook 脚本报错但主流程没停脚本抛异常且被忽略在脚本里记录日志单独运行脚本复现错误MCP Server 排错有一条通用路径先在终端里独立运行 Server确认它本身能启动、能通信再接入 Claude Code。这个方法能排除掉至少一半的“配置问题”。因为很多 MCP 故障根本不在协议层而是启动依赖没装好。6.3 行为不符合预期触发冲突与权限不足第三个常见类型是“模型行为不符合预期”。比如它明明读了 Skills却不按步骤做或者明明有插件钩子却没有执行格式化。先检查权限模式。如果工具的自动执行权限没开模型可能在等待用户确认视觉上看起来像“没做”。这种情况下终端里会有权限确认提示注意多看输出不要只盯最终结果。再检查触发冲突。多个 Skill 都匹配同一任务时模型可能把两个流程混在一起。比如“创建服务”和“创建迁移脚本”同时触发就可能漏步骤。解决方式是精简 Skill 数量或者在 description 里加更严格的边界。还有种常见误判Hook 确实执行了但脚本逻辑本身有问题。比如matcher匹配了文件却没有正确读取本次改动列表导致格式化像“没跑过”。这种情况只能靠给 Hook 脚本加日志来定位。最后分享一个小经验把这套扩展栈当成一个会持续进化的系统而不是一次配完就结束的静态配置。每当你发现 Claude 重复犯错优先去想“该更新 CLAUDE.md、固化一个 Skill还是加一个 Hook 钩子”。我在实际项目中最大的体会是单个组件单独用都会有短板但四层叠加之后Claude Code 才能真正从一个聪明的对话工具变成一个稳定输出、可审计、可传承的团队工程资产。