Claude Code features in the SDK
Load CLAUDE.md, rules, skills, hooks and other filesystem configuration into Agent SDK sessions, and control exactly which sources an agent reads.
An SDK agent sits on the same foundation as Claude Code, so it can pick up the same filesystem configuration: CLAUDE.md and rules files, skills, hooks, subagents, commands and settings. That is powerful when you want an agent to follow the conventions your team already wrote down for the CLI, and a liability when you want a deployed agent to behave identically everywhere. This page is about controlling which of those inputs load.
The default: same as the CLI
Leave settingSources unset and query() reads exactly what the CLI would: user, project and local settings, CLAUDE.md files, and the skills, agents and commands under .claude/. Pass an empty list and the agent gets only what you configure in code.
A handful of inputs are read regardless; they are listed under what settingSources does not cover.
Choosing sources with settingSources
settingSources (TypeScript) or setting_sources (Python) takes a list of any of "user", "project" and "local". Leaving it out is the same as passing all three.
Here is an agent for a client project that should follow the repository's own CLAUDE.md and hooks, but not my personal preferences from ~/.claude:
import { query } from "@anthropic-ai/claude-agent-sdk";
for await (const msg of query({
prompt: "Add pagination to the /orders endpoint, following our API conventions",
options: {
cwd: "/work/clients/ridgeway-api",
settingSources: ["project"],
allowedTools: ["Read", "Edit", "Glob", "Grep", "Bash"],
},
})) {
if (msg.type === "result" && msg.subtype === "success") console.log(msg.result);
}
opts = ClaudeAgentOptions(
cwd="/work/clients/ridgeway-api",
setting_sources=["project"],
allowed_tools=["Read", "Edit", "Glob", "Grep", "Bash"],
)
What each source loads
<cwd> below means the cwd option, or the process's current directory if you did not set one.
"project"
settings.jsonand its hooks from<cwd>/.claude/only. There is no fallback to parent directories for these.CLAUDE.mdand.claude/rules/*.mdfrom<cwd>and every parent directory.- Skills, commands and subagents from
.claude/skills/,.claude/commands/and.claude/agents/in<cwd>and each parent up to the repository root. - The same three folders inside any directory you add with
additionalDirectories/add_dirs(which the SDK passes to Claude Code as--add-dir).
"user"
~/.claude/settings.json,~/.claude/CLAUDE.mdand~/.claude/rules/*.md.- Skills, commands and subagents from
~/.claude/skills/,~/.claude/commands/and~/.claude/agents/.
"local"
<cwd>/.claude/settings.local.json.CLAUDE.local.mdin<cwd>and every parent directory.
What settingSources does not cover
These are read whatever you pass, which matters if you are relying on settingSources: [] for isolation.
| Input | When it loads | How to switch it off |
|---|---|---|
| Managed policy settings | Endpoint policy (MDM plist, registry policy or managed settings file) loads from the host. Server-managed settings are fetched on eligible setups when the session authenticates with a qualifying credential: an organisation OAuth login, a directly configured API key, or a user_oauth Anthropic profile. | Endpoint policy: remove it from the host. Server-managed settings: controlled by an Owner in your Claude organisation; you cannot turn them off from the SDK. |
~/.claude.json global config | Always | Point CLAUDE_CONFIG_DIR elsewhere through env |
Auto memory in ~/.claude/projects/<project>/memory/ | Added to the system prompt at session start. The agent saves new memories using the ordinary Write and Edit tools, so those must be enabled for saving to work. | autoMemoryEnabled: false in settings, or CLAUDE_CODE_DISABLE_AUTO_MEMORY=1 in env |
| claude.ai MCP connectors | When the session authenticates with a claude.ai login. Not when CLAUDE_CODE_OAUTH_TOKEN holds a claude setup-token token, which can only make model requests. Passing mcpServers: {} does not stop them. | strictMcpConfig: true, disableClaudeAiConnectors: true in settings, or ENABLE_CLAUDEAI_MCP_SERVERS=false in env |
sandbox.credentials deny entries and file mask entries in ~/.claude/settings.json | Applied whenever the command sandbox runs, even if user settings are excluded. They only ever narrow what sandboxed commands can reach. | Remove them from ~/.claude/settings.json |
Warning: Default options are not a multi-tenant isolation boundary. Because of the inputs above, an SDK process can absorb host-level configuration and per-directory memory. For multi-tenant services, give each tenant its own filesystem, pass
settingSources: []and setCLAUDE_CODE_DISABLE_AUTO_MEMORY=1inenv. Server-managed settings still arrive if the process authenticates with an organisation credential. Secure deployment covers the rest.
CLAUDE.md and rules
CLAUDE.md and .claude/rules/*.md files give the agent standing context: build commands, naming conventions, architecture decisions, things never to touch. Load the right source and the agent follows them without being told in every prompt.
| Where | Files | Needs source | Loaded |
|---|---|---|---|
| Project root | <cwd>/CLAUDE.md or <cwd>/.claude/CLAUDE.md | "project" | Session start |
| Project rules | .claude/rules/*.md in <cwd> and every parent | "project" | Session start |
| Parent folders | CLAUDE.md above <cwd> | "project" | Session start |
| Subfolders | CLAUDE.md below <cwd> | "project" | On demand, when Claude works in that folder |
| Local | CLAUDE.local.md in <cwd> and every parent | "local" | Session start |
| User | ~/.claude/CLAUDE.md | "user" | Session start |
| User rules | ~/.claude/rules/*.md | "user" | Session start |
Everything is additive. If a user file and a project file disagree, there is no formal winner: Claude reads both and interprets them. Avoid conflicts, or say explicitly in the more specific file which one wins, for example "Where these project rules disagree with personal defaults, follow these."
If you do not need the same instructions shared with interactive sessions, you can skip CLAUDE.md and put context straight into the system prompt; see modifying system prompts. For writing good CLAUDE.md files, see memory.
Skills
Skills are Markdown files with specialist knowledge or a repeatable workflow. Unlike CLAUDE.md, they are not loaded in full every session: the agent sees a short description at startup and pulls in the body only when it is relevant.
Skills are found on disk through settingSources. The skills option then controls which are enabled:
skills value | Effect |
|---|---|
| omitted | Discovered user and project skills are on, and the Skill tool is available (same as the CLI) |
"all" | Every discovered skill is on |
["release-notes", "pr-review"] | Only the named skills are on |
[] | All skills off |
When you set skills, the SDK adds Skill to allowedTools for you. If you also give an explicit tools list, include "Skill" in it, or Claude cannot call skills.
opts = ClaudeAgentOptions(
setting_sources=["project"],
skills=["release-notes"],
allowed_tools=["Read", "Grep", "Bash"],
)
Note: There is no API for registering a skill in code. A skill is always a file at
.claude/skills/<name>/SKILL.md. SDK skills has the details.
Hooks
You can define hooks in two ways, and both run together:
- Filesystem hooks in
settings.json, loaded throughsettingSources. These are the same hooks you would write for the CLI; see the hooks guide. - Programmatic hooks: callback functions passed in the
hooksoption. They run in your process and return structured decisions; see SDK hooks.
A callback that returns an empty object lets the tool proceed. To stop it, return hookSpecificOutput with permissionDecision: "deny" and a permissionDecisionReason, which Claude receives as the tool result.
A Python example that keeps an agent out of production config, alongside whatever hooks the project already defines:
from claude_agent_sdk import ClaudeAgentOptions, HookMatcher
async def guard_prod_config(input_data, tool_use_id, context):
path = input_data.get("tool_input", {}).get("file_path", "")
if "/config/production" in path:
return {
"hookSpecificOutput": {
"hookEventName": "PreToolUse",
"permissionDecision": "deny",
"permissionDecisionReason": "Production config is managed by the platform team.",
}
}
return {}
opts = ClaudeAgentOptions(
setting_sources=["project"],
hooks={"PreToolUse": [HookMatcher(matcher="Edit|Write", hooks=[guard_prod_config])]},
)
The same in TypeScript, narrowing on the event name to get the right input type:
import type { HookInput, HookJSONOutput } from "@anthropic-ai/claude-agent-sdk";
const guardProdConfig = async (input: HookInput): Promise<HookJSONOutput> => {
if (input.hook_event_name !== "PreToolUse") return {};
const { file_path } = input.tool_input as { file_path?: string };
if (file_path?.includes("/config/production")) {
return {
hookSpecificOutput: {
hookEventName: "PreToolUse",
permissionDecision: "deny",
permissionDecisionReason: "Production config is managed by the platform team.",
},
};
}
return {};
};
Filesystem or programmatic?
| Kind | Good for | Notes |
|---|---|---|
Filesystem (settings.json) | Hooks shared between CLI users and SDK agents | Types: command, http, mcp_tool, prompt and agent. Fire in the main agent and its subagents. |
| Programmatic (callbacks) | Application logic and in-process integration | Also fire inside subagents. The input includes agent_id and agent_type so you can tell which agent triggered it. |
TypeScript supports some events Python does not yet have, including SessionStart, SessionEnd, TeammateIdle and TaskCompleted. The hooks reference documents the filesystem syntax.
Picking the right feature
| You want to | Use | How in the SDK |
|---|---|---|
| Apply project conventions on every run | CLAUDE.md | settingSources: ["project"] |
| Give reference material Claude reads when relevant | Skills | settingSources plus skills |
| Package a repeatable workflow (release, review, deploy) | User-invocable skills | settingSources plus skills |
| Send a self-contained subtask to a fresh context | Subagents | agents option, with Agent allowed |
| Coordinate several Claude Code instances with shared tasks and messaging | Agent teams | Not an SDK option; a CLI feature where one session leads |
| Run deterministic checks on tool calls | Hooks | hooks callbacks, or shell hooks via settingSources |
| Give structured access to an external service | MCP | mcpServers |
Everything you switch on takes up some context. The features overview breaks down the cost of each.