82`settingSources` covers user, project, and local settings. A few inputs are read regardless of its value:82`settingSources` covers user, project, and local settings. A few inputs are read regardless of its value:
83 83
84| Input | Behavior | To disable |84| Input | Behavior | To disable |
8585| :---------------------------------------------------- | :--------------------------------------- | :------------------------------------------------------------------------------------------ || :----------------------------------------------------------------- | :------------------------------------------------------------------------------------------------------------------------ | :------------------------------------------------------------------------------------------ |
86| Managed policy settings | Always loaded when present on the host | Remove the managed settings file |86| Managed policy settings | Always loaded when present on the host | Remove the managed settings file |
87| `~/.claude.json` global config | Always read | Relocate with `CLAUDE_CONFIG_DIR` in `env` |87| `~/.claude.json` global config | Always read | Relocate with `CLAUDE_CONFIG_DIR` in `env` |
88| Auto memory at `~/.claude/projects/<project>/memory/` | Loaded by default into the system prompt | Set `autoMemoryEnabled: false` in settings, or `CLAUDE_CODE_DISABLE_AUTO_MEMORY=1` in `env` |88| Auto memory at `~/.claude/projects/<project>/memory/` | Loaded by default into the system prompt | Set `autoMemoryEnabled: false` in settings, or `CLAUDE_CODE_DISABLE_AUTO_MEMORY=1` in `env` |
89| [claude.ai MCP connectors](/en/mcp#use-mcp-servers-from-claude-ai) | Loaded when the active authentication method is a claude.ai subscription. Passing `mcpServers: {}` does not suppress them | Set `strictMcpConfig: true`, or `ENABLE_CLAUDEAI_MCP_SERVERS=false` in `env` |
89 90
90<Warning>91<Warning>
91 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`. See [Secure deployment](/en/agent-sdk/secure-deployment).92 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`. See [Secure deployment](/en/agent-sdk/secure-deployment).
119 120
120Skills are markdown files that give your agent specialized knowledge and invocable workflows. Unlike `CLAUDE.md` (which loads every session), skills load on demand. The agent receives skill descriptions at startup and loads the full content when relevant.121Skills are markdown files that give your agent specialized knowledge and invocable workflows. Unlike `CLAUDE.md` (which loads every session), skills load on demand. The agent receives skill descriptions at startup and loads the full content when relevant.
121 122
122123Skills 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. The SDK enables the Skill tool automatically when `skills` is set, so you do not need to add it to `allowedTools`.Skills 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.
123 124
124<CodeGroup>125<CodeGroup>
125 ```python Python theme={null}126 ```python Python theme={null}
174 175
175Both types execute during the same hook lifecycle. If you already have hooks in your project's `.claude/settings.json` and you set `settingSources: ["project"]`, those hooks run automatically in the SDK with no extra configuration.176Both types execute during the same hook lifecycle. If you already have hooks in your project's `.claude/settings.json` and you set `settingSources: ["project"]`, those hooks run automatically in the SDK with no extra configuration.
176 177
177178Hook callbacks receive the tool input and return a decision dict. Returning `{}` (an empty dict) means allow the tool to proceed. Returning `{"decision": "block", "reason": "..."}` prevents execution and the reason is sent to Claude as the tool result. See the [hooks guide](/en/agent-sdk/hooks) for the full callback signature and return types.Hook 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. The top-level `decision` and `reason` fields are deprecated for `PreToolUse`. See the [hooks guide](/en/agent-sdk/hooks) for the full callback signature and return types.
178 179
179<CodeGroup>180<CodeGroup>
180 ```python Python theme={null}181 ```python Python theme={null}
188 async def audit_bash(input_data, tool_use_id, context):189 async def audit_bash(input_data, tool_use_id, context):
189 command = input_data.get("tool_input", {}).get("command", "")190 command = input_data.get("tool_input", {}).get("command", "")
190 if "rm -rf" in command:191 if "rm -rf" in command:
191192 return {"decision": "block", "reason": "Destructive command blocked"} return {
193 "hookSpecificOutput": {
194 "hookEventName": "PreToolUse",
195 "permissionDecision": "deny",
196 "permissionDecisionReason": "Destructive command blocked",
197 }
198 }
192 return {} # Empty dict: allow the tool to proceed199 return {} # Empty dict: allow the tool to proceed
193 200
194 201
219 if (input.hook_event_name !== "PreToolUse") return {};226 if (input.hook_event_name !== "PreToolUse") return {};
220 const toolInput = input.tool_input as { command?: string };227 const toolInput = input.tool_input as { command?: string };
221 if (toolInput.command?.includes("rm -rf")) {228 if (toolInput.command?.includes("rm -rf")) {
222229 return { decision: "block", reason: "Destructive command blocked" }; return {
230 hookSpecificOutput: {
231 hookEventName: "PreToolUse",
232 permissionDecision: "deny",
233 permissionDecisionReason: "Destructive command blocked",
234 },
235 };
223 }236 }
224 return {}; // Empty object: allow the tool to proceed237 return {}; // Empty object: allow the tool to proceed
225 };238 };
247| Hook type | Best for |260| Hook type | Best for |
248| :---------------------------------------- | :----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |261| :---------------------------------------- | :----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
249| **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. |262| **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. |
250263| **Programmatic** (callbacks in `query()`) | Application-specific logic; returning structured decisions; in-process integration. Scoped to the main session only. || **Programmatic** (callbacks in `query()`) | Application-specific logic, structured decisions, and in-process integration. These also fire inside subagents. The callback receives `agent_id` and `agent_type` to distinguish. |
251 264
252<Note>265<Note>
253 The TypeScript SDK supports additional hook events beyond Python, including `SessionStart`, `SessionEnd`, `TeammateIdle`, and `TaskCompleted`. See the [hooks guide](/en/agent-sdk/hooks) for the full event compatibility table.266 The TypeScript SDK supports additional hook events beyond Python, including `SessionStart`, `SessionEnd`, `TeammateIdle`, and `TaskCompleted`. See the [hooks guide](/en/agent-sdk/hooks) for the full event compatibility table.