dart_acp
A Dart implementation of the Agent Client Protocol (ACP), providing both a library for building ACP clients and a full-featured command-line tool for interacting with ACP agents.
Overview
dart_acp enables Dart and Flutter applications to communicate with AI agents that implement the Agent Client Protocol. The package includes:
dart_acplibrary: A complete ACP client implementation for Dart applicationsacpclitool: A command-line interface for testing and interacting with ACP agents
Key Features
Protocol & Streaming
- Full ACP Protocol Support: Compatible with the latest specification from agentclientprotocol.com
- Typed Update Streaming: Sealed
AcpUpdatetypes with exhaustive pattern matching - Turn-Scoped Streams: Prompt streams auto-close after
TurnEndedfor clean resource management - Session Replay: Replay buffers persist updates for
session/loadhistory playback - Tool Call Merging: Automatic merge semantics for
tool_call_update(only non-null fields update)
Security
- Workspace Jail: Path traversal prevention with symlink resolution and boundary enforcement
- Terminal CWD Jailing: Terminal processes confined to workspace directory
- Permission Policies: Pluggable permission provider with read/write/execute classification
- Secure Path Handling: Home directory expansion, path normalization, and canonicalization
Session Management
- Session State Tracking: Per-session workspace roots, mode state, and tool call history
- Session Extensions: Support for
session/list,session/resume,session/fork,session/set_config_option - Mode Switching: Query available modes and switch dynamically during sessions
Architecture
- Provider Pattern: Pluggable
FsProvider,PermissionProvider, andTerminalProviderwith sensible defaults - Transport Abstraction:
StdioTransport(spawn processes) andStdinTransport(piped I/O) - JSON-RPC 2.0: Built on
json_rpc_2package with bidirectional request/notification support
Tooling
- CLI Tool (
acpcli): Full-featured command-line client with multiple output modes - E2E Testing: Integration tests with real agents (Gemini, Claude, Codex)
Documentation
specs/acpcli-requirements.md: Complete requirements and feature documentation for the CLIspecs/dart_acp_technical_design.md: Technical architecture and design decisionsspecs/acp-client-best-practices.md: ACP implementation best practices with conformance checklist
dart_acp Library
The dart_acp library provides a high-level API for building ACP client applications.
Quick Start
import 'dart:io';
import 'package:dart_acp/dart_acp.dart';
void main() async {
// Create and start the client
final client = await AcpClient.start(
config: AcpConfig(
agentCommand: 'npx',
agentArgs: ['@zed-industries/claude-code-acp'],
),
);
// Initialize and create a session
await client.initialize();
final workspaceRoot = Directory.current.path;
final sessionId = await client.newSession(workspaceRoot);
// Send a prompt with @-mention support
final stream = client.prompt(
sessionId: sessionId,
content: 'examine @main.dart and explain what it does.',
);
// Stream the response
await for (final update in stream) {
print(update.text);
}
// Clean up
await client.dispose();
exit(0);
}
See the full example: example/main.dart
Core Components
AcpClient
The main entry point for interacting with ACP agents:
final client = await AcpClient.start(
config: AcpConfig(
agentCommand: 'your-agent-binary',
agentArgs: ['--flag'],
envOverrides: {'API_KEY': 'value'},
// Optional providers
fsProvider: myFsProvider,
permissionProvider: myPermissionProvider,
terminalProvider: DefaultTerminalProvider(),
),
);
Session Management
// Create a new session
final sessionId = await client.newSession(workspaceRoot);
// Resume an existing session (if agent supports it)
await client.loadSession(
sessionId: existingId,
workspaceRoot: workspaceRoot,
);
// Subscribe to session updates
client.sessionUpdates(sessionId).listen((update) {
if (update is PlanUpdate) {
print('Plan: ${update.plan.title}');
} else if (update is ToolCallUpdate) {
print('Tool: ${update.toolCall.title}');
}
});
@-Mention Support
The library automatically parses @-mentions in prompts:
// Local files: @file.txt, @"path with spaces/file.txt"
// URLs: @https://example.com/resource
// Home paths: @~/Documents/file.txt
final updates = client.prompt(
sessionId: sessionId,
content: 'Review @lib/src/main.dart and @README.md',
);
Update Types
The library provides strongly-typed update events:
MessageDelta: Assistant response text chunksPlanUpdate: Execution plans with prioritiesToolCallUpdate: Tool invocations with statusDiffUpdate: File modification diffsAvailableCommandsUpdate: Slash commandsModeUpdate: Session mode changesTurnEnded: End of response with stop reason
Advanced Features
Providers
Customize behavior with pluggable providers:
// File system provider with workspace jail
class MyFsProvider implements FsProvider {
Future<String> readTextFile(String path, {int? line, int? limit}) async {
// Custom implementation
}
Future<void> writeTextFile(String path, String content) async {
// Custom implementation
}
}
// Permission provider for security
class MyPermissionProvider implements PermissionProvider {
Future<PermissionOutcome> requestPermission(
PermissionOptions options,
) async {
// Return allow, deny, or cancelled
}
}
Session Modes (Extension)
// Get available modes
final modes = client.sessionModes(sessionId);
print('Current: ${modes?.currentModeId}');
print('Available: ${modes?.availableModes}');
// Switch mode
await client.setMode(sessionId: sessionId, modeId: 'edit');
acpcli Tool
A comprehensive command-line interface for testing and interacting with ACP agents.
Installation & Setup
- Configure agents in
example/acpcli/settings.json:
{
"agent_servers": {
"claude": {
"command": "npx",
"args": ["@zed-industries/claude-code-acp"],
"env": {
"ACP_PERMISSION_MODE": "acceptEdits",
"ACP_DEBUG": "true"
}
},
"gemini": {
"command": "gemini",
"args": ["--experimental-acp"],
"env": {
"GEMINI_MODEL": "gemini-2.5-pro"
}
},
"codex": {
"command": "npx",
"args": ["@zed-industries/codex-acp"]
}
}
}
- Run the CLI:
dart example/acpcli/acpcli.dart "Your prompt here"
See the full implementation: example/acpcli/acpcli.dart
Usage
dart example/acpcli/acpcli.dart [options] [--] [prompt]
Options:
-h, --help Show help and exit
-a, --agent <name> Select agent from settings.json
-o, --outputmode <mode> Output mode: text, simple, jsonl, json (default: text)
--settings <path> Use specific settings.json file
--write Enable write operations (confined to CWD)
--yolo Enable read-anywhere; writes remain confined to CWD
--list-caps Show agent capabilities
--list-modes Show available session modes
--list-commands Show slash commands
--list-sessions List existing sessions (if agent supports)
--mode <id> Set session mode
--resume <id> Resume existing session
--save-session <path> Save session ID to file
Prompt:
Provide as positional argument or pipe via stdin
Use @-mentions for files: @path, @"file.txt", @https://example.com/doc
Examples:
dart example/acpcli/acpcli.dart -a gemini "Summarize README.md"
echo "List commands" | dart example/acpcli/acpcli.dart -o jsonl
dart example/acpcli/acpcli.dart "Review @lib/src/main.dart"
Key Features
Agent Selection
Select from configured agents or use the default:
# Use default (first in settings.json)
dart example/acpcli/acpcli.dart "Hello"
# Select specific agent
dart example/acpcli/acpcli.dart -a claude "Hello"
dart example/acpcli/acpcli.dart -a gemini "Hello"
dart example/acpcli/acpcli.dart -a codex "Hello"
Output Modes
| Mode | Flag | Description | Use Case |
|---|---|---|---|
| text | -o text (default) |
Human-readable with metadata | Interactive use |
| simple | -o simple |
Assistant text only | Clean output |
| jsonl | -o jsonl |
Raw protocol frames | Debugging/automation |
| json | -o json |
Alias for jsonl | Protocol analysis |
@-Mention Support
Reference files and URLs directly in prompts:
# Local files
dart example/acpcli/acpcli.dart "Review @src/main.dart"
dart example/acpcli/acpcli.dart "Analyze @\"my file.txt\""
# URLs
dart example/acpcli/acpcli.dart "Summarize @https://example.com/api-docs"
# Home directory
dart example/acpcli/acpcli.dart "Read @~/Documents/notes.txt"
Permission Control
Non-interactive permission handling via flags:
# Enable writes (confined to CWD)
dart example/acpcli/acpcli.dart --write "Create a new file"
# Read anywhere; writes remain confined to CWD
dart example/acpcli/acpcli.dart --yolo "Search system files"
Discovery Commands
Explore agent capabilities without sending prompts:
# List capabilities
dart example/acpcli/acpcli.dart -a claude --list-caps
# List session modes
dart example/acpcli/acpcli.dart -a claude --list-modes
# List slash commands
dart example/acpcli/acpcli.dart -a claude --list-commands
# List existing sessions (if agent supports)
dart example/acpcli/acpcli.dart -a claude --list-sessions
# Combine multiple lists
dart example/acpcli/acpcli.dart --list-caps --list-modes --list-commands
Session Management
Save and resume sessions (if agent supports loadSession):
# Save session
dart example/acpcli/acpcli.dart --save-session /tmp/session.txt "Initial prompt"
# Resume later
dart example/acpcli/acpcli.dart --resume $(cat /tmp/session.txt) "Continue"
Protocol Debugging
Use JSONL mode to inspect raw ACP protocol:
# Mirror all JSON-RPC frames
dart example/acpcli/acpcli.dart -o jsonl "Test" | jq '.'
# Filter specific message types
dart example/acpcli/acpcli.dart -o jsonl "Test" | grep tool_call
Tool Monitoring
In text mode, the CLI displays rich information about tool usage:
[tool] read Read file @ src/main.py
[tool.in] {"path":"src/main.py","line":1,"limit":200}
[tool.out] "import sys..."
Plans and Progress
Track agent execution plans:
dart example/acpcli/acpcli.dart "Create a plan to refactor the auth module"
# [plan] {"title": "Refactoring", "steps": [...], "status": "in_progress"}
Terminal Support
When agents execute commands:
dart example/acpcli/acpcli.dart "Run: npm test"
# [term] created id=term_001 cmd=npm
# [term] output id=term_001
# [term] exited id=term_001 code=0
acpcomply Compliance App (Experimental)
Note: This tool is experimental and under active development. Test coverage and reliability may vary.
The compliance runner executes a suite of ACP agent compliance tests and prints a Markdown report to stdout.
- Entrypoint:
example/acpcomply/acpcomply.dart - Tests:
example/acpcomply/compliance-tests/*.jsont - Requirements/spec:
specs/acpcomply-requirements.md
What It Verifies
- Initialization and session setup
- Prompt turns and cancellation semantics
- Capability respect (FS/terminal disabled)
- Error handling (unknown methods, invalid params)
- File system and terminal flows
- Plans, slash commands, and streaming chunks
- Session modes and session load replay
- Tool calls, diffs, locations, and permission flows
- MCP stdio session setup (optional)
How It Works
- Creates a per-test sandbox workspace and writes declared files
- Reuses
AcpClientfor transport and Agent→Client handling - Matches server responses/notifications via regex subset matching
- Observes Agent→Client requests (
fs/*,terminal/*,session/request_permission) - Prints an agent-first Markdown report (no summary table): header per agent, then one H2 per test with actionable status and diffs
Run
dart run example/acpcomply/acpcomply.dart
Notes:
- Some tests are optional and will be marked NA when agents lack the corresponding capability
- The MCP stdio test forwards a local MCP server definition at
tool/mcp_server/bin/server.dartto the agent
Installing Agents
Gemini CLI
# Install via npm (requires Node.js)
npm install -g @google/gemini-cli@0.17.1
# Authenticate
gemini auth login
# Configure in settings.json:
# "gemini": {
# "command": "gemini",
# "args": ["--experimental-acp"],
# "env": { "GEMINI_MODEL": "gemini-2.5-pro" }
# }
Claude Code Adapter
# Run via npx (recommended) - requires Node.js v22 (LTS)
npx @zed-industries/claude-code-acp
# Note: Ensure you are using a stable Node.js version (e.g., v22).
# Experimental versions (like v25) may cause crashes.
Codex Adapter
# Run via npx (recommended)
npx @zed-industries/codex-acp
Testing
Unit Tests
Run fast unit tests without agents:
dart test --exclude-tags e2e
E2E Tests
Run integration tests with real agents:
# All tests including e2e
dart test
# Only e2e tests
dart test --tags e2e
# Specific agent tests
dart test --tags e2e -n "gemini"
dart test --tags e2e -n "claude"
dart test --tags e2e -n "codex"
Tests use test/test_settings.json for agent configuration.
Troubleshooting
Gemini "Invalid argument" Errors
Some Gemini models have ACP implementation bugs. Solution:
- Don't set
GEMINI_MODELenvironment variable - Use the default model or test specific models first
Permission Denied
Agents need explicit permissions for file operations:
- Use
--writefor write operations (confined to the workspace/CWD) - Use
--yolofor read-anywhere; writes still remain confined to the workspace
Authentication Required
Authenticate with the agent's native tools first:
gemini auth login
# or follow Claude Code OAuth flow
Settings Not Found
Ensure example/acpcli/settings.json exists or use --settings to specify a path.
License
See LICENSE file for details.