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.

4 min read
system prompt agent SDK claude_code preset agent instructions SDK systemPrompt option operator agent prompt

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

Keep Going

Ready to Start Building?

Pick the next step that matches where you are right now.

Tutorial
Claude Code Basics

Start with the terminal basics. A hands-on, step-by-step guide to your first 10 minutes with Claude Code.

Start the Tutorial
Guide
AI-Powered Workflows

Automate your client work. Learn how to connect AI tools into workflows that handle repetitive tasks for you.

Read the Guide
Community
Join the Community

Connect with other fractional leaders building with AI. Share workflows, get feedback, and learn from operators who are ahead of you.

Apply to Join