Follow Discord
Sweep 22 Oct 2026 · 21:04Z Build v2.1.309 528 read Stable v2.1.308 Latest v2.1.309 Next v2.1.309 Feeds RSS JSON llms.txt llms-full.txt Unofficial
One change · claude-code

Plugins in the SDK changedagent-sdk/plugins

Nearest release: v2.1.283, published 4 hours before upstream edited the page. Shown because the two are within 24 hours of each other. Nothing here says the release caused the edit.

Upstream edited this page at 25 Sep 2026 23:42 UTC, give or take a minute or two: the time comes from Anthropic’s own sitemap rather than from a commit. This site recorded the change at 20 Oct 2026 18:37 UTC.

Upstream edited
Recorded here
Lines+219added
Lines−227removed
From line 17 where the diff opens
First seen 14 Aug 2026 this site's first read of the page
Recorded edits8to this page, all time

The whole hunk

from line 17, old and new numbered
/
lines
from line 17
1717 
1818To use a plugin distributed through a [marketplace](/docs/en/plugins/overview) or remote repository, download it first and provide the local directory path. For the directory layout a plugin needs, see the [Plugin structure reference](#plugin-structure-reference) below.
1919 
20<CodeGroup>
21 ```typescript TypeScript theme={null}
22 import { query } from "@anthropic-ai/claude-agent-sdk";
20```typescript TypeScript theme={null}
21import { query } from "@anthropic-ai/claude-agent-sdk";
2322 
24 for await (const message of query({
25 prompt: "Hello",
26 options: {
27 plugins: [
28 { type: "local", path: "./my-plugin" },
29 { type: "local", path: "/absolute/path/to/another-plugin" }
30 ]
31 }
32 })) {
33 // Plugin commands, agents, and other features are now available
23for await (const message of query({
24 prompt: "Hello",
25 options: {
26 plugins: [
27 { type: "local", path: "./my-plugin" },
28 { type: "local", path: "/absolute/path/to/another-plugin" }
29 ]
3430 }
35 ```
31})) {
32 // Plugin commands, agents, and other features are now available
33}
34```
3635 
37 ```python Python theme={null}
38 import asyncio
39 from claude_agent_sdk import query, ClaudeAgentOptions
36```python Python theme={null}
37import asyncio
38from claude_agent_sdk import query, ClaudeAgentOptions
4039 
4140 
42 async def main():
43 async for message in query(
44 prompt="Hello",
45 options=ClaudeAgentOptions(
46 plugins=[
47 {"type": "local", "path": "./my-plugin"},
48 {"type": "local", "path": "/absolute/path/to/another-plugin"},
49 ]
50 ),
51 ):
52 # Plugin commands, agents, and other features are now available
53 pass
41async def main():
42 async for message in query(
43 prompt="Hello",
44 options=ClaudeAgentOptions(
45 plugins=[
46 {"type": "local", "path": "./my-plugin"},
47 {"type": "local", "path": "/absolute/path/to/another-plugin"},
48 ]
49 ),
50 ):
51 # Plugin commands, agents, and other features are now available
52 pass
5453 
5554 
56 asyncio.run(main())
57 ```
58</CodeGroup>
55asyncio.run(main())
56```
5957 
6058### Path specifications
6159 
from line 62
6462* **Relative paths**: resolved relative to the `cwd` option (for example, `"./plugins/my-plugin"`)
6563* **Absolute paths**: full file system paths (for example, `"/home/user/plugins/my-plugin"`)
6664 
67<Note>
68 The path should point to the plugin's root directory: the parent of `skills/`, `agents/`, `hooks/`, `commands/`, or `.claude-plugin/`.
69</Note>
65> [!NOTE]
66>
67> The path should point to the plugin's root directory: the parent of `skills/`, `agents/`, `hooks/`, `commands/`, or `.claude-plugin/`.
7068 
7169## Verifying plugin installation
7270 
7371When plugins load successfully, they appear in the system initialization message. You can verify that your plugins are available:
7472 
75<CodeGroup>
76 ```typescript TypeScript theme={null}
77 import { query } from "@anthropic-ai/claude-agent-sdk";
73```typescript TypeScript theme={null}
74import { query } from "@anthropic-ai/claude-agent-sdk";
7875 
79 for await (const message of query({
80 prompt: "Hello",
81 options: {
82 plugins: [{ type: "local", path: "./my-plugin" }]
83 }
84 })) {
85 if (message.type === "system" && message.subtype === "init") {
86 // Check loaded plugins
87 console.log("Plugins:", message.plugins);
88 // Example: [{ name: "my-plugin", path: "/absolute/path/to/my-plugin" }]
76for await (const message of query({
77 prompt: "Hello",
78 options: {
79 plugins: [{ type: "local", path: "./my-plugin" }]
80 }
81})) {
82 if (message.type === "system" && message.subtype === "init") {
83 // Check loaded plugins
84 console.log("Plugins:", message.plugins);
85 // Example: [{ name: "my-plugin", path: "/absolute/path/to/my-plugin" }]
8986 
90 // Plugin skills appear with the plugin name as a prefix
91 console.log("Skills:", message.skills);
92 // Example: ["my-plugin:greet"]
87 // Plugin skills appear with the plugin name as a prefix
88 console.log("Skills:", message.skills);
89 // Example: ["my-plugin:greet"]
9390 
94 // Plugin commands use the same prefix, and skills appear here too
95 console.log("Commands:", message.slash_commands);
96 // Example: ["compact", "context", "my-plugin:custom-command", "my-plugin:greet"]
97 }
91 // Plugin commands use the same prefix, and skills appear here too
92 console.log("Commands:", message.slash_commands);
93 // Example: ["compact", "context", "my-plugin:custom-command", "my-plugin:greet"]
9894 }
99 ```
95}
96```
10097 
101 ```python Python theme={null}
102 import asyncio
103 from claude_agent_sdk import query, ClaudeAgentOptions, SystemMessage
98```python Python theme={null}
99import asyncio
100from claude_agent_sdk import query, ClaudeAgentOptions, SystemMessage
104101 
105102 
106 async def main():
107 async for message in query(
108 prompt="Hello",
109 options=ClaudeAgentOptions(
110 plugins=[{"type": "local", "path": "./my-plugin"}]
111 ),
112 ):
113 if isinstance(message, SystemMessage) and message.subtype == "init":
114 # Check loaded plugins
115 print("Plugins:", message.data.get("plugins"))
116 # Example: [{"name": "my-plugin", "path": "/absolute/path/to/my-plugin"}]
103async def main():
104 async for message in query(
105 prompt="Hello",
106 options=ClaudeAgentOptions(
107 plugins=[{"type": "local", "path": "./my-plugin"}]
108 ),
109 ):
110 if isinstance(message, SystemMessage) and message.subtype == "init":
111 # Check loaded plugins
112 print("Plugins:", message.data.get("plugins"))
113 # Example: [{"name": "my-plugin", "path": "/absolute/path/to/my-plugin"}]
117114 
118 # Plugin skills appear with the plugin name as a prefix
119 print("Skills:", message.data.get("skills"))
120 # Example: ["my-plugin:greet"]
115 # Plugin skills appear with the plugin name as a prefix
116 print("Skills:", message.data.get("skills"))
117 # Example: ["my-plugin:greet"]
121118 
122 # Plugin commands use the same prefix, and skills appear here too
123 print("Commands:", message.data.get("slash_commands"))
124 # Example: ["compact", "context", "my-plugin:custom-command", "my-plugin:greet"]
119 # Plugin commands use the same prefix, and skills appear here too
120 print("Commands:", message.data.get("slash_commands"))
121 # Example: ["compact", "context", "my-plugin:custom-command", "my-plugin:greet"]
125122 
126123 
127 asyncio.run(main())
128 ```
129</CodeGroup>
124asyncio.run(main())
125```
130126 
131127## Use plugin skills
132128 
133129Skills from plugins are automatically namespaced with the plugin name to avoid conflicts. To invoke one directly, send `/plugin-name:skill-name` as the prompt.
134130 
135<CodeGroup>
136 ```typescript TypeScript theme={null}
137 import { query } from "@anthropic-ai/claude-agent-sdk";
131```typescript TypeScript theme={null}
132import { query } from "@anthropic-ai/claude-agent-sdk";
138133 
139 // Load a plugin with a custom /greet skill
140 for await (const message of query({
141 prompt: "/my-plugin:greet", // Use plugin skill with namespace
142 options: {
143 plugins: [{ type: "local", path: "./my-plugin" }]
144 }
145 })) {
146 // Claude executes the custom greeting skill from the plugin
147 if (message.type === "assistant") {
148 console.log(message.message.content);
149 }
134// Load a plugin with a custom /greet skill
135for await (const message of query({
136 prompt: "/my-plugin:greet", // Use plugin skill with namespace
137 options: {
138 plugins: [{ type: "local", path: "./my-plugin" }]
150139 }
151 ```
140})) {
141 // Claude executes the custom greeting skill from the plugin
142 if (message.type === "assistant") {
143 console.log(message.message.content);
144 }
145}
146```
152147 
153 ```python Python theme={null}
154 import asyncio
155 from claude_agent_sdk import query, ClaudeAgentOptions, AssistantMessage, TextBlock
148```python Python theme={null}
149import asyncio
150from claude_agent_sdk import query, ClaudeAgentOptions, AssistantMessage, TextBlock
156151 
157152 
158 async def main():
159 # Load a plugin with a custom /greet skill
160 async for message in query(
161 prompt="/my-plugin:greet", # Use plugin skill with namespace
162 options=ClaudeAgentOptions(
163 plugins=[{"type": "local", "path": "./my-plugin"}]
164 ),
165 ):
166 # Claude executes the custom greeting skill from the plugin
167 if isinstance(message, AssistantMessage):
168 for block in message.content:
169 if isinstance(block, TextBlock):
170 print(f"Claude: {block.text}")
153async def main():
154 # Load a plugin with a custom /greet skill
155 async for message in query(
156 prompt="/my-plugin:greet", # Use plugin skill with namespace
157 options=ClaudeAgentOptions(
158 plugins=[{"type": "local", "path": "./my-plugin"}]
159 ),
160 ):
161 # Claude executes the custom greeting skill from the plugin
162 if isinstance(message, AssistantMessage):
163 for block in message.content:
164 if isinstance(block, TextBlock):
165 print(f"Claude: {block.text}")
171166 
172167 
173 asyncio.run(main())
174 ```
175</CodeGroup>
168asyncio.run(main())
169```
176170 
177<Note>
178 If you installed a plugin via the CLI (for example, `/plugin install my-plugin@marketplace`), you can still use it in the SDK by providing its installation path. Check `~/.claude/plugins/` for CLI-installed plugins.
179</Note>
171> [!NOTE]
172>
173> If you installed a plugin via the CLI (for example, `/plugin install my-plugin@marketplace`), you can still use it in the SDK by providing its installation path. Check `~/.claude/plugins/` for CLI-installed plugins.
180174 
181175## Complete example
182176 
183177Here's a full example demonstrating plugin loading and usage:
184178 
185<CodeGroup>
186 ```typescript TypeScript theme={null}
187 import { query } from "@anthropic-ai/claude-agent-sdk";
188 import { fileURLToPath } from "node:url";
179```typescript TypeScript theme={null}
180import { query } from "@anthropic-ai/claude-agent-sdk";
181import { fileURLToPath } from "node:url";
189182 
190 async function runWithPlugin() {
191 const pluginPath = fileURLToPath(new URL("./plugins/my-plugin", import.meta.url));
183async function runWithPlugin() {
184 const pluginPath = fileURLToPath(new URL("./plugins/my-plugin", import.meta.url));
192185 
193 console.log("Loading plugin from:", pluginPath);
186 console.log("Loading plugin from:", pluginPath);
194187 
195 for await (const message of query({
196 prompt: "What custom commands do you have available?",
197 options: {
198 plugins: [{ type: "local", path: pluginPath }],
199 maxTurns: 3
200 }
201 })) {
202 if (message.type === "system" && message.subtype === "init") {
203 console.log("Loaded plugins:", message.plugins);
204 console.log("Available skills:", message.skills);
205 console.log("Available commands:", message.slash_commands);
206 }
188 for await (const message of query({
189 prompt: "What custom commands do you have available?",
190 options: {
191 plugins: [{ type: "local", path: pluginPath }],
192 maxTurns: 3
193 }
194 })) {
195 if (message.type === "system" && message.subtype === "init") {
196 console.log("Loaded plugins:", message.plugins);
197 console.log("Available skills:", message.skills);
198 console.log("Available commands:", message.slash_commands);
199 }
207200 
208 if (message.type === "assistant") {
209 console.log("Assistant:", message.message.content);
210 }
201 if (message.type === "assistant") {
202 console.log("Assistant:", message.message.content);
211203 }
212204 }
205}
213206 
214 runWithPlugin().catch(console.error);
215 ```
207runWithPlugin().catch(console.error);
208```
216209 
217 ```python Python theme={null}
218 #!/usr/bin/env python3
219 """Example demonstrating how to use plugins with the Agent SDK."""
210```python Python theme={null}
211#!/usr/bin/env python3
212"""Example demonstrating how to use plugins with the Agent SDK."""
220213 
221 import asyncio
222 from pathlib import Path
214import asyncio
215from pathlib import Path
223216 
224 from claude_agent_sdk import (
225 AssistantMessage,
226 ClaudeAgentOptions,
227 SystemMessage,
228 TextBlock,
229 query,
230 )
217from claude_agent_sdk import (
218 AssistantMessage,
219 ClaudeAgentOptions,
220 SystemMessage,
221 TextBlock,
222 query,
223)
231224 
232225 
233 async def run_with_plugin():
234 """Example using a custom plugin."""
235 plugin_path = Path(__file__).parent / "plugins" / "my-plugin"
226async def run_with_plugin():
227 """Example using a custom plugin."""
228 plugin_path = Path(__file__).parent / "plugins" / "my-plugin"
236229 
237 print(f"Loading plugin from: {plugin_path}")
230 print(f"Loading plugin from: {plugin_path}")
238231 
239 options = ClaudeAgentOptions(
240 plugins=[{"type": "local", "path": str(plugin_path)}],
241 max_turns=3,
242 )
232 options = ClaudeAgentOptions(
233 plugins=[{"type": "local", "path": str(plugin_path)}],
234 max_turns=3,
235 )
243236 
244 async for message in query(
245 prompt="What custom commands do you have available?", options=options
246 ):
247 if isinstance(message, SystemMessage) and message.subtype == "init":
248 print(f"Loaded plugins: {message.data.get('plugins')}")
249 print(f"Available skills: {message.data.get('skills')}")
250 print(f"Available commands: {message.data.get('slash_commands')}")
237 async for message in query(
238 prompt="What custom commands do you have available?", options=options
239 ):
240 if isinstance(message, SystemMessage) and message.subtype == "init":
241 print(f"Loaded plugins: {message.data.get('plugins')}")
242 print(f"Available skills: {message.data.get('skills')}")
243 print(f"Available commands: {message.data.get('slash_commands')}")
251244 
252 if isinstance(message, AssistantMessage):
253 for block in message.content:
254 if isinstance(block, TextBlock):
255 print(f"Assistant: {block.text}")
245 if isinstance(message, AssistantMessage):
246 for block in message.content:
247 if isinstance(block, TextBlock):
248 print(f"Assistant: {block.text}")
256249 
257250 
258 if __name__ == "__main__":
259 asyncio.run(run_with_plugin())
260 ```
261</CodeGroup>
251if __name__ == "__main__":
252 asyncio.run(run_with_plugin())
253```
262254 
263255## Plugin structure reference
264256 
from line 272
280272└── .mcp.json # MCP server definitions
281273```
282274 
283<Note>
284 The `commands/` directory holds skills as flat Markdown files. Use `skills/` for new plugins. Claude Code supports both locations.
285</Note>
275> [!NOTE]
276>
277> The `commands/` directory holds skills as flat Markdown files. Use `skills/` for new plugins. Claude Code supports both locations.
286278 
287279## Multiple plugin sources
288280 
from line 293
301293];
302294```
303295 
304<Note>
305 The SDK doesn't expand tilde paths like `~/plugins`. If a plugin path doesn't exist, the SDK skips that plugin and the session continues, so check the `plugins` list in the init message to confirm each plugin loaded.
306</Note>
296> [!NOTE]
297>
298> The SDK doesn't expand tilde paths like `~/plugins`. If a plugin path doesn't exist, the SDK skips that plugin and the session continues, so check the `plugins` list in the init message to confirm each plugin loaded.
307299 
308300## Troubleshooting
309301 
Feedback