Claude Code SDK for Dart
A powerful Dart SDK for interacting with Claude Code, providing seamless integration with AI-powered coding assistance through the Claude Code CLI.
Features
- ๐ Easy Integration: Simple API for creating chat sessions with Claude
- ๐ File Support: Send files along with text prompts for context-aware responses
- ๐พ Bytes Support: Send in-memory data as temporary files (auto-cleanup on dispose)
- ๐ Schema Support: Get structured responses using JSON schemas
- ๐ Session Management: Automatic conversation continuity with --resume flag
- ๐ ๏ธ Auto-Installation: Built-in methods to check and install Claude Code SDK
- ๐งน Resource Management: Proper cleanup and disposal of chat sessions and temp files
- ๐ Secure: API key management with environment variable support
- โก Reliable: Simple Process.run based implementation (no streaming complexity)
Prerequisites
Before using this SDK, you need:
-
Node.js and npm (for Claude Code CLI)
- Download from nodejs.org
-
Claude Code CLI
- Install globally:
npm install -g @anthropic-ai/claude-code
- Or use the SDK's built-in installer (see below)
- Install globally:
-
Anthropic API Key
- Get your API key from Anthropic Console
Installation
Add this to your package's pubspec.yaml
file:
dependencies:
claude_code_sdk: ^1.0.0
Then run:
dart pub get
Quick Start
Basic Usage
import 'package:claude_code_sdk/claude_code_sdk.dart';
void main() async {
// Initialize the SDK with your API key
final claudeSDK = Claude('YOUR_API_KEY');
// Create a new chat session
final claudeChat = claudeSDK.createNewChat();
try {
// Send a simple text message
final result = await claudeChat.sendMessage([
ClaudeSdkContent.text('What is the capital of France?'),
]);
print('Claude says: $result');
} finally {
// Always dispose of the chat when done
await claudeChat.dispose();
}
}
Sending Files with Messages
import 'dart:io';
import 'package:claude_code_sdk/claude_code_sdk.dart';
void main() async {
final claudeSDK = Claude('YOUR_API_KEY');
final claudeChat = claudeSDK.createNewChat();
try {
// Send a message with a file
final result = await claudeChat.sendMessage([
ClaudeSdkContent.text('Please analyze this HTML file and extract the user name'),
ClaudeSdkContent.file(File('example.html')),
]);
print('Analysis result: $result');
} finally {
await claudeChat.dispose();
}
}
Sending Bytes as Temporary Files
You can send in-memory data (like images, documents, or any binary data) without creating permanent files:
import 'dart:typed_data';
import 'package:claude_code_sdk/claude_code_sdk.dart';
void main() async {
final claudeSDK = Claude('YOUR_API_KEY');
final claudeChat = claudeSDK.createNewChat();
try {
// Example 1: Send image bytes
final imageBytes = await File('photo.jpg').readAsBytes();
final result = await claudeChat.sendMessage([
ClaudeSdkContent.text('What is in this image?'),
ClaudeSdkContent.bytes(
data: imageBytes,
fileExtension: 'jpg',
),
]);
// Example 2: Send text as bytes
final textContent = 'Hello, this is dynamic content!';
final textBytes = Uint8List.fromList(textContent.codeUnits);
final result2 = await claudeChat.sendMessage([
ClaudeSdkContent.text('Read this text:'),
ClaudeSdkContent.bytes(
data: textBytes,
fileExtension: 'txt',
),
]);
print('Result: $result2');
} finally {
// Temporary files are automatically deleted when disposed
await claudeChat.dispose();
}
}
Note: Temporary files created from bytes are automatically cleaned up when you call dispose()
on the chat session.
Using Schemas for Structured Responses
import 'dart:io';
import 'package:claude_code_sdk/claude_code_sdk.dart';
void main() async {
final claudeSDK = Claude('YOUR_API_KEY');
final claudeChat = claudeSDK.createNewChat();
try {
// Define a schema with nullable properties
final schema = SchemaObject(
properties: {
'userName': SchemaProperty.string(
description: 'The name of the user found in the HTML',
nullable: false, // Required field
),
'userEmail': SchemaProperty.string(
description: 'The email of the user if found',
nullable: true, // Optional field
),
'userRole': SchemaProperty.string(
description: 'The role or title of the user',
nullable: true, // Optional field
),
},
// No need to specify 'required' array - it's automatically derived from nullable properties
);
// Send message with schema
final result = await claudeChat.sendMessageWithSchema(
messages: [
ClaudeSdkContent.text('Extract user information from this HTML file'),
ClaudeSdkContent.file(File('profile.html')),
],
schema: schema,
);
print('Model message: ${result.modelMessage}');
print('Extracted data: ${result.data}');
// Access specific fields
final userName = result.data['userName'];
print('User name: $userName');
} finally {
await claudeChat.dispose();
}
}
Streaming Responses
import 'package:claude_code_sdk/claude_code_sdk.dart';
void main() async {
final claudeSDK = Claude('YOUR_API_KEY');
final claudeChat = claudeSDK.createNewChat(
options: ClaudeChatOptions(
streamJson: true,
),
);
try {
// Stream the response
await for (final chunk in claudeChat.streamResponse([
ClaudeSdkContent.text('Write a detailed explanation of quantum computing'),
])) {
print(chunk); // Print each chunk as it arrives
}
} finally {
await claudeChat.dispose();
}
}
Advanced Configuration
Chat Options
final claudeChat = claudeSDK.createNewChat(
options: ClaudeChatOptions(
systemPrompt: 'You are a helpful coding assistant',
maxTurns: 5,
allowedTools: ['Read', 'Write', 'Bash'],
permissionMode: 'acceptEdits',
cwd: '/path/to/project',
model: 'claude-3.5-sonnet',
outputJson: true,
timeoutMs: 30000,
),
);
Checking and Installing Claude Code SDK
void main() async {
final claudeSDK = Claude('YOUR_API_KEY');
// Check if Claude Code SDK is installed
final isInstalled = await claudeSDK.isClaudeCodeSDKInstalled();
if (!isInstalled) {
print('Claude Code SDK is not installed. Installing...');
try {
// Install the SDK globally
await claudeSDK.installClaudeCodeSDK(global: true);
print('Installation complete!');
} catch (e) {
print('Installation failed: $e');
}
}
// Get SDK information
final info = await claudeSDK.getSDKInfo();
print('SDK Info: $info');
}
Schema Building
The SDK provides convenient factory methods for building schemas with nullable control:
final schema = SchemaObject(
properties: {
'name': SchemaProperty.string(
description: 'User name',
defaultValue: 'Anonymous',
nullable: false, // Required field
),
'age': SchemaProperty.number(
description: 'User age',
nullable: false, // Required field
),
'email': SchemaProperty.string(
description: 'User email',
nullable: true, // Optional field (default)
),
'isActive': SchemaProperty.boolean(
description: 'Whether the user is active',
defaultValue: true,
nullable: false, // Required with default value
),
'tags': SchemaProperty.array(
items: SchemaProperty.string(),
description: 'List of tags',
nullable: true, // Optional array
),
'metadata': SchemaProperty.object(
properties: {
'created': SchemaProperty.string(nullable: false),
'updated': SchemaProperty.string(nullable: true),
},
description: 'Metadata object',
nullable: true, // Optional nested object
),
},
// The 'required' array is automatically generated from nullable: false properties
description: 'User information schema',
);
Nullable Property Behavior
nullable: false
- The property is required and must be present in the responsenullable: true
(default) - The property is optional and may be omitted or null- Properties with
nullable: false
are automatically added to the JSON schema'srequired
array - You can still use the legacy
required
parameter on SchemaObject for backward compatibility
Error Handling
The SDK provides specific exception types for different error scenarios:
import 'package:claude_code_sdk/claude_code_sdk.dart';
void main() async {
final claudeSDK = Claude('YOUR_API_KEY');
final claudeChat = claudeSDK.createNewChat();
try {
final result = await claudeChat.sendMessage([
ClaudeSdkContent.text('Hello, Claude!'),
]);
print(result);
} on CLINotFoundException {
print('Claude Code CLI is not installed. Please install it first.');
} on ProcessException catch (e) {
print('Process error: ${e.message}');
if (e.exitCode != null) {
print('Exit code: ${e.exitCode}');
}
} on JSONDecodeException catch (e) {
print('Failed to parse response: ${e.message}');
} on ClaudeSDKException catch (e) {
print('SDK error: ${e.message}');
} finally {
await claudeChat.dispose();
}
}
Implementation Details
This SDK uses a simple and reliable approach:
- Process.run: Each message is sent as a separate process call (no streaming complexity)
- Session Management: Uses Claude CLI's
--resume
flag for conversation continuity - JSON Output: Always uses
--output-format json
for consistent parsing - Automatic Fallback: Tries
claude
command first, falls back toclaude-code
if needed
Resource Management
Important: Always Dispose Chat Sessions
Always dispose of chat sessions when done to ensure proper cleanup:
// Method 1: Using try-finally
final chat = claudeSDK.createNewChat();
try {
// Use the chat
await chat.sendMessage([...]);
} finally {
await chat.dispose();
}
// Method 2: Dispose all sessions at once
await claudeSDK.dispose(); // Disposes all active sessions
API Reference
Claude Class
Claude(String apiKey)
- Creates a new SDK instancecreateNewChat({ClaudeChatOptions? options})
- Creates a new chat sessionisClaudeCodeSDKInstalled()
- Checks if Claude Code CLI is installedinstallClaudeCodeSDK({bool global = true})
- Installs the Claude Code SDKgetSDKInfo()
- Gets information about installed SDKsdispose()
- Disposes all active chat sessions
ClaudeChat Class
sendMessage(List<ClaudeSdkContent> contents)
- Sends a message and returns the responsesendMessageWithSchema({messages, schema})
- Sends a message with a schema for structured responseget sessionId
- Gets the current session ID (null until first message)resetConversation()
- Resets the conversation, starting a new sessiondispose()
- Disposes the chat session and cleans up resources (including temp files)
ClaudeSdkContent
ClaudeSdkContent.text(String text)
- Creates text contentClaudeSdkContent.file(File file)
- Creates file contentClaudeSdkContent.bytes({data, fileExtension})
- Creates content from bytes (temporary file)
Environment Variables
You can also set your API key as an environment variable:
export ANTHROPIC_API_KEY="your-api-key-here"
Then use it in your code:
final apiKey = Platform.environment['ANTHROPIC_API_KEY'] ?? '';
final claudeSDK = Claude(apiKey);
Troubleshooting
Claude Code CLI not found
If you get a CLINotFoundException
, make sure Claude Code is installed:
npm install -g @anthropic-ai/claude-code
Or use the SDK's built-in installer:
await claudeSDK.installClaudeCodeSDK();
Permission Errors
On Unix-like systems, you might need to use sudo
for global npm installations:
sudo npm install -g @anthropic-ai/claude-code
Process Cleanup
Always dispose of chat sessions to prevent resource leaks:
await claudeChat.dispose();
// or
await claudeSDK.dispose(); // Disposes all sessions
Examples
Check the example/
directory for more comprehensive examples:
example/basic_usage.dart
- Simple text messagingexample/file_analysis.dart
- Analyzing files with Claudeexample/schema_example.dart
- Using schemas for structured responsesexample/streaming_example.dart
- Streaming responsesexample/installation_check.dart
- Checking and installing dependencies
Contributing
Contributions are welcome! Please feel free to submit a Pull Request.
License
This project is licensed under the MIT License - see the LICENSE file for details.
Support
For issues and questions:
- Open an issue on GitHub
- Check the Anthropic documentation
- Visit the Claude Code documentation
Acknowledgments
- Built on top of the official Claude Code CLI by Anthropic
- Inspired by the Python and TypeScript SDKs
Libraries
- claude_code_sdk
- Claude Code SDK for Dart