claude_agent_sdk 0.1.0
claude_agent_sdk: ^0.1.0 copied to clipboard
A typed Dart SDK for building agents with the Claude Code CLI.
claude_agent_sdk
A typed Dart SDK for building agents on top of the Claude Code CLI.
Overview #
claude_agent_sdk launches an installed Claude Code CLI and exposes one-shot
queries, interactive sessions, typed stream messages, runtime permissions,
hooks, programmatic subagents, user-dialog callbacks, settings resolution,
in-process MCP tools, and persistent session APIs.
The package does not bundle Claude Code or an API client. Install and
authenticate the claude CLI before running a live agent.
Install #
dart pub add claude_agent_sdk
The package requires Dart 3.10 or newer and Claude Code 2.0 or newer.
Quick Start #
import 'dart:io';
import 'package:claude_agent_sdk/claude_agent_sdk.dart';
Future<void> main() async {
await for (final message in query(
'Explain the architecture of this repository.',
options: ClaudeAgentOptions(
workingDirectory: Directory.current.path,
allowedTools: const ['Read', 'Glob', 'Grep'],
permissionMode: PermissionMode.plan,
),
)) {
if (message case AssistantMessage(:final content)) {
for (final block in content.whereType<TextBlock>()) {
stdout.write(block.text);
}
}
}
}
Interactive client #
final client = ClaudeAgentClient(
options: ClaudeAgentOptions(
systemPrompt: const SystemPrompt.claudeCode(
append: 'Keep answers concise.',
),
),
);
await client.connect();
try {
await client.send('Review the current implementation.');
await for (final message in client.receiveResponse()) {
// The stream includes the terminal ResultMessage.
}
await client.setPermissionMode(PermissionMode.acceptEdits);
await client.send('Apply the fixes you found.');
await client.receiveResponse().drain<void>();
} finally {
await client.disconnect();
}
Pre-warmed query #
Pay process startup and initialization cost before a latency-sensitive prompt. A warm handle accepts exactly one prompt and closes itself after the result:
final warm = await startup(options: ClaudeAgentOptions(model: 'sonnet'));
await for (final message in warm.query('Summarize the current diff.')) {
// The process was already initialized before query() was called.
}
Subagents #
Define subagents in agents and optionally select one as the main-thread
agent. Claude invokes a subagent through its Agent tool based on the
description. forwardSubagentText exposes its complete nested transcript;
otherwise the runtime forwards only tool activity.
final options = ClaudeAgentOptions(
agent: 'coordinator',
agents: {
'coordinator': AgentDefinition(
description: 'Coordinates implementation work.',
prompt: 'Delegate focused research and verification tasks.',
tools: const ['Read', 'Glob', 'Grep', 'Agent'],
),
'test-runner': AgentDefinition(
description: 'Runs tests and diagnoses failures.',
prompt: 'Run the smallest relevant test suite and report evidence.',
tools: const ['Read', 'Glob', 'Grep', 'Bash'],
model: 'haiku',
maxTurns: 8,
runsInBackground: true,
),
},
forwardSubagentText: true,
agentProgressSummaries: true,
);
Subagent assistant and user messages carry parentToolUseId,
subagentType, and taskDescription when supplied by the runtime. Use
listSubagents / getSubagentMessages (or their SessionStore variants) to
read persisted nested conversations. supportedAgents() discovers the
effective programmatic, filesystem, plugin, and built-in agent set.
Permissions, hooks, and SDK MCP #
canUseTool receives interactive permission decisions over the control
channel. Hooks are typed by lifecycle event. SdkMcpServer exposes local Dart
functions as MCP tools without starting another process.
final options = ClaudeAgentOptions(
canUseTool: (toolName, input, context) async {
if (toolName == 'Bash') {
return const PermissionDenied(message: 'Shell access is disabled.');
}
return PermissionAllowed();
},
hooks: {
HookEvent.preToolUse: [
HookMatcher(
matcher: 'Write|Edit',
hooks: [
(input, toolUseId, context) async =>
const HookOutput(systemMessage: 'Edit audited by Dart.'),
],
),
],
},
mcp: McpServers({
'local': SdkMcpServer(
name: 'local',
tools: [
SdkMcpTool(
name: 'lookup',
description: 'Looks up an application value.',
inputSchema: {
'type': 'object',
'properties': <String, Object?>{},
},
handler: (input) async => McpToolResult(
content: const [McpTextContent('value')],
),
),
],
),
}),
);
Session persistence #
Local session helpers read the same JSONL transcripts as Claude Code:
final sessions = listSessions(directory: Directory.current.path);
final messages = getSessionMessages(
sessions.first.sessionId,
directory: Directory.current.path,
);
Use SessionStore to mirror transcripts into a database or object store.
InMemorySessionStore is the reference implementation. Store-backed resume
materializes a temporary Claude config tree, copies only the authentication
state needed by the subprocess, removes OAuth refresh tokens from copied
credentials, and cleans the tree after disconnect.
Supported Features #
| Area | Dart API |
|---|---|
| One-shot | query, queryStream, pre-warmed startup / ClaudeWarmQuery |
| Interactive | ClaudeAgentClient, UserInput |
| Messages | AssistantMessage, UserMessage, ResultMessage, system/task/rate-limit events, raw-preserving envelopes |
| Control | initialization/reinitialization, interrupt/queue cancellation, permission/model switching, file read/rewind, dynamic MCP, plugin/skill reload, task stop/background, usage and context status |
| Interaction | typed permission and elicitation callbacks with cancellation |
| Extensibility | typed hooks, programmatic/main-thread agents, skills/plugins, tool aliases, canUseTool, stdio/SSE/HTTP MCP config, SdkMcpServer |
| Sessions | local list/read/rename/tag/fork/delete, optional system history, nested subagent ancestry/transcripts, and corresponding SessionStore APIs |
| Runtime | CLI discovery/execution/logout and provenance-aware tiered settings resolution |
Unknown top-level messages and content blocks are preserved as
UnknownAgentMessage and UnknownContentBlock, keeping newer CLI output
forward-compatible.
Model discovery exposes effort, adaptive-thinking, Fast-mode, and auto-mode
capabilities. UserMessage.toolResultMetadata provides the runtime's
per-tool reason and optional feedback when a denied, interrupted, or cancelled
tool never executed. Hook callbacks receive a ControlCallbackContext; its
cancellation signal is triggered when the runtime cancels the pending hook.
UserInput accepts optional uuid and priority fields for transports that
need turn identity or immediate steering. Message envelopes retain the raw
wire payload alongside the typed message when protocol extensions must be
inspected without losing forward compatibility.
Examples and verification #
dart run example/quick_start.dart
dart run example/subagents.dart
CLAUDE_AGENT_SDK_LIVE_TEST=1 dart run example/quick_start.dart
dart test
dart analyze .
dart pub publish --dry-run
See example/README.md and benchmark/README.md.
Security #
Agents can read files, execute tools, and call external services according to their configuration. Read SECURITY.md before enabling tools in an application that processes untrusted input.
Benchmarks #
| Scenario | Runtime |
|---|---|
ToolUseBlock construction with nested immutable input |
4.57 µs |
Measured on the package's development host with Dart 3.12.0.
Contributing #
See CONTRIBUTING.md. Run formatting, analysis, tests, the example smoke tool, and the benchmark smoke tool before opening a change.
License #
Licensed under the Apache License 2.0.
