6 6
7> 向 Claude Code 插件添加 skills、hooks、MCP 服务器和其他所有组件类型,并提供针对每种类型的验证示例。7> 向 Claude Code 插件添加 skills、hooks、MCP 服务器和其他所有组件类型,并提供针对每种类型的验证示例。
8 8
9export const Piece = ({id, children}) => <div className="pe-piece" data-piece={id}>{children}</div>;
10
11export const PluginExplorer = ({children}) => {
12 const PIECES = [{
13 id: 'manifest',
14 name: 'Manifest',
15 path: '.claude-plugin/plugin.json',
16 lines: [{
17 depth: 0,
18 kind: 'folder',
19 text: '.claude-plugin/'
20 }, {
21 depth: 1,
22 kind: 'file',
23 text: 'plugin.json'
24 }],
25 href: '/en/plugins/manifest-reference#manifest-file',
26 linkText: 'Go to the manifest reference'
27 }, {
28 id: 'skills',
29 name: 'Skills',
30 path: 'skills/review/SKILL.md',
31 lines: [{
32 depth: 0,
33 kind: 'folder',
34 text: 'skills/'
35 }, {
36 depth: 1,
37 kind: 'folder',
38 text: 'review/'
39 }, {
40 depth: 2,
41 kind: 'file',
42 text: 'SKILL.md'
43 }],
44 href: '/en/plugins/components#skills',
45 linkText: 'Go to the Skills section'
46 }, {
47 id: 'commands',
48 name: 'Commands',
49 path: 'commands/about.md',
50 lines: [{
51 depth: 0,
52 kind: 'folder',
53 text: 'commands/'
54 }, {
55 depth: 1,
56 kind: 'file',
57 text: 'about.md'
58 }],
59 href: '/en/plugins/components#commands',
60 linkText: 'Go to the Commands section'
61 }, {
62 id: 'agents',
63 name: 'Agents',
64 path: 'agents/security-reviewer.md',
65 lines: [{
66 depth: 0,
67 kind: 'folder',
68 text: 'agents/'
69 }, {
70 depth: 1,
71 kind: 'file',
72 text: 'security-reviewer.md'
73 }],
74 href: '/en/plugins/components#agents',
75 linkText: 'Go to the Agents section'
76 }, {
77 id: 'hooks',
78 name: 'Hooks',
79 path: 'hooks/hooks.json',
80 lines: [{
81 depth: 0,
82 kind: 'folder',
83 text: 'hooks/'
84 }, {
85 depth: 1,
86 kind: 'file',
87 text: 'hooks.json'
88 }],
89 href: '/en/plugins/components#hooks',
90 linkText: 'Go to the Hooks section'
91 }, {
92 id: 'monitors',
93 name: 'Monitors',
94 path: 'monitors/monitors.json',
95 lines: [{
96 depth: 0,
97 kind: 'folder',
98 text: 'monitors/'
99 }, {
100 depth: 1,
101 kind: 'file',
102 text: 'monitors.json'
103 }],
104 href: '/en/plugins/components#monitors',
105 linkText: 'Go to the Monitors section'
106 }, {
107 id: 'output-styles',
108 name: 'Output styles',
109 path: 'output-styles/terse.md',
110 lines: [{
111 depth: 0,
112 kind: 'folder',
113 text: 'output-styles/'
114 }, {
115 depth: 1,
116 kind: 'file',
117 text: 'terse.md'
118 }],
119 href: '/en/plugins/components#themes-and-output-styles',
120 linkText: 'Go to the Themes and output styles section'
121 }, {
122 id: 'themes',
123 name: 'Themes',
124 path: 'themes/dracula.json',
125 lines: [{
126 depth: 0,
127 kind: 'folder',
128 text: 'themes/'
129 }, {
130 depth: 1,
131 kind: 'file',
132 text: 'dracula.json'
133 }],
134 href: '/en/plugins/components#themes-and-output-styles',
135 linkText: 'Go to the Themes and output styles section'
136 }, {
137 id: 'workflows',
138 name: 'Workflows',
139 path: 'workflows/audit-routes.js',
140 lines: [{
141 depth: 0,
142 kind: 'folder',
143 text: 'workflows/'
144 }, {
145 depth: 1,
146 kind: 'file',
147 text: 'audit-routes.js'
148 }],
149 href: '/en/workflows#distribute-a-workflow-in-a-plugin',
150 linkText: 'Go to Distribute a workflow in a plugin'
151 }, {
152 id: 'bin',
153 name: 'Executables',
154 path: 'bin/hello-plugin',
155 lines: [{
156 depth: 0,
157 kind: 'folder',
158 text: 'bin/'
159 }, {
160 depth: 1,
161 kind: 'file',
162 text: 'hello-plugin'
163 }],
164 href: '/en/plugins/components#executables',
165 linkText: 'Go to the Executables section'
166 }, {
167 id: 'scripts',
168 name: 'Scripts',
169 path: 'scripts/format.sh',
170 lines: [{
171 depth: 0,
172 kind: 'folder',
173 text: 'scripts/'
174 }, {
175 depth: 1,
176 kind: 'file',
177 text: 'format.sh'
178 }],
179 href: '/en/plugins/components#hooks',
180 linkText: 'Go to the Hooks section'
181 }, {
182 id: 'settings',
183 name: 'Default settings',
184 path: 'settings.json',
185 lines: [{
186 depth: 0,
187 kind: 'file',
188 text: 'settings.json'
189 }],
190 href: '/en/plugins/components#default-settings',
191 linkText: 'Go to the Default settings section'
192 }, {
193 id: 'mcp',
194 name: 'MCP servers',
195 path: '.mcp.json',
196 lines: [{
197 depth: 0,
198 kind: 'file',
199 text: '.mcp.json'
200 }],
201 href: '/en/plugins/components#mcp-servers',
202 linkText: 'Go to the MCP servers section'
203 }, {
204 id: 'lsp',
205 name: 'LSP servers',
206 path: '.lsp.json',
207 lines: [{
208 depth: 0,
209 kind: 'file',
210 text: '.lsp.json'
211 }],
212 href: '/en/plugins/components#lsp-servers',
213 linkText: 'Go to the LSP servers section'
214 }];
215 const [selectedId, setSelectedId] = useState('manifest');
216 const [isFullscreen, setIsFullscreen] = useState(false);
217 const rootRef = useRef(null);
218 useEffect(() => {
219 const onFsChange = () => setIsFullscreen(!!document.fullscreenElement);
220 document.addEventListener('fullscreenchange', onFsChange);
221 return () => document.removeEventListener('fullscreenchange', onFsChange);
222 }, []);
223 const toggleFullscreen = () => {
224 if (!rootRef.current) return;
225 if (document.fullscreenElement) document.exitFullscreen(); else rootRef.current.requestFullscreen().catch(() => {});
226 };
227 const selected = PIECES.find(p => p.id === selectedId) || PIECES[0];
228 const onTreeKeyDown = e => {
229 const keys = ['ArrowDown', 'ArrowUp', 'Home', 'End'];
230 if (keys.indexOf(e.key) === -1) return;
231 const i = PIECES.findIndex(p => p.id === selectedId);
232 let next = i;
233 if (e.key === 'ArrowDown') next = Math.min(PIECES.length - 1, i + 1);
234 if (e.key === 'ArrowUp') next = Math.max(0, i - 1);
235 if (e.key === 'Home') next = 0;
236 if (e.key === 'End') next = PIECES.length - 1;
237 e.preventDefault();
238 if (next === i) return;
239 const id = PIECES[next].id;
240 setSelectedId(id);
241 const el = document.getElementById('pe-node-' + id);
242 if (el) el.focus();
243 };
244 const FolderIcon = () => <svg className="pe-icon" width="15" height="15" viewBox="0 0 16 16" fill="none" stroke="currentColor" strokeWidth="1.3" strokeLinejoin="round" aria-hidden="true">
245 <path d="M1.5 4.5a1 1 0 0 1 1-1h3.2l1.3 1.5h6a1 1 0 0 1 1 1V12a1 1 0 0 1-1 1h-10.5a1 1 0 0 1-1-1z" />
246 </svg>;
247 const FileIcon = () => <svg className="pe-icon" width="15" height="15" viewBox="0 0 16 16" fill="none" stroke="currentColor" strokeWidth="1.3" strokeLinejoin="round" aria-hidden="true">
248 <path d="M4 1.5h5.5L13 5v9.5H4z" />
249 <path d="M9.5 1.5V5H13" />
250 </svg>;
251 return <div ref={rootRef} className={isFullscreen ? 'pe-root pe-fullscreen not-prose' : 'pe-root not-prose'} data-selected={selected.id}>
252 <style>{`
253 .pe-root {
254 --pe-mono: var(--font-mono, ui-monospace, SFMono-Regular, Menlo, monospace);
255 --pe-accent: #D97757;
256 --pe-accent-text: #A8502F;
257 --pe-accent-bg: rgba(217,119,87,0.10);
258 --pe-bg: #FFFFFF;
259 --pe-surface: #FAFAF7;
260 --pe-hover: #F0EEE6;
261 --pe-border: #E8E6DC;
262 --pe-text: #141413;
263 --pe-text-2: #3D3D3A;
264 --pe-text-3: #5E5D59;
265 font-family: inherit;
266 background: var(--pe-bg);
267 color: var(--pe-text);
268 border: 1px solid var(--pe-border);
269 border-radius: 12px;
270 margin: 1.5rem 0;
271 overflow: hidden;
272 box-sizing: border-box;
273 }
274 .dark .pe-root {
275 --pe-accent-text: #EBA98F;
276 --pe-accent-bg: rgba(217,119,87,0.18);
277 --pe-bg: #1A1918;
278 --pe-surface: #232221;
279 --pe-hover: #2E2D2B;
280 --pe-border: #3A3936;
281 --pe-text: #F1EFE9;
282 --pe-text-2: #D6D4CA;
283 --pe-text-3: #B8B5AD;
284 }
285 .pe-root *, .pe-root *::before, .pe-root *::after { box-sizing: border-box; }
286 .pe-head { display: flex; align-items: flex-start; gap: 12px; padding: 18px 24px 16px; border-bottom: 1px solid var(--pe-border); }
287 .pe-head-text { flex: 1; min-width: 0; }
288 .pe-fs-btn { flex-shrink: 0; width: 32px; height: 32px; display: inline-flex; align-items: center; justify-content: center; border: 1px solid var(--pe-border); border-radius: 6px; background: var(--pe-surface); color: var(--pe-text-2); font-size: 15px; line-height: 1; cursor: pointer; }
289 .pe-fs-btn:hover { background: var(--pe-hover); }
290 .pe-fs-btn:focus-visible { outline: 2px solid var(--pe-accent); outline-offset: 2px; }
291 .pe-fullscreen { border-radius: 0; height: 100vh; display: flex; flex-direction: column; overflow: auto; }
292 .pe-fullscreen .pe-body { flex: 1; }
293 .pe-title { font-size: 19px; font-weight: 600; line-height: 1.3; color: var(--pe-text); margin: 0; }
294 .pe-sub { font-size: 15px; line-height: 1.5; color: var(--pe-text-3); margin: 4px 0 0; }
295 .pe-sub code { font-family: var(--pe-mono); font-size: 0.88em; padding: 1px 5px; border-radius: 4px; background: var(--pe-surface); border: 1px solid var(--pe-border); }
296 .pe-body { display: flex; align-items: stretch; }
297 .pe-tree-pane { width: 270px; flex-shrink: 0; background: var(--pe-surface); border-right: 1px solid var(--pe-border); padding: 16px 0 12px; }
298 .pe-panel { flex: 1; min-width: 0; padding: 16px 24px 24px; }
299 .pe-caption { font-size: 13px; font-weight: 600; color: var(--pe-text-3); margin: 0 0 10px; }
300 .pe-tree-pane .pe-caption { padding: 0 16px; }
301 .pe-rootline { display: flex; align-items: center; gap: 7px; padding: 3px 16px; font-family: var(--pe-mono); font-size: 13.5px; color: var(--pe-text-3); }
302 .pe-node {
303 display: block; width: 100%; margin: 0; padding: 3px 16px 3px 30px; text-align: left; cursor: pointer;
304 background: transparent; color: var(--pe-text-2);
305 border: none; border-left: 3px solid transparent;
306 font-family: var(--pe-mono); font-size: 13.5px; line-height: 1.4;
307 }
308 .pe-node:hover { background: var(--pe-hover); }
309 .pe-node:focus-visible { outline: 2px solid var(--pe-accent); outline-offset: -2px; }
310 .pe-node[aria-pressed="true"] { background: var(--pe-accent-bg); border-left-color: var(--pe-accent); color: var(--pe-accent-text); font-weight: 600; }
311 .pe-line { display: flex; align-items: center; gap: 7px; padding: 2px 0; }
312 .pe-line-tree { flex-wrap: wrap; }
313 .pe-line-tree .pe-req { flex-basis: 100%; margin: 2px 0 0 22px; white-space: normal; width: fit-content; max-width: calc(100% - 22px); }
314 .pe-line span { overflow-wrap: anywhere; }
315 .pe-piece { display: none; font-size: 16px; line-height: 1.6; color: var(--pe-text-2); }
316 .pe-root[data-selected="manifest"] .pe-piece[data-piece="manifest"],
317 .pe-root[data-selected="skills"] .pe-piece[data-piece="skills"],
318 .pe-root[data-selected="commands"] .pe-piece[data-piece="commands"],
319 .pe-root[data-selected="agents"] .pe-piece[data-piece="agents"],
320 .pe-root[data-selected="hooks"] .pe-piece[data-piece="hooks"],
321 .pe-root[data-selected="monitors"] .pe-piece[data-piece="monitors"],
322 .pe-root[data-selected="output-styles"] .pe-piece[data-piece="output-styles"],
323 .pe-root[data-selected="themes"] .pe-piece[data-piece="themes"],
324 .pe-root[data-selected="workflows"] .pe-piece[data-piece="workflows"],
325 .pe-root[data-selected="bin"] .pe-piece[data-piece="bin"],
326 .pe-root[data-selected="scripts"] .pe-piece[data-piece="scripts"],
327 .pe-root[data-selected="settings"] .pe-piece[data-piece="settings"],
328 .pe-root[data-selected="mcp"] .pe-piece[data-piece="mcp"],
329 .pe-root[data-selected="lsp"] .pe-piece[data-piece="lsp"] { display: block; }
330 .pe-piece p { margin: 0 0 10px; }
331 .pe-piece p:last-child { margin-bottom: 0; }
332 .pe-piece code { font-family: var(--pe-mono); font-size: 0.88em; padding: 1px 5px; border-radius: 4px; background: var(--pe-surface); border: 1px solid var(--pe-border); }
333 .pe-piece .code-block { margin: 12px 0 0; }
334 .pe-piece pre code { padding: 0; border: none; background: none; }
335 .pe-piece a { color: var(--pe-accent-text); }
336 .pe-line-compact { display: none; }
337 .pe-icon { flex-shrink: 0; }
338 .pe-req { margin-left: 8px; padding: 0 6px; border-radius: 999px; font-size: 11px; line-height: 18px; letter-spacing: .02em; color: var(--pe-accent-text); border: 1px solid var(--pe-border); background: var(--pe-surface); white-space: nowrap; font-weight: 500; vertical-align: middle; }
339 .pe-name { font-size: 22px; font-weight: 600; line-height: 1.25; letter-spacing: -0.2px; color: var(--pe-text); margin: 0; }
340 .pe-path { font-family: var(--pe-mono); font-size: 13.5px; color: var(--pe-accent-text); margin: 4px 0 0; overflow-wrap: anywhere; }
341 .pe-block { margin: 20px 0 0; }
342 .pe-link {
343 display: inline-block; margin: 24px 0 0; padding: 8px 14px; border-radius: 8px;
344 font-size: 14.5px; font-weight: 600; text-decoration: none;
345 color: var(--pe-accent-text); background: var(--pe-accent-bg); border: 1px solid var(--pe-accent);
346 }
347 .pe-link:hover { filter: brightness(0.97); }
348 .pe-link:focus-visible { outline: 2px solid var(--pe-accent); outline-offset: 2px; }
349 @media (max-width: 700px) {
350 .pe-head { padding: 16px 16px 14px; }
351 .pe-body { flex-direction: column; }
352 .pe-tree-pane { width: 100%; border-right: none; border-bottom: 1px solid var(--pe-border); }
353 .pe-line-tree { display: none; }
354 .pe-line-compact { display: flex; }
355 .pe-panel { padding: 16px 16px 20px; }
356 }
357 `}</style>
358
359 <div className="pe-head">
360 <div className="pe-head-text">
361 <div className="pe-title">What goes in a plugin</div>
362 <div className="pe-sub">This example plugin, <code>my-plugin</code>, has one of every kind of component, each in its default location. Select a file or folder to read what it’s for and see what goes in it.</div>
363 </div>
364 <button type="button" className="pe-fs-btn" onClick={toggleFullscreen} aria-label={isFullscreen ? 'Exit fullscreen' : 'Fullscreen'} title={isFullscreen ? 'Exit fullscreen' : 'Fullscreen'}>
365 {isFullscreen ? '⤡' : '⛶'}
366 </button>
367 </div>
368
369 <div className="pe-body">
370 <div className="pe-tree-pane">
371 <div className="pe-caption" id="pe-tree-caption">Plugin directory</div>
372 <div role="group" aria-labelledby="pe-tree-caption" onKeyDown={onTreeKeyDown}>
373 <div className="pe-rootline"><FolderIcon /><span>my-plugin/</span></div>
374 {PIECES.map(p => <button key={p.id} id={'pe-node-' + p.id} type="button" className="pe-node" aria-pressed={p.id === selected.id} aria-label={p.name + ', ' + p.path} onClick={() => setSelectedId(p.id)}>
375 {p.lines.map((line, i) => <span key={i} className="pe-line pe-line-tree" style={{
376 paddingLeft: line.depth * 18 + 'px'
377 }}>
378 {line.kind === 'folder' ? <FolderIcon /> : <FileIcon />}
379 <span>{line.text}</span>
380 {p.required && i === p.lines.length - 1 ? <span className="pe-req">{p.required}</span> : null}
381 </span>)}
382 <span className="pe-line pe-line-compact">
383 <FileIcon />
384 <span>{p.path}</span>
385 {p.required ? <span className="pe-req">{p.required}</span> : null}
386 </span>
387 </button>)}
388 </div>
389 </div>
390
391 <div className="pe-panel" role="region" aria-labelledby="pe-panel-caption" aria-live="polite" aria-atomic="true">
392 <div className="pe-caption" id="pe-panel-caption">Selected piece</div>
393 <div className="pe-name">{selected.name}{selected.required ? <span className="pe-req">{selected.required}</span> : null}</div>
394 <div className="pe-path">{selected.path}</div>
395
396 <div className="pe-block">{children}</div>
397
398 <a className="pe-link" href={selected.href}>{selected.linkText}</a>
399 </div>
400 </div>
401 </div>;
402};
403
404Claude Code 插件由多个组件构建而成,例如 skills、agents、hooks 和 MCP 服务器。每个组件在插件中都有一个默认文件夹,在 `.claude-plugin/plugin.json` 中有一个可选的清单键来替换或添加到该文件夹,以及用户看到的名称。有关每个键的完整字段表,请参阅[清单参考](/docs/zh-CN/plugins/manifest-reference#fields)。9Claude Code 插件由多个组件构建而成,例如 skills、agents、hooks 和 MCP 服务器。每个组件在插件中都有一个默认文件夹,在 `.claude-plugin/plugin.json` 中有一个可选的清单键来替换或添加到该文件夹,以及用户看到的名称。有关每个键的完整字段表,请参阅[清单参考](/docs/zh-CN/plugins/manifest-reference#fields)。
405 10
406使用此页面向已加载的插件添加组件。11使用此页面向已加载的插件添加组件。
407 12
408添加组件后,在运行中的会话中运行 `/reload-plugins` 或启动新会话,以便 Claude Code 加载它。要在加载前检查组件的文件,请从插件目录在 shell 中运行 [`claude plugin validate .`](/docs/zh-CN/plugins/cli-reference#plugin-validate)。13添加组件后,在运行中的会话中运行 `/reload-plugins` 或启动新会话,以便 Claude Code 加载它。要在加载前检查组件的文件,请从插件目录在 shell 中运行 [`claude plugin validate .`](/docs/zh-CN/plugins/cli-reference#plugin-validate)。
409 14
410<Note>15> [!NOTE]
411 这些情况在其他页面上有介绍:16>
412 17> 这些情况在其他页面上有介绍:
413 * **构建您的第一个插件**:从[创建插件](/docs/zh-CN/plugins/create)开始18>
414 * **安装他人的插件**:请参阅[安装插件](/docs/zh-CN/plugins/install)19> * **构建您的第一个插件**:从[创建插件](/docs/zh-CN/plugins/create)开始
415 * **您的插件用户在 claude.ai 或 Cowork 中**:那里加载的是不同的组件集。请参阅[插件结构和测试](https://claude.com/docs/plugins/build)和[组件支持表](https://claude.com/docs/plugins/platform-support#compare-component-support-by-app)20> * **安装他人的插件**:请参阅[安装插件](/docs/zh-CN/plugins/install)
416</Note>21> * **您的插件用户在 claude.ai 或 Cowork 中**:那里加载的是不同的组件集。请参阅[插件结构和测试](https://claude.com/docs/plugins/build)和[组件支持表](https://claude.com/docs/plugins/platform-support#compare-component-support-by-app)
417 22
418<h2 id="explore-the-plugin-directory">23## 浏览插件目录
419 浏览插件目录
420</h2>
421 24
422浏览器显示了一个示例插件 `my-plugin`,它在其默认位置拥有每种组件的一个副本:25浏览器显示了一个示例插件 `my-plugin`,它在其默认位置拥有每种组件的一个副本:
423 26
433 36
434每个文件都是其格式的最小有效示例,用于展示形状而不是实用性:真实的 skill 或 agent 包含完整的说明,通常还有支持文件,真实的 hook 或监视器执行真实的工作。浏览器后的部分使用与浏览器相同的文件作为示例,并链接到更完整的文件。选择一个文件或文件夹来阅读其用途、查看其内容,并找到涵盖它的部分。37每个文件都是其格式的最小有效示例,用于展示形状而不是实用性:真实的 skill 或 agent 包含完整的说明,通常还有支持文件,真实的 hook 或监视器执行真实的工作。浏览器后的部分使用与浏览器相同的文件作为示例,并链接到更完整的文件。选择一个文件或文件夹来阅读其用途、查看其内容,并找到涵盖它的部分。
435 38
436<PluginExplorer>39What goes in a plugin
437 <Piece id="manifest">
438 [清单](/docs/zh-CN/plugins/manifest-reference)是插件 `.claude-plugin/` 目录中的 `plugin.json` 文件。它包含插件的元数据和 Claude Code 提示用户的 `userConfig` 值。Claude Code 可以在没有清单的情况下加载插件。在文件中,只有 `name` 是必需的。在这个文件中,`description` 是用户在 `/plugin` 中看到的插件文本,`version` 使用户保持在该版本,直到您更改它:
439 40
440 ```json theme={null}41This example plugin, `my-plugin`, has one of every kind of component, each in its default location. Select a file or folder to read what it’s for and see what goes in it.
441 {42
43Plugin directory
44
45my-plugin/
46
47Selected piece
48
49[清单](/docs/zh-CN/plugins/manifest-reference)是插件 `.claude-plugin/` 目录中的 `plugin.json` 文件。它包含插件的元数据和 Claude Code 提示用户的 `userConfig` 值。Claude Code 可以在没有清单的情况下加载插件。在文件中,只有 `name` 是必需的。在这个文件中,`description` 是用户在 `/plugin` 中看到的插件文本,`version` 使用户保持在该版本,直到您更改它:
50
51```json theme={null}
52{
442 "name": "my-plugin",53 "name": "my-plugin",
443 "version": "1.0.0",54 "version": "1.0.0",
444 "description": "Review, formatting, and database tools for this team"55 "description": "Review, formatting, and database tools for this team"
445 }56}
446 ```57```
447 </Piece>
448 58
449 <Piece id="skills">59一个 [skill](/docs/zh-CN/skills) 是一个 `SKILL.md` 文件。将每个 skill 保存在 `skills/` 下的自己的目录中。Claude 读取每个 skill 的 `description`,当用户要求的内容与其匹配时,例如在这里要求 Claude 审查拉取请求,Claude 加载 skill 的说明并遵循它们。用户也可以直接将其作为 `/my-plugin:review` 运行:
450 一个 [skill](/docs/zh-CN/skills) 是一个 `SKILL.md` 文件。将每个 skill 保存在 `skills/` 下的自己的目录中。Claude 读取每个 skill 的 `description`,当用户要求的内容与其匹配时,例如在这里要求 Claude 审查拉取请求,Claude 加载 skill 的说明并遵循它们。用户也可以直接将其作为 `/my-plugin:review` 运行:
451 60
452 ```markdown theme={null}61```markdown theme={null}
453 ---62---
454 description: Reviews a pull request for style and test coverage. Use when asked to review code.63description: Reviews a pull request for style and test coverage. Use when asked to review code.
455 ---64---
456 65
457 Review the changed files. Report style problems first, then missing tests.66Review the changed files. Report style problems first, then missing tests.
458 ```67```
459 </Piece>
460 68
461 <Piece id="commands">69命令是用户按名称运行的单个 Markdown 文件。命令是较旧的格式:skill 以相同的方式按名称运行,也可以在其自己的目录中携带支持文件,因此将新的写成 skills,并为您已有的文件保留 `commands/`。此文件变成 `/my-plugin:about` 并采用与 skill 相同的 frontmatter:
462 命令是用户按名称运行的单个 Markdown 文件。命令是较旧的格式:skill 以相同的方式按名称运行,也可以在其自己的目录中携带支持文件,因此将新的写成 skills,并为您已有的文件保留 `commands/`。此文件变成 `/my-plugin:about` 并采用与 skill 相同的 frontmatter:
463 70
464 ```markdown theme={null}71```markdown theme={null}
465 ---72---
466 description: Summarize the repository73description: Summarize the repository
467 ---74---
468 75
469 Summarize what this repository does in three sentences.76Summarize what this repository does in three sentences.
470 ```77```
471 </Piece>
472 78
473 <Piece id="agents">79一个[子代理](/docs/zh-CN/sub-agents)是一个单独的助手,拥有自己的说明和自己的上下文窗口,Claude 可以将任务委托给它并获得结果。`agents/` 下的每个 Markdown 文件定义一个:frontmatter 命名它并说明何时使用它,正文是其系统提示。这个被命名为 `my-plugin:security-reviewer`,用户可以使用 `@agent-my-plugin:security-reviewer` 调用它:
474 一个[子代理](/docs/zh-CN/sub-agents)是一个单独的助手,拥有自己的说明和自己的上下文窗口,Claude 可以将任务委托给它并获得结果。`agents/` 下的每个 Markdown 文件定义一个:frontmatter 命名它并说明何时使用它,正文是其系统提示。这个被命名为 `my-plugin:security-reviewer`,用户可以使用 `@agent-my-plugin:security-reviewer` 调用它:
475 80
476 ```markdown theme={null}81```markdown theme={null}
477 ---82---
478 name: security-reviewer83name: security-reviewer
479 description: Reviews code changes for security issues. Use after edits to authentication or input handling.84description: Reviews code changes for security issues. Use after edits to authentication or input handling.
480 model: sonnet85model: sonnet
481 ---86---
482 87
483 You are a security reviewer. Read the changed files and report injection, authentication, and secrets-handling risks.88You are a security reviewer. Read the changed files and report injection, authentication, and secrets-handling risks.
484 ```89```
485 </Piece>
486 90
487 <Piece id="hooks">91一个 [hook](/docs/zh-CN/hooks-guide) 在 Claude Code 生命周期中的某个点自动运行某些内容,例如在每次文件编辑后:shell 命令、HTTP 请求、MCP 工具调用、对模型的提示或子代理。将插件的 hooks 保存在插件根目录的 `hooks/hooks.json` 中。这个在 Claude 写入或编辑文件后运行插件的 `scripts/format.sh`:
488 一个 [hook](/docs/zh-CN/hooks-guide) 在 Claude Code 生命周期中的某个点自动运行某些内容,例如在每次文件编辑后:shell 命令、HTTP 请求、MCP 工具调用、对模型的提示或子代理。将插件的 hooks 保存在插件根目录的 `hooks/hooks.json` 中。这个在 Claude 写入或编辑文件后运行插件的 `scripts/format.sh`:
489 92
490 ```json theme={null}93```json theme={null}
491 {94{
492 "hooks": {95 "hooks": {
493 "PostToolUse": [96 "PostToolUse": [
494 {97 {
502 }105 }
503 ]106 ]
504 }107 }
505 }108}
506 ```109```
507 </Piece>
508 110
509 <Piece id="monitors">111监视器是一个 shell 命令,Claude Code 在会话启动时在后台启动它,并保持运行直到会话结束。它打印的内容作为通知到达 Claude。`when` 字段可以改为在命名 skill 首次运行时启动它。这个跟踪错误日志:
510 监视器是一个 shell 命令,Claude Code 在会话启动时在后台启动它,并保持运行直到会话结束。它打印的内容作为通知到达 Claude。`when` 字段可以改为在命名 skill 首次运行时启动它。这个跟踪错误日志:
511 112
512 ```json theme={null}113```json theme={null}
513 [114[
514 {115 {
515 "name": "error-log",116 "name": "error-log",
516 "command": "tail -F ./logs/error.log",117 "command": "tail -F ./logs/error.log",
517 "description": "Application error log"118 "description": "Application error log"
518 }119 }
519 ]120]
520 ```121```
521 </Piece>
522 122
523 <Piece id="output-styles">123插件可以包含[输出样式](/docs/zh-CN/output-styles),这改变了 Claude 如何格式化和表述其回复。将每个输出样式保存为 `output-styles/<name>.md`。这个在 `/output-style` 中显示为 `my-plugin:terse`:
524 插件可以包含[输出样式](/docs/zh-CN/output-styles),这改变了 Claude 如何格式化和表述其回复。将每个输出样式保存为 `output-styles/<name>.md`。这个在 `/output-style` 中显示为 `my-plugin:terse`:
525 124
526 ```markdown theme={null}125```markdown theme={null}
527 ---126---
528 name: terse127name: terse
529 description: Answer in as few words as possible128description: Answer in as few words as possible
530 keep-coding-instructions: true129keep-coding-instructions: true
531 ---130---
532 131
533 Keep every reply short. Skip preambles and summaries.132Keep every reply short. Skip preambles and summaries.
534 ```133```
535 </Piece>
536 134
537 <Piece id="themes">135插件可以包含 [Claude Code 界面的颜色主题](/docs/zh-CN/terminal-config#create-a-custom-theme)。将每个主题保存为 `themes/<slug>.json`。这个在 `/theme` 中显示为 `Dracula`,标记为来自 `my-plugin`:
538 插件可以包含 [Claude Code 界面的颜色主题](/docs/zh-CN/terminal-config#create-a-custom-theme)。将每个主题保存为 `themes/<slug>.json`。这个在 `/theme` 中显示为 `Dracula`,标记为来自 `my-plugin`:
539 136
540 ```json theme={null}137```json theme={null}
541 {138{
542 "name": "Dracula",139 "name": "Dracula",
543 "base": "dark",140 "base": "dark",
544 "overrides": {141 "overrides": {
545 "claude": "#bd93f9",142 "claude": "#bd93f9",
546 "error": "#ff5555"143 "error": "#ff5555"
547 }144 }
548 }145}
549 ```146```
550 </Piece>
551 147
552 <Piece id="workflows">148`workflows/` 文件夹包含 [workflow](/docs/zh-CN/workflows) `.js` 文件:一个 `meta` 块,然后是协调多个子代理的脚本正文。这个作为 `/my-plugin:audit-routes` 运行:
553 `workflows/` 文件夹包含 [workflow](/docs/zh-CN/workflows) `.js` 文件:一个 `meta` 块,然后是协调多个子代理的脚本正文。这个作为 `/my-plugin:audit-routes` 运行:
554 149
555 ```javascript theme={null}150```javascript theme={null}
556 export const meta = {151export const meta = {
557 name: 'audit-routes',152 name: 'audit-routes',
558 description: 'Audit every route handler for missing auth checks',153 description: 'Audit every route handler for missing auth checks',
559 }154}
560 155
561 const found = await agent('List every .ts file under src/routes/.', {156const found = await agent('List every .ts file under src/routes/.', {
562 schema: { type: 'object', required: ['files'], properties: { files: { type: 'array', items: { type: 'string' } } } },157 schema: { type: 'object', required: ['files'], properties: { files: { type: 'array', items: { type: 'string' } } } },
563 })158})
564 159
565 const audits = await pipeline(found.files, file =>160const audits = await pipeline(found.files, file =>
566 agent(`Audit ${file} for missing authentication checks.`, { label: file }),161 agent(`Audit ${file} for missing authentication checks.`, { label: file }),
567 )162)
568 163
569 return audits.filter(Boolean)164return audits.filter(Boolean)
570 ```165```
571 </Piece>
572 166
573 <Piece id="bin">167`bin/` 是插件如何提供命令行工具的方式。启用插件时,Claude Code 将此文件夹放在它运行命令的 shell 的 `PATH` 上,因此 Claude 或 skill 的说明可以按名称运行该工具,而无需用户安装任何东西。有了这个[可执行文件](#executables),`hello-plugin` 是 Claude 可以运行的命令:
574 `bin/` 是插件如何提供命令行工具的方式。启用插件时,Claude Code 将此文件夹放在它运行命令的 shell 的 `PATH` 上,因此 Claude 或 skill 的说明可以按名称运行该工具,而无需用户安装任何东西。有了这个[可执行文件](#executables),`hello-plugin` 是 Claude 可以运行的命令:
575 168
576 ```bash theme={null}169```bash theme={null}
577 #!/bin/bash170#!/bin/bash
578 echo "hello from my-plugin"171echo "hello from my-plugin"
579 ```172```
580 </Piece>
581 173
582 <Piece id="scripts">174`hooks/hooks.json` 中的 hook 运行一个脚本,这个文件夹是示例保留它的地方。名称 `scripts/` 是一个约定,不是 Claude Code 查找的东西:hook 通过其路径指向文件,`${CLAUDE_PLUGIN_ROOT}/scripts/format.sh`。格式化脚本可能看起来像这样:
583 `hooks/hooks.json` 中的 hook 运行一个脚本,这个文件夹是示例保留它的地方。名称 `scripts/` 是一个约定,不是 Claude Code 查找的东西:hook 通过其路径指向文件,`${CLAUDE_PLUGIN_ROOT}/scripts/format.sh`。格式化脚本可能看起来像这样:
584 175
585 ```bash theme={null}176```bash theme={null}
586 #!/bin/bash177#!/bin/bash
587 npx prettier --write .178npx prettier --write .
588 ```179```
589 </Piece>
590 180
591 <Piece id="settings">181插件根目录中的 `settings.json` 包含在启用插件时应用的[设置](/docs/zh-CN/settings-reference),因此插件可以改变会话的行为方式,而不仅仅是添加组件。只有两个键从插件生效,[`agent`](/docs/zh-CN/settings-reference#agent) 和 [`subagentStatusLine`](/docs/zh-CN/settings-reference#subagentstatusline);所有其他键都被丢弃。请参阅[默认设置](#default-settings)。
592 插件根目录中的 `settings.json` 包含在启用插件时应用的[设置](/docs/zh-CN/settings-reference),因此插件可以改变会话的行为方式,而不仅仅是添加组件。只有两个键从插件生效,[`agent`](/docs/zh-CN/settings-reference#agent) 和 [`subagentStatusLine`](/docs/zh-CN/settings-reference#subagentstatusline);所有其他键都被丢弃。请参阅[默认设置](#default-settings)。
593 182
594 这个设置 `agent`,它将会话的主线程作为插件自己的 `security-reviewer` agent 运行,因此该 agent 的系统提示、工具限制和模型应用于整个会话:183这个设置 `agent`,它将会话的主线程作为插件自己的 `security-reviewer` agent 运行,因此该 agent 的系统提示、工具限制和模型应用于整个会话:
595 184
596 ```json theme={null}185```json theme={null}
597 {186{
598 "agent": "security-reviewer"187 "agent": "security-reviewer"
599 }188}
600 ```189```
601 </Piece>
602 190
603 <Piece id="mcp">191一个 [MCP 服务器](/docs/zh-CN/mcp)从外部系统为 Claude 提供工具。在插件根目录的 `.mcp.json` 中声明它。这个从插件内的脚本启动本地服务器,并在 `/mcp` 中显示为 `plugin:my-plugin:db`:
604 一个 [MCP 服务器](/docs/zh-CN/mcp)从外部系统为 Claude 提供工具。在插件根目录的 `.mcp.json` 中声明它。这个从插件内的脚本启动本地服务器,并在 `/mcp` 中显示为 `plugin:my-plugin:db`:
605 192
606 ```json theme={null}193```json theme={null}
607 {194{
608 "mcpServers": {195 "mcpServers": {
609 "db": {196 "db": {
610 "command": "node",197 "command": "node",
611 "args": ["${CLAUDE_PLUGIN_ROOT}/server.js"]198 "args": ["${CLAUDE_PLUGIN_ROOT}/server.js"]
612 }199 }
613 }200 }
614 }201}
615 ```202```
616 </Piece>
617 203
618 <Piece id="lsp">204LSP 服务器为 Claude 提供[诊断和代码导航](/docs/zh-CN/plugins/code-intelligence)。在插件根目录的 `.lsp.json` 中声明服务器。这个为 `.go` 文件连接 Go 语言服务器:
619 LSP 服务器为 Claude 提供[诊断和代码导航](/docs/zh-CN/plugins/code-intelligence)。在插件根目录的 `.lsp.json` 中声明服务器。这个为 `.go` 文件连接 Go 语言服务器:
620 205
621 ```json theme={null}206```json theme={null}
622 {207{
623 "gopls": {208 "gopls": {
624 "command": "gopls",209 "command": "gopls",
625 "args": ["serve"],210 "args": ["serve"],
627 ".go": "go"212 ".go": "go"
628 }213 }
629 }214 }
630 }215}
631 ```216```
632 </Piece>
633</PluginExplorer>
634 217
635<h2 id="add-each-kind-of-component">218## 添加每种组件
636 添加每种组件
637</h2>
638 219
639下面的每个部分涵盖一种组件:其文件在插件中的位置、一个验证示例、插件加载后用户看到的内容,以及更改默认位置的清单键。添加你的插件需要的组件;没有任何组件是必需的。220下面的每个部分涵盖一种组件:其文件在插件中的位置、一个验证示例、插件加载后用户看到的内容,以及更改默认位置的清单键。添加你的插件需要的组件;没有任何组件是必需的。
640 221
641<h3 id="skills">222### Skills
642 Skills
643</h3>
644 223
645一个 [skill](/docs/zh-CN/skills) 是一个 `SKILL.md` 文件,当其描述与任务匹配时,Claude 可以加载它。用户也可以将其作为命令运行。将每个 skill 保存在 `skills/` 下的自己的目录中:224一个 [skill](/docs/zh-CN/skills) 是一个 `SKILL.md` 文件,当其描述与任务匹配时,Claude 可以加载它。用户也可以将其作为命令运行。将每个 skill 保存在 `skills/` 下的自己的目录中:
646 225
679 258
680对于 frontmatter 字段和支持文件,参见 [Skills](/docs/zh-CN/skills)。259对于 frontmatter 字段和支持文件,参见 [Skills](/docs/zh-CN/skills)。
681 260
682<h3 id="commands">261### Commands
683 Commands
684</h3>
685 262
686命令是用户按名称运行的单个 Markdown 文件,例如 `/my-plugin:about`。263命令是用户按名称运行的单个 Markdown 文件,例如 `/my-plugin:about`。
687 264
688<Note>265> [!NOTE]
689 Commands 是较旧的格式,[skills](#skills) 对新工作已经取代了它们。skill 以相同的方式按名称运行,它也可以在其目录中携带支持文件。对于你从 `.claude/commands/` 迁移过来的文件,保留 `commands/`。266>
690</Note>267> Commands 是较旧的格式,[skills](#skills) 对新工作已经取代了它们。skill 以相同的方式按名称运行,它也可以在其目录中携带支持文件。对于你从 `.claude/commands/` 迁移过来的文件,保留 `commands/`。
691 268
692在 `commands/<file>.md` 保存命令,它变成 `/<plugin>:<file>`。子目录添加一个段,所以 `commands/db/migrate.md` 是 `/my-plugin:db:migrate`。269在 `commands/<file>.md` 保存命令,它变成 `/<plugin>:<file>`。子目录添加一个段,所以 `commands/db/migrate.md` 是 `/my-plugin:db:migrate`。
693 270
694命令文件采用与 skills 相同的 frontmatter。271命令文件采用与 skills 相同的 frontmatter。
695 272
696<h4 id="define-commands-in-the-manifest">273#### 在清单中定义命令
697 在清单中定义命令
698</h4>
699 274
700只有当你想将命令文件保存在 `commands/` 之外的地方,或在 `plugin.json` 中定义一个短命令而不需要单独的 Markdown 文件时,你才需要这样做。设置 `commands` 清单键,Claude Code 会读取它而不是扫描 `commands/`。该键接受一个路径、路径数组或一个对象,该对象将每个命令名称映射到 `source` 文件或内联 `content`。275只有当你想将命令文件保存在 `commands/` 之外的地方,或在 `plugin.json` 中定义一个短命令而不需要单独的 Markdown 文件时,你才需要这样做。设置 `commands` 清单键,Claude Code 会读取它而不是扫描 `commands/`。该键接受一个路径、路径数组或一个对象,该对象将每个命令名称映射到 `source` 文件或内联 `content`。
701 276
717 292
718对于完整的键语法,参见 [`commands`](/docs/zh-CN/plugins/manifest-reference#commands)。293对于完整的键语法,参见 [`commands`](/docs/zh-CN/plugins/manifest-reference#commands)。
719 294
720<h3 id="agents">295### Agents
721 Agents
722</h3>
723 296
724一个 [subagent](/docs/zh-CN/sub-agents) 是一个单独的助手,有自己的说明和上下文窗口,Claude 可以将任务委托给它。`agents/` 下的每个 Markdown 文件定义一个:297一个 [subagent](/docs/zh-CN/sub-agents) 是一个单独的助手,有自己的说明和上下文窗口,Claude 可以将任务委托给它。`agents/` 下的每个 Markdown 文件定义一个:
725 298
737 310
738`agents` 清单键替换 `agents/` 扫描。311`agents` 清单键替换 `agents/` 扫描。
739 312
740<h4 id="organize-agents-in-subfolders">313#### 在子文件夹中组织代理
741 在子文件夹中组织代理
742</h4>
743 314
744你可以将插件代理文件放在 `agents/` 的子文件夹中。Claude Code [递归加载它们](/docs/zh-CN/sub-agents#choose-the-subagent-scope),并用冒号连接插件名称、每个子文件夹名称和文件名,以形成代理的作用域名称。例如,`my-plugin` 插件中的 `agents/review/security.md` 加载为 `my-plugin:review:security`。两个设置改变该名称:315你可以将插件代理文件放在 `agents/` 的子文件夹中。Claude Code [递归加载它们](/docs/zh-CN/sub-agents#choose-the-subagent-scope),并用冒号连接插件名称、每个子文件夹名称和文件名,以形成代理的作用域名称。例如,`my-plugin` 插件中的 `agents/review/security.md` 加载为 `my-plugin:review:security`。两个设置改变该名称:
745 316
746* Frontmatter `name`:它仅替换文件名,所以 `agents/review/security.md` 中的 `name: audit` 加载为 `my-plugin:review:audit`317* Frontmatter `name`:它仅替换文件名,所以 `agents/review/security.md` 中的 `name: audit` 加载为 `my-plugin:review:audit`
747* 清单 [`agents`](/docs/zh-CN/plugins/manifest-reference#fields) 字段:你在那里列出的文件加载时不带子文件夹名称,所以 `"agents": "./custom/review/security.md"` 加载为 `my-plugin:security`318* 清单 [`agents`](/docs/zh-CN/plugins/manifest-reference#fields) 字段:你在那里列出的文件加载时不带子文件夹名称,所以 `"agents": "./custom/review/security.md"` 加载为 `my-plugin:security`
748 319
749<h4 id="frontmatter-fields-in-plugin-agents">320#### 插件代理中的 Frontmatter 字段
750 插件代理中的 Frontmatter 字段
751</h4>
752 321
753插件代理的 frontmatter 遵循以下规则:322插件代理的 frontmatter 遵循以下规则:
754 323
758 327
759对于每个字段的作用和优先级规则,参见 [Subagents](/docs/zh-CN/sub-agents#supported-frontmatter-fields)。328对于每个字段的作用和优先级规则,参见 [Subagents](/docs/zh-CN/sub-agents#supported-frontmatter-fields)。
760 329
761<h3 id="hooks">330### Hooks
762 Hooks
763</h3>
764 331
765一个 [hook](/docs/zh-CN/hooks-guide) 在 Claude Code 生命周期中的某个点自动运行某些东西,例如在每次文件编辑后:shell 命令、HTTP 请求、MCP 工具调用、对模型的提示或 subagent。在插件根目录的 `hooks/hooks.json` 中保存插件的 hooks,在顶级 `"hooks"` 键下,形状与 `settings.json` 中的 `hooks` 对象相同。这让你可以复制现有的设置 hook 而不做任何改变。332一个 [hook](/docs/zh-CN/hooks-guide) 在 Claude Code 生命周期中的某个点自动运行某些东西,例如在每次文件编辑后:shell 命令、HTTP 请求、MCP 工具调用、对模型的提示或 subagent。在插件根目录的 `hooks/hooks.json` 中保存插件的 hooks,在顶级 `"hooks"` 键下,形状与 `settings.json` 中的 `hooks` 对象相同。这让你可以复制现有的设置 hook 而不做任何改变。
766 333
794 361
795要将 hooks 写成在 Claude Code 内运行并可以在其界面中绘制的 JavaScript 函数,在同一 `hooks/hooks.json` 中的 `modules` 键下列出一个模块文件。具有一个的插件是 mod。参见 [创建 mod](/docs/zh-CN/plugins/mods/create)。362要将 hooks 写成在 Claude Code 内运行并可以在其界面中绘制的 JavaScript 函数,在同一 `hooks/hooks.json` 中的 `modules` 键下列出一个模块文件。具有一个的插件是 mod。参见 [创建 mod](/docs/zh-CN/plugins/mods/create)。
796 363
797<h4 id="when-plugin-hooks-fire">364#### 插件 hooks 何时触发
798 插件 hooks 何时触发
799</h4>
800 365
801插件的 hooks 不会等待使用插件的某个 skill 或命令。Claude Code 在会话加载插件时注册它们,从那时起它们在其事件上触发。要限制 hook 何时运行,缩小其 `matcher`。366插件的 hooks 不会等待使用插件的某个 skill 或命令。Claude Code 在会话加载插件时注册它们,从那时起它们在其事件上触发。要限制 hook 何时运行,缩小其 `matcher`。
802 367
803如果 hook 从不触发,参见 [不触发的 hooks](/docs/zh-CN/plugins/troubleshooting#failed-to-load-hooks-from-and-hooks-that-dont-fire)。368如果 hook 从不触发,参见 [不触发的 hooks](/docs/zh-CN/plugins/troubleshooting#failed-to-load-hooks-from-and-hooks-that-dont-fire)。
804 369
805<h4 id="environment-quoting-and-matching-mcp-tools">370#### 环境、引用和匹配 MCP 工具
806 环境、引用和匹配 MCP 工具
807</h4>
808 371
809hook 的环境、`${CLAUDE_PLUGIN_ROOT}` 的引用和插件自己的 MCP 工具的匹配器工作如下:372hook 的环境、`${CLAUDE_PLUGIN_ROOT}` 的引用和插件自己的 MCP 工具的匹配器工作如下:
810 373
812* **引用**:当 `command` 没有 `args` 时,它通过 shell 运行,所以用双引号包装 `${CLAUDE_PLUGIN_ROOT}` 路径,如 [Hooks](#hooks) 下的 `hooks/hooks.json` 示例所做的那样,以保持展开的路径为一个 shell 单词。当你改为传递 `args` 时,每个元素作为一个参数传递,没有 shell,不需要引用。参见 [exec 形式和 shell 形式](/docs/zh-CN/hooks#exec-form-and-shell-form)375* **引用**:当 `command` 没有 `args` 时,它通过 shell 运行,所以用双引号包装 `${CLAUDE_PLUGIN_ROOT}` 路径,如 [Hooks](#hooks) 下的 `hooks/hooks.json` 示例所做的那样,以保持展开的路径为一个 shell 单词。当你改为传递 `args` 时,每个元素作为一个参数传递,没有 shell,不需要引用。参见 [exec 形式和 shell 形式](/docs/zh-CN/hooks#exec-form-and-shell-form)
813* **匹配插件自己的 MCP 工具**:来自此插件声明的 [MCP 服务器](#mcp-servers) 的工具名为 `mcp__plugin_<plugin>_<server>__<tool>`,所以在匹配器中写入该完整名称。仅在服务器名称上的匹配器从不触发。参见 [匹配 MCP 工具](/docs/zh-CN/hooks#match-mcp-tools)376* **匹配插件自己的 MCP 工具**:来自此插件声明的 [MCP 服务器](#mcp-servers) 的工具名为 `mcp__plugin_<plugin>_<server>__<tool>`,所以在匹配器中写入该完整名称。仅在服务器名称上的匹配器从不触发。参见 [匹配 MCP 工具](/docs/zh-CN/hooks#match-mcp-tools)
814 377
815<h3 id="mcp-servers">378### MCP servers
816 MCP servers
817</h3>
818 379
819MCP 服务器从外部系统为 Claude 提供工具。在插件根目录的 `.mcp.json` 中声明它,形状与 [项目 `.mcp.json`](/docs/zh-CN/mcp#project-scope) 相同。此 `.mcp.json` 声明一个名为 `db` 的服务器:380MCP 服务器从外部系统为 Claude 提供工具。在插件根目录的 `.mcp.json` 中声明它,形状与 [项目 `.mcp.json`](/docs/zh-CN/mcp#project-scope) 相同。此 `.mcp.json` 声明一个名为 `db` 的服务器:
820 381
839 400
840`mcpServers` 清单键接受内联服务器映射、JSON 文件的路径或这些的数组。当清单服务器与 `.mcp.json` 中的服务器同名时,清单服务器替换它。401`mcpServers` 清单键接受内联服务器映射、JSON 文件的路径或这些的数组。当清单服务器与 `.mcp.json` 中的服务器同名时,清单服务器替换它。
841 402
842<h4 id="reach-users-on-claude-ai-and-cowork">403#### 到达 claude.ai 和 Cowork 上的用户
843 到达 claude.ai 和 Cowork 上的用户
844</h4>
845 404
846本地 stdio 服务器,例如 [MCP 服务器](#mcp-servers) 下的 `db` 服务器,在 Claude Code 和在 Claude Desktop 应用中在你的机器上运行的 Cowork 会话中运行,但不在 claude.ai 上。要到达那里的用户,通过其 `https://` URL 引用远程服务器,claude.ai 和 Cowork 将其作为连接器提供给用户,如 [将 MCP 连接器与其 skill 捆绑](https://claude.com/docs/plugins/build#bundle-an-mcp-connector-with-its-skill) 所示。405本地 stdio 服务器,例如 [MCP 服务器](#mcp-servers) 下的 `db` 服务器,在 Claude Code 和在 Claude Desktop 应用中在你的机器上运行的 Cowork 会话中运行,但不在 claude.ai 上。要到达那里的用户,通过其 `https://` URL 引用远程服务器,claude.ai 和 Cowork 将其作为连接器提供给用户,如 [将 MCP 连接器与其 skill 捆绑](https://claude.com/docs/plugins/build#bundle-an-mcp-connector-with-its-skill) 所示。
847 406
848<h4 id="server-names-tool-names-and-reloads">407#### 服务器名称、工具名称和重新加载
849 服务器名称、工具名称和重新加载
850</h4>
851 408
852服务器的名称、变量替换和重新加载行为遵循以下规则:409服务器的名称、变量替换和重新加载行为遵循以下规则:
853 410
856* **替换**:`${CLAUDE_PLUGIN_ROOT}` 和其他 [路径变量](#path-variables-and-persistent-data) 在 `command`、`args` 和 `env` 中被替换。`args` 中不需要引用,因为每个元素作为一个参数传递413* **替换**:`${CLAUDE_PLUGIN_ROOT}` 和其他 [路径变量](#path-variables-and-persistent-data) 在 `command`、`args` 和 `env` 中被替换。`args` 中不需要引用,因为每个元素作为一个参数传递
857* **重新加载**:当用户运行 `/reload-plugins` 且 [重新加载适用](/docs/zh-CN/plugins/cli-reference#reloads-that-change-mcp-tools) 时,配置未更改的服务器保持其连接。配置已更改的服务器重新连接,你删除的服务器断开连接414* **重新加载**:当用户运行 `/reload-plugins` 且 [重新加载适用](/docs/zh-CN/plugins/cli-reference#reloads-that-change-mcp-tools) 时,配置未更改的服务器保持其连接。配置已更改的服务器重新连接,你删除的服务器断开连接
858 415
859<h4 id="include-a-packaged-mcpb-server">416#### 包含打包的 MCPB 服务器
860 包含打包的 MCPB 服务器
861</h4>
862 417
863`mcpServers` 键也接受打包的服务器作为 [MCPB 文件](https://github.com/modelcontextprotocol/mcpb),其扩展名为 `.mcpb` 或较旧的 `.dxt`。将键指向文件,作为插件内的路径或 `https://` URL:418`mcpServers` 键也接受打包的服务器作为 [MCPB 文件](https://github.com/modelcontextprotocol/mcpb),其扩展名为 `.mcpb` 或较旧的 `.dxt`。将键指向文件,作为插件内的路径或 `https://` URL:
864 419
880 435
881对于传输和身份验证,参见 [MCP](/docs/zh-CN/mcp#plugin-provided-mcp-servers)。436对于传输和身份验证,参见 [MCP](/docs/zh-CN/mcp#plugin-provided-mcp-servers)。
882 437
883<h3 id="lsp-servers">438### LSP servers
884 LSP servers
885</h3>
886 439
887LSP 服务器为 Claude 提供诊断和代码导航。如果 [官方代码智能插件](/docs/zh-CN/plugins/code-intelligence) 已经涵盖你的语言,安装那个而不是写一个。否则在插件根目录的 `.lsp.json` 中声明服务器:440LSP 服务器为 Claude 提供诊断和代码导航。如果 [官方代码智能插件](/docs/zh-CN/plugins/code-intelligence) 已经涵盖你的语言,安装那个而不是写一个。否则在插件根目录的 `.lsp.json` 中声明服务器:
888 441
915 468
916Claude Code 断开超过任一限制或向 stdout 写入非协议输出的服务器,并将断开连接计为 `restartOnCrash` 和 `maxRestarts` 的崩溃。当你使用 `--debug` 运行时,Claude Code 将命名原因的错误写入调试日志。469Claude Code 断开超过任一限制或向 stdout 写入非协议输出的服务器,并将断开连接计为 `restartOnCrash` 和 `maxRestarts` 的崩溃。当你使用 `--debug` 运行时,Claude Code 将命名原因的错误写入调试日志。
917 470
918<h3 id="executables">471### Executables
919 Executables
920</h3>
921 472
922插件根目录中 `bin/` 中的文件在启用插件时位于 Bash 工具的 shell 的 `PATH` 上,所以 Claude 可以将它们作为裸命令运行。添加一个可执行脚本:473插件根目录中 `bin/` 中的文件在启用插件时位于 Bash 工具的 shell 的 `PATH` 上,所以 Claude 可以将它们作为裸命令运行。Claude 在 [沙箱](/docs/zh-CN/sandboxing) 之外使用 [PowerShell 工具](/docs/zh-CN/tools-reference#powershell-tool) 运行的命令也能在其 `PATH` 上找到它们,而在沙箱中运行的 PowerShell 命令则不能。添加一个可执行脚本:
923 474
924```bash bin/hello-plugin theme={null}475```bash bin/hello-plugin theme={null}
925#!/bin/bash476#!/bin/bash
932 483
933claude.ai 和 Cowork 不安装具有顶级 `bin/` 目录的插件,包括你 [通过 claude.ai 组织设置分发的](https://claude.com/docs/plugins/org-sync#keep-executables-out-of-the-top-level-bin-directory) 插件。484claude.ai 和 Cowork 不安装具有顶级 `bin/` 目录的插件,包括你 [通过 claude.ai 组织设置分发的](https://claude.com/docs/plugins/org-sync#keep-executables-out-of-the-top-level-bin-directory) 插件。
934 485
935<h3 id="default-settings">486### Default settings
936 Default settings
937</h3>
938 487
939要设置在启用插件时应用的默认值,在插件根目录添加 `settings.json`,或将相同的对象内联放在 `settings` 清单键中。两个键生效,`agent` 和 `subagentStatusLine`,每个其他键都被丢弃。488要设置在启用插件时应用的默认值,在插件根目录添加 `settings.json`,或将相同的对象内联放在 `settings` 清单键中。两个键生效,`agent` 和 `subagentStatusLine`,每个其他键都被丢弃。
940 489
958 507
959对于 `subagentStatusLine` 形状,参见 [subagent 状态行](/docs/zh-CN/statusline#subagent-status-lines)。508对于 `subagentStatusLine` 形状,参见 [subagent 状态行](/docs/zh-CN/statusline#subagent-status-lines)。
960 509
961<h3 id="themes-and-output-styles">510### Themes and output styles
962 Themes and output styles
963</h3>
964 511
965插件可以包含颜色主题和输出样式。两者都出现在与用户自己相同的选择器中。对于任一个,设置清单键替换文件夹扫描。512插件可以包含颜色主题和输出样式。两者都出现在与用户自己相同的选择器中。对于任一个,设置清单键替换文件夹扫描。
966 513
984}531}
985```532```
986 533
987<h3 id="channels">534### Channels
988 Channels
989</h3>
990 535
991一个 [channel](/docs/zh-CN/channels) 让外部系统(如聊天应用)将消息发送到会话中。在插件中,channel 是 MCP 服务器之一加上一个 `channels` 条目,该条目绑定到它并可以提示其自己的配置。此清单将 channel 绑定到 `telegram` 服务器并要求机器人令牌:536一个 [channel](/docs/zh-CN/channels) 让外部系统(如聊天应用)将消息发送到会话中。在插件中,channel 是 MCP 服务器之一加上一个 `channels` 条目,该条目绑定到它并可以提示其自己的配置。此清单将 channel 绑定到 `telegram` 服务器并要求机器人令牌:
992 537
1020 565
1021对于服务器必须实现的内容以及用户如何启用 channel 插件,参见 channels 参考中的 [打包为插件](/docs/zh-CN/channels-reference#package-as-a-plugin)。对于字段表,参见 [`channels`](/docs/zh-CN/plugins/manifest-reference#channels)。566对于服务器必须实现的内容以及用户如何启用 channel 插件,参见 channels 参考中的 [打包为插件](/docs/zh-CN/channels-reference#package-as-a-plugin)。对于字段表,参见 [`channels`](/docs/zh-CN/plugins/manifest-reference#channels)。
1022 567
1023<h3 id="monitors">568### Monitors
1024 Monitors
1025</h3>
1026 569
1027monitor 是在整个会话中在后台运行的 shell 命令。它打印的内容作为通知到达 Claude,所以 Claude 可以对日志或状态更改做出反应,而无需被要求观看它。在 `monitors/monitors.json` 中保存条目:570monitor 是在整个会话中在后台运行的 shell 命令。它打印的内容作为通知到达 Claude,所以 Claude 可以对日志或状态更改做出反应,而无需被要求观看它。在 `monitors/monitors.json` 中保存条目:
1028 571
1048 591
1049对于 `when` 触发器和其他字段,参见 [`monitors`](/docs/zh-CN/plugins/manifest-reference#monitors)。592对于 `when` 触发器和其他字段,参见 [`monitors`](/docs/zh-CN/plugins/manifest-reference#monitors)。
1050 593
1051<h2 id="user-configuration">594## 要求用户提供配置值
1052 要求用户提供配置值
1053</h2>
1054 595
1055在 `userConfig` 清单键中声明您的插件需要的值,以便用户不自己编辑 `settings.json`。每个选项显示在一个对话框中,其 `title` 作为标签,其 `description` 在下方。596在 `userConfig` 清单键中声明您的插件需要的值,以便用户不自己编辑 `settings.json`。每个选项显示在一个对话框中,其 `title` 作为标签,其 `description` 在下方。
1056 597
1077}618}
1078```619```
1079 620
1080<h3 id="when-the-configuration-dialog-appears">621### 配置对话框何时出现
1081 配置对话框何时出现
1082</h3>
1083 622
1084对话框是交互式 `/plugin` 界面的一部分。当用户执行以下任何操作时,它为任何尚未设置的选项打开:623对话框是交互式 `/plugin` 界面的一部分。当用户执行以下任何操作时,它为任何尚未设置的选项打开:
1085 624
1097 636
1098对于选项字段、每个值存储的位置、组件如何引用保存的值以及哪些字段拒绝 `${user_config.*}`,请参阅[用户配置](/docs/zh-CN/plugins/manifest-reference#user-configuration)。637对于选项字段、每个值存储的位置、组件如何引用保存的值以及哪些字段拒绝 `${user_config.*}`,请参阅[用户配置](/docs/zh-CN/plugins/manifest-reference#user-configuration)。
1099 638
1100<h2 id="path-variables-and-persistent-data">639## 引用插件路径和存储数据
1101 引用插件路径和存储数据
1102</h2>
1103 640
1104您不知道您的插件将被安装在哪里,所以通过这些变量而不是固定路径引用其文件和数据。它们在 skill、命令和 agent 内容、hook 和监视器命令以及 MCP 和 LSP 服务器配置中被替换。它们也被导出到 hook、MCP 和 LSP 进程:641您不知道您的插件将被安装在哪里,所以通过这些变量而不是固定路径引用其文件和数据。它们在 skill、命令和 agent 内容、hook 和监视器命令以及 MCP 和 LSP 服务器配置中被替换。它们也被导出到 hook、MCP 和 LSP 进程:
1105 642
1111 648
1112在 Windows 上,替换的路径使用正斜杠,所以 shell 不将反斜杠读取为转义。649在 Windows 上,替换的路径使用正斜杠,所以 shell 不将反斜杠读取为转义。
1113 650
1114<h3 id="install-dependencies-into-the-data-directory">651### 将依赖项安装到数据目录
1115 将依赖项安装到数据目录
1116</h3>
1117 652
1118对于市场安装的插件,Claude Code 在缓存插件时自动安装符合条件的 [Node.js 包依赖项](/docs/zh-CN/plugins/loading#node-js-package-dependencies),所以您可能不需要自己安装它们。当您这样做时,此 `SessionStart` hook 在首次运行时将 `node_modules` 安装到 `${CLAUDE_PLUGIN_DATA}` 中,并在更新改变 `package.json` 后再次安装:653对于市场安装的插件,Claude Code 在缓存插件时自动安装符合条件的 [Node.js 包依赖项](/docs/zh-CN/plugins/loading#node-js-package-dependencies),所以您可能不需要自己安装它们。当您这样做时,此 `SessionStart` hook 在首次运行时将 `node_modules` 安装到 `${CLAUDE_PLUGIN_DATA}` 中,并在更新改变 `package.json` 后再次安装:
1119 654
1136 671
1137在第一个会话后,`~/.claude/plugins/data/<id>/node_modules` 存在。MCP 服务器然后可以在其 `env` 中设置 `NODE_PATH` 为 `${CLAUDE_PLUGIN_DATA}/node_modules`。对于哪些字段替换哪个变量,请参阅[环境变量](/docs/zh-CN/plugins/manifest-reference#environment-variables)。672在第一个会话后,`~/.claude/plugins/data/<id>/node_modules` 存在。MCP 服务器然后可以在其 `env` 中设置 `NODE_PATH` 为 `${CLAUDE_PLUGIN_DATA}/node_modules`。对于哪些字段替换哪个变量,请参阅[环境变量](/docs/zh-CN/plugins/manifest-reference#environment-variables)。
1138 673
1139<h2 id="next-steps">674## 后续步骤
1140 后续步骤
1141</h2>
1142 675
1143* [插件清单参考](/docs/zh-CN/plugins/manifest-reference):`plugin.json` 字段、路径规则和标准布局676* [插件清单参考](/docs/zh-CN/plugins/manifest-reference):`plugin.json` 字段、路径规则和标准布局
1144* [使用 evals 测试插件](/docs/zh-CN/plugin-evals):检查您添加的组件以您打算的方式改变 Claude 的行为677* [使用 evals 测试插件](/docs/zh-CN/plugin-evals):检查您添加的组件以您打算的方式改变 Claude 的行为