Gemini CLI SDK for Dart
A powerful Dart SDK for interacting with Google Gemini CLI, providing seamless integration with AI-powered coding assistance through the Gemini CLI.
Features
- π Easy Integration: Simple API for creating chat sessions with Gemini
- π 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
- π οΈ Auto-Installation: Built-in methods to check and install Gemini CLI
- π MCP Support: Full Model Context Protocol integration for connecting to external tools
- π§Ή 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)
- π Streaming: Support for streaming responses in real-time
Prerequisites
Before using this SDK, you need:
-
Node.js and npm (for Gemini CLI)
- Download from nodejs.org
-
Gemini CLI
- Install globally:
npm install -g @google/gemini-cli - Or via Homebrew:
brew install gemini-cli - Or use the SDK's built-in installer (see below)
- Install globally:
-
Gemini API Key
- Get your API key from Google AI Studio
- Free tier: 60 requests/min, 1,000 requests/day (with OAuth)
- Or use API key tier: 100 requests/day
Installation
Add this to your package's pubspec.yaml file:
dependencies:
gemini_cli_sdk: ^1.1.0
Then run:
dart pub get
Quick Start
Basic Usage
import 'package:gemini_cli_sdk/gemini_cli_sdk.dart';
void main() async {
// Initialize the SDK with your API key
final geminiSDK = GeminiSDK('YOUR_API_KEY');
// Create a new chat session
final geminiChat = geminiSDK.createNewChat();
try {
// Send a simple text message
final result = await geminiChat.sendMessage([
GeminiSdkContent.text('What is the capital of France?'),
]);
print('Gemini says: $result');
} finally {
// Always dispose of the chat when done
await geminiChat.dispose();
}
}
Sending Files with Messages
import 'dart:io';
import 'package:gemini_cli_sdk/gemini_cli_sdk.dart';
void main() async {
final geminiSDK = GeminiSDK('YOUR_API_KEY');
final geminiChat = geminiSDK.createNewChat();
try {
// Send a message with a file
final result = await geminiChat.sendMessage([
GeminiSdkContent.text('Please analyze this HTML file and extract the user name'),
GeminiSdkContent.file(File('example.html')),
]);
print('Analysis result: $result');
} finally {
await geminiChat.dispose();
}
}
Using Schemas for Structured Responses
import 'dart:io';
import 'package:gemini_cli_sdk/gemini_cli_sdk.dart';
void main() async {
final geminiSDK = GeminiSDK('YOUR_API_KEY');
final geminiChat = geminiSDK.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
),
},
);
// Send message with schema
final result = await geminiChat.sendMessageWithSchema(
messages: [
GeminiSdkContent.text('Extract user information from this HTML file'),
GeminiSdkContent.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 geminiChat.dispose();
}
}
Streaming Responses
import 'package:gemini_cli_sdk/gemini_cli_sdk.dart';
void main() async {
final geminiSDK = GeminiSDK('YOUR_API_KEY');
final geminiChat = geminiSDK.createNewChat();
try {
// Stream the response
await for (final chunk in geminiChat.streamResponse([
GeminiSdkContent.text('Write a detailed explanation of quantum computing'),
])) {
print(chunk); // Print each chunk as it arrives
}
} finally {
await geminiChat.dispose();
}
}
Advanced Configuration
System Prompts
The SDK supports custom system prompts that complement (not override) Gemini's default system prompt. This allows you to add additional context or instructions to guide the model's behavior:
final geminiChat = geminiSDK.createNewChat(
options: GeminiChatOptions(
systemPrompt: '''You are a senior software engineer with expertise in Dart.
Always consider best practices and performance implications.
Provide code examples when relevant.''',
repeatSystemPrompt: false, // Only include in first message (default)
),
);
System Prompt Options:
systemPrompt: Additional context/instructions to complement Gemini's default behaviorrepeatSystemPrompt: Whether to include the prompt in every message (default:false)false: Include only in the first message (maintains context through conversation)true: Include in every message (useful for strict formatting requirements)
Example with repeated system prompt:
final chat = geminiSDK.createNewChat(
options: GeminiChatOptions(
systemPrompt: 'Always respond with exactly 3 bullet points.',
repeatSystemPrompt: true, // Ensures format consistency across all messages
),
);
Chat Options
final geminiChat = geminiSDK.createNewChat(
options: GeminiChatOptions(
model: 'gemini-2.5-flash', // or 'gemini-2.5-pro'
systemPrompt: 'You are a helpful coding assistant',
repeatSystemPrompt: false, // Only in first message
maxTurns: 5,
allowedTools: ['Read', 'Write', 'Bash'],
permissionMode: 'acceptEdits',
cwd: '/path/to/project',
outputJson: true,
timeoutMs: 30000,
includeDirectories: true,
directories: ['/src', '/lib'],
nonInteractive: true,
),
);
Checking and Installing Gemini SDK
void main() async {
final geminiSDK = GeminiSDK('YOUR_API_KEY');
// Check if Gemini CLI is installed
final isInstalled = await geminiSDK.isGeminiCLIInstalled();
if (!isInstalled) {
print('Gemini CLI is not installed. Installing...');
try {
// Install the CLI globally
await geminiSDK.installGeminiCLI(global: true);
print('Installation complete!');
} catch (e) {
print('Installation failed: $e');
}
}
// Get SDK information
final info = await geminiSDK.getSDKInfo();
print('SDK Info: $info');
}
Auto-Update SDK
The SDK provides a convenient method to automatically check for and install updates:
void main() async {
final geminiSDK = GeminiSDK('YOUR_API_KEY');
// Automatically check for updates and install if needed
await geminiSDK.updateToNewestVersionIfNeeded(global: true);
// The function will:
// 1. Check if CLI is installed (installs if not)
// 2. Compare current version with latest npm version
// 3. Update if a newer version is available
}
MCP (Model Context Protocol) Support
The SDK provides comprehensive support for MCP, allowing Gemini to connect to external tools and services.
Checking MCP Installation
final mcpInfo = await geminiSDK.isMcpInstalled();
print('MCP enabled: ${mcpInfo.hasMcpSupport}');
print('Configured servers: ${mcpInfo.servers.length}');
for (final server in mcpInfo.servers) {
print(' - ${server.name}: ${server.status}');
}
Listing MCP Servers
// List all configured MCP servers
final servers = await geminiSDK.listMcpServers();
for (final server in servers) {
print('Server: ${server.name}');
print(' Command: ${server.command}');
print(' Args: ${server.args.join(' ')}');
if (server.env != null && server.env!.isNotEmpty) {
print(' Environment: ${server.env!.keys.join(', ')}');
}
}
Installing Popular MCP Servers
// Install filesystem MCP server
await geminiSDK.installPopularMcpServer('filesystem');
// Install GitHub MCP with environment variables
await geminiSDK.installPopularMcpServer('github',
environment: {'GITHUB_TOKEN': 'your-github-token'}
);
// Available popular servers:
// - filesystem: File system access
// - github: GitHub integration
// - postgres: PostgreSQL database
// - git: Git operations
// - puppeteer: Web automation
// - sequential-thinking: Problem solving
// - slack: Slack integration
// - google-drive: Google Drive access
Adding Custom MCP Servers
// Add a custom MCP server
final customServer = McpServer(
name: 'my-custom-server',
command: 'node',
args: ['path/to/server.js'],
env: {'API_KEY': 'your-api-key'},
);
await geminiSDK.addMcpServer(
'my-custom-server',
customServer: customServer,
);
// Or add an npm package as MCP server
await geminiSDK.addMcpServer(
'my-npm-server',
packageName: '@company/mcp-server',
options: McpAddOptions(
scope: McpScope.user,
useNpx: true,
environment: {'CONFIG': 'value'},
),
);
Removing MCP Servers
// Remove an MCP server
await geminiSDK.removeMcpServer('my-custom-server');
Getting MCP Server Details
// Get details about a specific MCP server
final details = await geminiSDK.getMcpServerDetails('filesystem');
if (details != null) {
print('Server: ${details.name}');
print('Command: ${details.command}');
print('Args: ${details.args}');
}
Using MCP in Chat Sessions
Once MCP servers are configured, they're automatically available in chat sessions:
final chat = geminiSDK.createNewChat();
// Gemini can now use the configured MCP tools
final result = await chat.sendMessage([
GeminiSdkContent.text(
'List all files in my Documents folder' // Works if filesystem MCP is installed
),
]);
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: falseare automatically added to the JSON schema'srequiredarray - You can still use the legacy
requiredparameter on SchemaObject for backward compatibility
Error Handling
The SDK provides specific exception types for different error scenarios:
import 'package:gemini_cli_sdk/gemini_cli_sdk.dart';
void main() async {
final geminiSDK = GeminiSDK('YOUR_API_KEY');
final geminiChat = geminiSDK.createNewChat();
try {
final result = await geminiChat.sendMessage([
GeminiSdkContent.text('Hello, Gemini!'),
]);
print(result);
} on CLINotFoundException {
print('Gemini 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 GeminiSDKException catch (e) {
print('SDK error: ${e.message}');
} finally {
await geminiChat.dispose();
}
}
Authentication Methods
The SDK supports multiple authentication methods:
1. Direct API Key
final geminiSDK = GeminiSDK('YOUR_API_KEY');
2. Environment Variable
export GEMINI_API_KEY="your-api-key-here"
final apiKey = Platform.environment['GEMINI_API_KEY'] ?? '';
final geminiSDK = GeminiSDK(apiKey);
3. OAuth (Via CLI)
The Gemini CLI also supports OAuth authentication with your Google account for higher rate limits.
4. Vertex AI
For enterprise features, you can use Vertex AI authentication:
export GOOGLE_API_KEY="your-vertex-api-key"
export GOOGLE_GENAI_USE_VERTEXAI=true
Model Selection
The SDK supports different Gemini models:
- gemini-2.5-pro - Most capable model with 1M token context window
- gemini-2.5-flash - Faster, lighter model for quick responses
final chat = geminiSDK.createNewChat(
options: GeminiChatOptions(
model: 'gemini-2.5-flash', // or 'gemini-2.5-pro'
),
);
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 = geminiSDK.createNewChat();
try {
// Use the chat
await chat.sendMessage([...]);
} finally {
await chat.dispose();
}
// Method 2: Dispose all sessions at once
await geminiSDK.dispose(); // Disposes all active sessions
API Reference
GeminiSDK Class
GeminiSDK(String apiKey)- Creates a new SDK instancecreateNewChat({GeminiChatOptions? options})- Creates a new chat sessionisGeminiCLIInstalled()- Checks if Gemini CLI is installedinstallGeminiCLI({bool global = true})- Installs the Gemini CLIupdateToNewestVersionIfNeeded({bool global = true})- Updates SDK to newest version if availablegetSDKInfo()- Gets information about installed SDKsisMcpInstalled()- Checks MCP installation statuslistMcpServers()- Lists all configured MCP serversinstallPopularMcpServer(name, {environment})- Installs a popular MCP serveraddMcpServer(name, {packageName, customServer, options})- Adds an MCP servergetMcpServerDetails(name)- Gets details about a specific serverremoveMcpServer(name)- Removes an MCP serverdispose()- Disposes all active chat sessions
GeminiChat Class
sendMessage(List<GeminiSdkContent> contents)- Sends a message and returns the responsesendMessageWithSchema({messages, schema})- Sends a message with a schema for structured responsestreamResponse(List<GeminiSdkContent> contents)- Streams the 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)
GeminiSdkContent
GeminiSdkContent.text(String text)- Creates text contentGeminiSdkContent.file(File file)- Creates file contentGeminiSdkContent.bytes({data, fileExtension})- Creates content from bytes (temporary file)
Troubleshooting
Gemini CLI not found
If you get a CLINotFoundException, make sure Gemini CLI is installed:
npm install -g @google/gemini-cli
Or use the SDK's built-in installer:
await geminiSDK.installGeminiSDK();
Permission Errors
On Unix-like systems, you might need to use sudo for global npm installations:
sudo npm install -g @google/gemini-cli
Process Cleanup
Always dispose of chat sessions to prevent resource leaks:
await geminiChat.dispose();
// or
await geminiSDK.dispose(); // Disposes all sessions
Rate Limits
Be aware of the rate limits for your authentication method:
- OAuth (free): 60 requests/min, 1,000 requests/day
- API Key (free): 100 requests/day
- Vertex AI: Higher limits based on your plan
Examples
Check the example/ directory for more comprehensive examples:
example/basic_usage.dart- Simple text messagingexample/file_analysis.dart- Analyzing files with Geminiexample/schema_example.dart- Using schemas for structured responsesexample/system_prompt_example.dart- Using custom system promptsexample/streaming_example.dart- Streaming responsesexample/installation_check.dart- Checking and installing dependenciesexample/mcp_management.dart- Managing MCP serversexample/bytes_content_example.dart- Working with in-memory data
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 Google AI documentation
- Visit the Gemini CLI repository
Acknowledgments
- Built on top of the official Gemini CLI by Google
- Inspired by the Claude Code SDK architecture
- Supports Model Context Protocol (MCP) for extensibility
Libraries
- gemini_cli_sdk
- Gemini CLI SDK for Dart