System Prompts in the Claude Agent SDK
Three ways to write system prompts: plain string, the Claude Code preset, and the preset with a custom append. How the invisible base prompt works and what your prompt actually controls.
What system prompts actually do in the SDK
Every Agent SDK deployment runs with an invisible system prompt underneath — Anthropic's layer that handles all the tool definitions, operational instructions, and behavioral guardrails. You never see it. It updates automatically when the SDK ships new features.
Your system prompt stacks on top of that layer. You add the instructions specific to your use case: the role, the output format, the constraints that make the agent yours. The tool infrastructure is already handled.
This is the meaningful difference from calling the raw Anthropic API, where you define everything from scratch. With the SDK, you only write what makes your agent unique.
Three ways to write a system prompt
1. Pass a string
The most common pattern. Write your instructions, pass them directly:
for await (const message of query(messages(), {
model: "claude-sonnet-4-5",
systemPrompt: `You are a pre-call research assistant for a fractional GTM leader.
When given a company name and meeting context, produce a structured brief covering:
- Company stage, recent news, and key signals
- The contact's role and likely priorities
- 3 questions worth asking in the first 15 minutes
Output in clean Markdown. Be direct. No filler.`,
permissionMode: "bypassPermissions",
dangerouslyAllowBypassPermissions: true,
})) {
// handle messages
}
The more specific the role and output format, the more reliable the agent. A generic "you are a helpful assistant" gets generic results.
You can also store the system prompt in a separate file and import it:
import { readFileSync } from "fs";
const systemPrompt = readFileSync("system-prompt.md", "utf-8");
Useful when your prompt grows long or when you want to version-control it separately from your agent code.
2. Use the Claude Code preset
For agents built around file operations and code work, reuse the same system prompt Anthropic uses for Claude Code:
for await (const message of query(messages(), {
model: "claude-sonnet-4-5",
systemPrompt: {
type: "preset",
preset: "claude_code",
},
permissionMode: "bypassPermissions",
dangerouslyAllowBypassPermissions: true,
})) {
// handle messages
}
The full Claude Code prompt is private — Anthropic does not expose the text. But it is the same one that powers the Claude Code product, which means it is optimized for file operations, code reasoning, and agentic software work.
3. Combine the preset with a custom append
Start from the Claude Code base and layer your own context on top:
for await (const message of query(messages(), {
model: "claude-sonnet-4-5",
systemPrompt: {
type: "preset",
preset: "claude_code",
append: `You are working inside a fractional consulting practice.
All deliverables should be formatted for a senior operator audience.
Reference client engagement timelines and deliverable deadlines when relevant.
Do not make recommendations that require more than 48 hours to implement.`,
},
permissionMode: "bypassPermissions",
dangerouslyAllowBypassPermissions: true,
})) {
// handle messages
}
The append text gets added to the end of the Claude Code preset. Use this when building file-manipulation or research agents that also need your operator context.
When to use the preset: If you are building a coding or file-manipulation agent, the preset saves you from writing the tool-use instructions yourself. If you are building something with no connection to coding — a client briefing agent, a Slack assistant, a competitive research tool — write your own string prompt. The Claude Code preset has behaviors tuned for code work that create friction in other contexts.
Writing system prompts that produce reliable output
These patterns hold up across different operator agent types:
Define the role with context, not just a title. "You are a research assistant" starts the conversation. "You are a research assistant helping a fractional CMO prepare for board meetings with Series B-stage portfolio companies" ends up there faster. The context shapes how the agent frames and prioritizes every response.
Set output format expectations explicitly. If you need Markdown, say so. If you are deploying to Slack, specify Slack's markdown syntax (asterisks for bold, backticks for code). If the output goes directly into a client deliverable, say that — it raises the quality bar automatically.
Define what the agent must not do. Negative constraints prevent the most common failure modes. "Do not include information you cannot verify from the provided files" stops hallucinated data from ending up in a client brief. "Do not suggest changes that modify the client's existing data structure" prevents an automation from doing something irreversible.
Keep the scope narrow. A system prompt that describes five different roles creates an agent that does none of them reliably. One role, one output format, two or three firm constraints. That combination produces agents that do the same thing correctly every time.
---
Author: FractionalSkill