---
title: "Building Autonomous Workflows with the Claude Agent SDK"
url: "https://vanducng.dev/2026/08/09/building-autonomous-workflows-with-the-claude-agent-sdk/"
date: "2026-08-09"
series: "agentic-coding-with-claude-code"
categories: ["AI"]
tags: ["agentic-coding","claude-code","claude-agent-sdk","ai-agents"]
---

# Building Autonomous Workflows with the Claude Agent SDK

Claude Code is the interactive experience. The Claude Agent SDK is the programmable interface to the same agent loop, tools, and context management.

That distinction changes what we can build. Instead of opening a terminal and guiding one task by hand, we can place the agent inside a product, internal service, CI job, or document pipeline. Our application defines the goal and operational boundary. Claude decides which permitted actions to take, observes the results, and continues until the task is complete.

This is Part 2 of my **Agentic Coding with Claude Code** series. We will move from the concepts in Part 1 to the SDK primitives that make an agent work: `query()`, message streams, sessions, tools, and permissions.

## Claude Agent SDK vs Claude Code

The SDK and Claude Code share the same underlying capabilities, but they serve different interfaces.

| Interface | Best fit | Who drives the interaction? |
| --- | --- | --- |
| **Claude Code CLI** | Interactive development and one-off terminal tasks | A person works with Claude directly |
| **Claude Agent SDK** | Agents embedded in applications and automated processes | Application code starts, observes, and controls the run |

The [Agent SDK](https://docs.claude.com/en/docs/agent-sdk/overview) is available for TypeScript and Python. It runs the agent loop in our process, so we do not have to implement the cycle of model calls, tool execution, and tool-result feedback ourselves.

This is also where a useful distinction appears. A **workflow** follows orchestration that our code defines. An **agent** dynamically chooses its next action and tool. A production system often combines both: our application owns the reliable outer workflow, while Claude owns the decisions inside a bounded agentic step.

## `query()` Is the Entry Point

The main [TypeScript SDK](https://docs.claude.com/en/api/agent-sdk/typescript) entry point is `query()`:

```ts
query({
  prompt: string | AsyncIterable<SDKUserMessage>,
  options?: Options,
}): Query
```

The call has two important inputs:

- **`prompt`** describes the goal in natural language
- **`options`** configures the environment, including the model, working directory, tools, sessions, limits, and permission behavior

Unlike a conventional request that resolves to one response, `query()` returns a `Query`, an async generator of SDK messages. The application can observe assistant messages, tool calls, tool results, lifecycle events, and the final result as the agent works.

That stream provides operational visibility. It does not expose a transcript of Claude's hidden chain-of-thought. We should build interfaces around observable actions, concise progress, results, cost, and errors instead.

## A Minimal Read-Only Agent

Here is a complete TypeScript example that asks Claude to summarize one file:

```ts
import { query } from "@anthropic-ai/claude-agent-sdk";

try {
  for await (const message of query({
    prompt: "Read api-guide.md and summarize its key decisions and examples.",
    options: {
      allowedTools: ["Read(./api-guide.md)"],
      permissionMode: "dontAsk",
      maxTurns: 3,
      maxBudgetUsd: 0.25,
    },
  })) {
    if (message.type !== "result") {
      continue;
    }
    if (message.subtype !== "success") {
      throw new Error(`Agent ended with ${message.subtype}`);
    }
    console.log(message.result);
  }
} catch (error) {
  console.error("Agent failed", error);
  process.exitCode = 1;
}
```

The prompt defines the outcome. The scoped rule auto-approves `Read` only for `api-guide.md`, and `dontAsk` denies anything that was not pre-approved instead of prompting. The turn and cost limits bound the run, while the `catch` handles SDK failures. Tune those limits for the size of the real document. Because the file path is known, the agent does not need `Glob`, `Grep`, `Write`, or `Bash`.

There is a subtle but important rule here: **`allowedTools` is an approval list, not a complete capability boundary.** Unlisted tools can still fall through to the active permission mode. Pairing a narrow `allowedTools` list with `dontAsk`, or explicitly blocking tools with `disallowedTools`, creates the predictable boundary that headless automation needs.

## A Sketchnote of the SDK

<img src="https://vanducng.dev/2026/08/09/building-autonomous-workflows-with-the-claude-agent-sdk/claude-agent-sdk-workflow-sketch-note.png" alt="Sketchnote of a prompt and options entering the Claude Agent SDK query method, its streamed messages, perceive-reason-act loop, tools, and least-privilege permission boundary" title="Claude Agent SDK workflow sketchnote" width="1600" height="900">

The application starts the run and consumes its message stream. Inside that boundary, the SDK keeps feeding tool results back to Claude until the goal is complete or a configured limit stops the run.

## How the SDK Runs the Agentic Loop

Suppose the goal is:

```text
Summarize api-guide.md.
```

The loop can unfold like this:

1. **Perception**: Claude receives the goal and sees that `Read` is available.
2. **Reasoning**: It determines that it needs the document before it can summarize it.
3. **Action**: It calls `Read` for `api-guide.md`.
4. **Perception**: The file content returns as a tool result.
5. **Reasoning**: Claude identifies the important decisions, examples, and structure.
6. **Action**: It returns the summary as the final result.

For a larger task, this cycle can repeat many times within one `query()` call. The SDK handles the model-tool-model exchange and streams each observable step back to the application.

## Tools Define What the Agent Can Do

Tools turn the model's decisions into grounded actions.

| Category | Common tools | Capability |
| --- | --- | --- |
| **Observe** | `Read`, `Glob`, `Grep` | Inspect files and search the workspace |
| **Modify** | `Edit`, `Write` | Change or create files |
| **Execute** | `Bash` | Run commands and programs |
| **Web** | `WebSearch`, `WebFetch` | Search for and retrieve online information |
| **Extend** | `Agent`, `Skill`, MCP tools | Delegate focused work or load specialized capabilities |

The current SDK calls the subagent tool `Agent`; `Task` remains accepted as a compatibility alias. Skills can be made available with the `skills` option, while MCP connects tools and data sources that the built-in set does not cover.

Tool names are not a security model by themselves. A safe deployment combines a narrow tool surface with permission rules, filesystem or process isolation, hooks where every call must be checked, and application-level validation of the final result.

## Permission Modes Set the Autonomy Level

[Permission modes](https://docs.claude.com/en/docs/agent-sdk/permissions) determine what happens when Claude requests a tool.

| Mode | Behavior | Typical use |
| --- | --- | --- |
| **`default`** | Unmatched tools reach the application's approval callback | Interactive approval flows |
| **`dontAsk`** | Anything not pre-approved is denied | Fixed, headless agents |
| **`acceptEdits`** | File edits and common filesystem operations are auto-approved | Trusted work in an isolated directory |
| **`plan`** | Claude explores and plans while write operations require approval | Analysis before implementation |
| **`auto`** | A model classifier decides permission prompts where available | Environments that support classified approvals |
| **`bypassPermissions`** | Most tool calls run without prompts | Fully isolated, trusted automation only |

`bypassPermissions` requires an additional explicit opt-in and gives Claude broad system access. Even then, hooks and deny rules can still block calls. It should be the exception, not the default.

The practical least-privilege sequence is:

1. Start with the exact tools the task requires.
2. Deny or omit capabilities that should never be used.
3. Choose the most restrictive permission mode that still lets the task finish.
4. Add sandboxing and hooks when the environment needs stronger enforcement.
5. Expand authority only after a real workflow proves that it needs more.

## Sessions Carry Context Across Queries

A single `query()` can take many agentic turns. [Sessions](https://docs.claude.com/en/docs/agent-sdk/sessions) matter when separate queries need to share context.

The TypeScript SDK supports three useful paths:

- **`continue: true`** resumes the most recent session for the current directory and fits apps that run one conversation at a time there
- **`resume: sessionId`** returns to a specific session
- **`resume: sessionId` with `forkSession: true`** branches the conversation into a new session while preserving the original

Do not use `continue: true` to identify state in a concurrent or multi-user service. Capture the session ID from each run and pass the correct ID through `resume` for that user or job.

A session contains prompts, responses, tool calls, and tool results. It does not snapshot the filesystem. If two resumed or forked sessions operate in the same directory, they still see the same real files unless we isolate or checkpoint them separately.

That difference matters in production. Conversation state helps Claude remember what it learned. Application state must still own durable business facts, authorization, idempotency, and the status of external work.

## From One Query to a Real Workflow

A document-standardization process shows how the pieces combine. Imagine a directory of contracts that must be converted into a consistent internal format.

The outer workflow can:

1. Place copies of the source documents in an isolated working directory.
2. Start `query()` with a precise output schema and validation rules.
3. Allow read tools for the inputs and write access only to a dedicated output directory.
4. Stream progress and tool activity into the application's audit log.
5. Validate the generated documents before accepting them.
6. Route exceptions and final legal approval to a human reviewer.

Within that workflow, the agent can decide which document to inspect next, how to map inconsistent sections, and when it needs more context. The application still owns the non-negotiable sequence, permissions, validation, and approval boundary.

This is the pattern to aim for: **dynamic reasoning inside deterministic controls.**

## Apply Your Skills

Pick a repetitive process from your work, such as compiling a weekly report, preparing release notes, or onboarding a teammate. Write down:

1. What outcome should one `query()` own?
2. Which tools are strictly necessary?
3. Which actions must be denied, sandboxed, or approved?
4. What messages should the application surface to a human?
5. What belongs in the SDK session, and what must live in application storage?
6. Which validations prove that the final result is safe to use?

Then implement the smallest read-only version first. Add modification or execution tools only when the workflow has a concrete need for them.

## Key Terms

- **Claude Agent SDK**: The TypeScript and Python library that exposes Claude Code's agent loop, tools, and context management to applications
- **`query()`**: The primary SDK function that starts an agent run and returns an asynchronous message stream
- **`Query`**: The async generator returned by `query()`, with streamed SDK messages and controls for the active run
- **Agentic loop**: The repeating perception, reasoning, and action cycle managed by the SDK
- **Tool**: A capability Claude can call to observe or change its environment
- **Permission mode**: The global policy that controls how unmatched tool requests are approved, denied, or escalated
- **Least privilege**: Giving an agent only the authority required for its current goal
- **Session**: Persisted conversation history containing prompts, messages, tool calls, and tool results
- **Resume**: Continue a specific prior session by its ID
- **Fork**: Create a new session from an existing history without changing the original conversation

## Final Thoughts

The Claude Agent SDK turns the interaction model of Claude Code into a programmable building block. `query()` gives us a goal-driven agent loop, tools give it capabilities, the message stream gives us observability, sessions give it continuity, and permissions define its operating boundary.

The strongest systems do not maximize autonomy. They give Claude enough room to reason and adapt inside controls that remain understandable, testable, and owned by the application.

The next post will go deeper into context engineering: how project instructions, scoped rules, skills, and tool descriptions shape the decisions an agent makes.

---

*This is Part 2 of the Agentic Coding with Claude Code series.*
