Use Claude Code features in the SDK changedagent-sdk/claude-code-features
Nearest release: v2.1.291, published 2 hours after 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 6 Oct 2026 01:07 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+182added
Lines−188removed
From line
12
where the diff opens
First seen
14 Aug 2026
this site's first read of the page
Recorded edits13to this page, all time
The whole hunk
from line 12, old and new numbered
/
from line 12
1212
1313This example loads both user-level and project-level settings by setting `settingSources` to `["user", "project"]`:
1414
15<CodeGroup>
16 ```python Python theme={null}
17 from claude_agent_sdk import query, ClaudeAgentOptions, AssistantMessage, ResultMessage
18 import asyncio
15```python Python theme={null}
16from claude_agent_sdk import query, ClaudeAgentOptions, AssistantMessage, ResultMessage
17import asyncio
1918
2019
21 async def main():
22 async for message in query(
23 prompt="Help me refactor the auth module",
24 options=ClaudeAgentOptions(
25 # "user" loads from ~/.claude/, "project" loads from ./.claude/ in cwd.
26 # Together they give the agent access to CLAUDE.md, skills, hooks, and
27 # permissions from both locations.
28 setting_sources=["user", "project"],
29 allowed_tools=["Read", "Edit", "Bash"],
30 ),
31 ):
32 if isinstance(message, AssistantMessage):
33 for block in message.content:
34 if hasattr(block, "text"):
35 print(block.text)
36 if isinstance(message, ResultMessage) and message.subtype == "success":
37 print(f"\nResult: {message.result}")
20async def main():
21 async for message in query(
22 prompt="Help me refactor the auth module",
23 options=ClaudeAgentOptions(
24 # "user" loads from ~/.claude/, "project" loads from ./.claude/ in cwd.
25 # Together they give the agent access to CLAUDE.md, skills, hooks, and
26 # permissions from both locations.
27 setting_sources=["user", "project"],
28 allowed_tools=["Read", "Edit", "Bash"],
29 ),
30 ):
31 if isinstance(message, AssistantMessage):
32 for block in message.content:
33 if hasattr(block, "text"):
34 print(block.text)
35 if isinstance(message, ResultMessage) and message.subtype == "success":
36 print(f"\nResult: {message.result}")
3837
3938
40 asyncio.run(main())
41 ```
39asyncio.run(main())
40```
4241
43 ```typescript TypeScript theme={null}
44 import { query } from "@anthropic-ai/claude-agent-sdk";
42```typescript TypeScript theme={null}
43import { query } from "@anthropic-ai/claude-agent-sdk";
4544
46 for await (const message of query({
47 prompt: "Help me refactor the auth module",
48 options: {
49 // "user" loads from ~/.claude/, "project" loads from ./.claude/ in cwd.
50 // Together they give the agent access to CLAUDE.md, skills, hooks, and
51 // permissions from both locations.
52 settingSources: ["user", "project"],
53 allowedTools: ["Read", "Edit", "Bash"]
45for await (const message of query({
46 prompt: "Help me refactor the auth module",
47 options: {
48 // "user" loads from ~/.claude/, "project" loads from ./.claude/ in cwd.
49 // Together they give the agent access to CLAUDE.md, skills, hooks, and
50 // permissions from both locations.
51 settingSources: ["user", "project"],
52 allowedTools: ["Read", "Edit", "Bash"]
53 }
54})) {
55 if (message.type === "assistant") {
56 for (const block of message.message.content) {
57 if (block.type === "text") console.log(block.text);
5458 }
55 })) {
56 if (message.type === "assistant") {
57 for (const block of message.message.content) {
58 if (block.type === "text") console.log(block.text);
59 }
60 }
61 if (message.type === "result" && message.subtype === "success") {
62 console.log(`\nResult: ${message.result}`);
63 }
6459 }
65 ```
66</CodeGroup>
60 if (message.type === "result" && message.subtype === "success") {
61 console.log(`\nResult: ${message.result}`);
62 }
63}
64```
6765
6866When this runs, the assistant's response prints to stdout, followed by a final result line once the run completes.
6967
from line 89
9189| [claude.ai MCP connectors](/docs/en/mcp#use-mcp-servers-from-claude-ai) | Loaded when the session authenticates with your claude.ai login. Not loaded when `CLAUDE_CODE_OAUTH_TOKEN` holds a token from [`claude setup-token`](/docs/en/authentication#generate-a-long-lived-token), which can only make model requests. Passing `mcpServers: {}` does not suppress the connectors | Set `strictMcpConfig: true`, [`disableClaudeAiConnectors: true`](/docs/en/mcp#disable-claude-ai-connectors) in settings, or `ENABLE_CLAUDEAI_MCP_SERVERS=false` in `env` |
9290| [`sandbox.credentials`](/docs/en/sandboxing#protect-credentials) `deny` entries and file `mask` entries in `~/.claude/settings.json` | When the [command sandbox](/docs/en/sandboxing) runs, Claude Code applies the `deny` entries and keeps the `credentials.files` `mask` entries as restrictions even when `settingSources` excludes user settings. Claude Code uses these entries only to narrow what sandboxed commands can access | Remove the entries from `~/.claude/settings.json` |
9391
94<Warning>
95 Do not rely on default `query()` options for multi-tenant isolation. Because the inputs above are read regardless of `settingSources`, an SDK process can pick up host-level configuration and per-directory memory. For multi-tenant deployments, run each tenant in its own filesystem and set `settingSources: []` plus `CLAUDE_CODE_DISABLE_AUTO_MEMORY=1` in `env`. [Server-managed settings](/docs/en/server-managed-settings) are fetched when the process authenticates with an organization credential; filesystem isolation does not remove them. See [Secure deployment](/docs/en/agent-sdk/secure-deployment).
96</Warning>
92> [!WARNING]
93>
94> Do not rely on default `query()` options for multi-tenant isolation. Because the inputs above are read regardless of `settingSources`, an SDK process can pick up host-level configuration and per-directory memory. For multi-tenant deployments, run each tenant in its own filesystem and set `settingSources: []` plus `CLAUDE_CODE_DISABLE_AUTO_MEMORY=1` in `env`. [Server-managed settings](/docs/en/server-managed-settings) are fetched when the process authenticates with an organization credential; filesystem isolation does not remove them. See [Secure deployment](/docs/en/agent-sdk/secure-deployment).
9795
9896## Project instructions (CLAUDE.md and rules)
9997
from line 111
113111
114112All levels are additive: if both project and user CLAUDE.md files exist, the agent sees both. There is no hard precedence rule between levels; if instructions conflict, the outcome depends on how Claude interprets them. Write non-conflicting rules, or state precedence explicitly in the more specific file ("These project instructions override any conflicting user-level defaults").
115113
116<Tip>
117 You can also inject context directly via `systemPrompt` without using CLAUDE.md files. See [Modify system prompts](/docs/en/agent-sdk/modifying-system-prompts). Use CLAUDE.md when you want the same context shared between interactive Claude Code sessions and your SDK agents.
118</Tip>
114> [!TIP]
115>
116> You can also inject context directly via `systemPrompt` without using CLAUDE.md files. See [Modify system prompts](/docs/en/agent-sdk/modifying-system-prompts). Use CLAUDE.md when you want the same context shared between interactive Claude Code sessions and your SDK agents.
119117
120118For how to structure and organize CLAUDE.md content, see [Manage Claude's memory](/docs/en/memory).
121119
from line 123
125123
126124Skills are discovered from the filesystem through `settingSources`. When the `skills` option on `query()` is omitted, discovered user and project skills are enabled and the Skill tool is available, matching CLI behavior. To control which skills are enabled, pass `skills` as `"all"`, a list of skill names, or `[]` to disable all. When `skills` is set, the SDK adds the Skill tool to `allowedTools` automatically. If you also pass an explicit `tools` list, include `"Skill"` in that list so Claude can invoke skills.
127125
128<CodeGroup>
129 ```python Python theme={null}
130 from claude_agent_sdk import query, ClaudeAgentOptions, ResultMessage
131 import asyncio
126```python Python theme={null}
127from claude_agent_sdk import query, ClaudeAgentOptions, ResultMessage
128import asyncio
132129
133130
134 # Skills in .claude/skills/ are discovered automatically
135 # when settingSources includes "project"
136 async def main():
137 async for message in query(
138 prompt="Review this PR using our code review checklist",
139 options=ClaudeAgentOptions(
140 setting_sources=["user", "project"],
141 skills="all",
142 allowed_tools=["Read", "Grep", "Glob"],
143 ),
144 ):
145 if isinstance(message, ResultMessage) and message.subtype == "success":
146 print(message.result)
131# Skills in .claude/skills/ are discovered automatically
132# when settingSources includes "project"
133async def main():
134 async for message in query(
135 prompt="Review this PR using our code review checklist",
136 options=ClaudeAgentOptions(
137 setting_sources=["user", "project"],
138 skills="all",
139 allowed_tools=["Read", "Grep", "Glob"],
140 ),
141 ):
142 if isinstance(message, ResultMessage) and message.subtype == "success":
143 print(message.result)
147144
148145
149 asyncio.run(main())
150 ```
146asyncio.run(main())
147```
151148
152 ```typescript TypeScript theme={null}
153 import { query } from "@anthropic-ai/claude-agent-sdk";
149```typescript TypeScript theme={null}
150import { query } from "@anthropic-ai/claude-agent-sdk";
154151
155 // Skills in .claude/skills/ are discovered automatically
156 // when settingSources includes "project"
157 for await (const message of query({
158 prompt: "Review this PR using our code review checklist",
159 options: {
160 settingSources: ["user", "project"],
161 skills: "all",
162 allowedTools: ["Read", "Grep", "Glob"]
163 }
164 })) {
165 if (message.type === "result" && message.subtype === "success") {
166 console.log(message.result);
167 }
152// Skills in .claude/skills/ are discovered automatically
153// when settingSources includes "project"
154for await (const message of query({
155 prompt: "Review this PR using our code review checklist",
156 options: {
157 settingSources: ["user", "project"],
158 skills: "all",
159 allowedTools: ["Read", "Grep", "Glob"]
168160 }
169 ```
170</CodeGroup>
161})) {
162 if (message.type === "result" && message.subtype === "success") {
163 console.log(message.result);
164 }
165}
166```
171167
172<Note>
173 Skills must be created as filesystem artifacts (`.claude/skills/<name>/SKILL.md`). The SDK does not have a programmatic API for registering skills. See [Agent Skills in the SDK](/docs/en/agent-sdk/skills) for full details.
174</Note>
168> [!NOTE]
169>
170> Skills must be created as filesystem artifacts (`.claude/skills/<name>/SKILL.md`). The SDK does not have a programmatic API for registering skills. See [Agent Skills in the SDK](/docs/en/agent-sdk/skills) for full details.
175171
176172## Hooks
177173
from line 178
182178
183179Hook callbacks receive the tool input and return a decision dict. Returning `{}` means allow the tool to proceed. To block execution, return a `hookSpecificOutput` object with `permissionDecision: "deny"` and a `permissionDecisionReason`. The reason is sent to Claude as the tool result. See the [hooks guide](/docs/en/agent-sdk/hooks) for the full callback signature and return types.
184180
185<CodeGroup>
186 ```python Python theme={null}
187 from claude_agent_sdk import query, ClaudeAgentOptions, HookMatcher, ResultMessage
188 import asyncio
181```python Python theme={null}
182from claude_agent_sdk import query, ClaudeAgentOptions, HookMatcher, ResultMessage
183import asyncio
189184
190185
191 # PreToolUse hook callback. Positional args:
192 # input_data: HookInput dict with tool_name, tool_input, hook_event_name
193 # tool_use_id: str | None, the ID of the tool call being intercepted
194 # context: HookContext, reserved for future abort-signal support
195 async def audit_bash(input_data, tool_use_id, context):
196 command = input_data.get("tool_input", {}).get("command", "")
197 if "rm -rf" in command:
198 return {
199 "hookSpecificOutput": {
200 "hookEventName": "PreToolUse",
201 "permissionDecision": "deny",
202 "permissionDecisionReason": "Destructive command blocked",
203 }
204 }
205 return {} # Empty dict: allow the tool to proceed
186# PreToolUse hook callback. Positional args:
187# input_data: HookInput dict with tool_name, tool_input, hook_event_name
188# tool_use_id: str | None, the ID of the tool call being intercepted
189# context: HookContext, reserved for future abort-signal support
190async def audit_bash(input_data, tool_use_id, context):
191 command = input_data.get("tool_input", {}).get("command", "")
192 if "rm -rf" in command:
193 return {
194 "hookSpecificOutput": {
195 "hookEventName": "PreToolUse",
196 "permissionDecision": "deny",
197 "permissionDecisionReason": "Destructive command blocked",
198 }
199 }
200 return {} # Empty dict: allow the tool to proceed
206201
207202
208 # Filesystem hooks from .claude/settings.json run automatically
209 # when settingSources loads them. You can also add programmatic hooks:
210 async def main():
211 async for message in query(
212 prompt="Refactor the auth module",
213 options=ClaudeAgentOptions(
214 setting_sources=["project"], # Loads hooks from .claude/settings.json
215 hooks={
216 "PreToolUse": [
217 HookMatcher(matcher="Bash", hooks=[audit_bash]),
218 ]
219 },
220 ),
221 ):
222 if isinstance(message, ResultMessage) and message.subtype == "success":
223 print(message.result)
203# Filesystem hooks from .claude/settings.json run automatically
204# when settingSources loads them. You can also add programmatic hooks:
205async def main():
206 async for message in query(
207 prompt="Refactor the auth module",
208 options=ClaudeAgentOptions(
209 setting_sources=["project"], # Loads hooks from .claude/settings.json
210 hooks={
211 "PreToolUse": [
212 HookMatcher(matcher="Bash", hooks=[audit_bash]),
213 ]
214 },
215 ),
216 ):
217 if isinstance(message, ResultMessage) and message.subtype == "success":
218 print(message.result)
224219
225220
226 asyncio.run(main())
227 ```
221asyncio.run(main())
222```
228223
229 ```typescript TypeScript theme={null}
230 import { query, type HookInput, type HookJSONOutput } from "@anthropic-ai/claude-agent-sdk";
224```typescript TypeScript theme={null}
225import { query, type HookInput, type HookJSONOutput } from "@anthropic-ai/claude-agent-sdk";
231226
232 // PreToolUse hook callback. HookInput is a discriminated union on
233 // hook_event_name, so narrowing on it gives TypeScript the right
234 // tool_input shape for this event.
235 const auditBash = async (input: HookInput): Promise<HookJSONOutput> => {
236 if (input.hook_event_name !== "PreToolUse") return {};
237 const toolInput = input.tool_input as { command?: string };
238 if (toolInput.command?.includes("rm -rf")) {
239 return {
240 hookSpecificOutput: {
241 hookEventName: "PreToolUse",
242 permissionDecision: "deny",
243 permissionDecisionReason: "Destructive command blocked",
244 },
245 };
246 }
247 return {}; // Empty object: allow the tool to proceed
248 };
227// PreToolUse hook callback. HookInput is a discriminated union on
228// hook_event_name, so narrowing on it gives TypeScript the right
229// tool_input shape for this event.
230const auditBash = async (input: HookInput): Promise<HookJSONOutput> => {
231 if (input.hook_event_name !== "PreToolUse") return {};
232 const toolInput = input.tool_input as { command?: string };
233 if (toolInput.command?.includes("rm -rf")) {
234 return {
235 hookSpecificOutput: {
236 hookEventName: "PreToolUse",
237 permissionDecision: "deny",
238 permissionDecisionReason: "Destructive command blocked",
239 },
240 };
241 }
242 return {}; // Empty object: allow the tool to proceed
243};
249244
250 // Filesystem hooks from .claude/settings.json run automatically
251 // when settingSources loads them. You can also add programmatic hooks:
252 for await (const message of query({
253 prompt: "Refactor the auth module",
254 options: {
255 settingSources: ["project"], // Loads hooks from .claude/settings.json
256 hooks: {
257 PreToolUse: [{ matcher: "Bash", hooks: [auditBash] }]
258 }
245// Filesystem hooks from .claude/settings.json run automatically
246// when settingSources loads them. You can also add programmatic hooks:
247for await (const message of query({
248 prompt: "Refactor the auth module",
249 options: {
250 settingSources: ["project"], // Loads hooks from .claude/settings.json
251 hooks: {
252 PreToolUse: [{ matcher: "Bash", hooks: [auditBash] }]
259253 }
260 })) {
261 if (message.type === "result" && message.subtype === "success") {
262 console.log(message.result);
263 }
264254 }
265 ```
266</CodeGroup>
255})) {
256 if (message.type === "result" && message.subtype === "success") {
257 console.log(message.result);
258 }
259}
260```
267261
268262### When to use which hook type
269263
from line 266
272266| **Filesystem** (`settings.json`) | Sharing hooks between CLI and SDK sessions. Supports `"command"` (shell scripts), `"http"` (POST to an endpoint), `"mcp_tool"` (call a connected MCP server's tool), `"prompt"` (LLM evaluates a prompt), and `"agent"` (spawns a verifier agent). These fire in the main agent and any subagents it spawns. |
273267| **Programmatic** (callbacks in `query()`) | Application-specific logic, structured decisions, and in-process integration. These also fire inside subagents. The hook input, the callback's first argument, carries `agent_id` and `agent_type` fields that identify which agent fired the hook. |
274268
275<Note>
276 The TypeScript SDK supports additional hook events beyond Python, including `SessionStart`, `SessionEnd`, `TeammateIdle`, and `TaskCompleted`. See the [hooks guide](/docs/en/agent-sdk/hooks) for the full event compatibility table.
277</Note>
269> [!NOTE]
270>
271> The TypeScript SDK supports additional hook events beyond Python, including `SessionStart`, `SessionEnd`, `TeammateIdle`, and `TaskCompleted`. See the [hooks guide](/docs/en/agent-sdk/hooks) for the full event compatibility table.
278272
279273For full details on programmatic hooks, see [Control execution with hooks](/docs/en/agent-sdk/hooks). For filesystem hook syntax, see [Hooks](/docs/en/hooks).
280274
No line in this hunk matches that.