Overview
The ACP file system capability enables:- Reading text files from the client’s file system
- Writing text files to the client’s file system
- Proper permission handling and error management
- Optional line-based reading for large files
File System Request Types
The SDK provides these file system-related types inlib/src/schema.dart:
ReadTextFileRequest- Read contents of a text fileReadTextFileResponse- File content responseWriteTextFileRequest- Write content to a text fileWriteTextFileResponse- Write operation acknowledgmentFileSystemCapability- Declares supported file operations
Enabling File System Capabilities
Client Side
Advertise file system support during initialization:Agent Side
Check capabilities before using file operations:Agent Side
Agents use file operations through the client connection.Reading Files
import 'package:acp_dart/acp_dart.dart';
class MyAgent implements Agent {
final AgentSideConnection _connection;
Future<String> readFile(String sessionId, String path) async {
final response = await _connection.readTextFile(
ReadTextFileRequest(
sessionId: sessionId,
path: path,
),
);
return response.content;
}
}
Future<String> readFileLines(
String sessionId,
String path, {
int? startLine,
int? lineLimit,
}) async {
final response = await _connection.readTextFile(
ReadTextFileRequest(
sessionId: sessionId,
path: path,
line: startLine, // Starting line number (0-based)
limit: lineLimit, // Number of lines to read
),
);
return response.content;
}
Future<void> readAndReport(String sessionId, String path) async {
// Report read operation starting
await _connection.sessionUpdate(
SessionNotification(
sessionId: sessionId,
update: ToolCallSessionUpdate(
toolCallId: 'read_1',
title: 'Reading $path',
kind: ToolKind.read,
status: ToolCallStatus.pending,
locations: [
ToolCallLocation(path: path),
],
rawInput: {'path': path},
),
),
);
try {
// Read the file
final response = await _connection.readTextFile(
ReadTextFileRequest(
sessionId: sessionId,
path: path,
),
);
// Report success
await _connection.sessionUpdate(
SessionNotification(
sessionId: sessionId,
update: ToolCallUpdateSessionUpdate(
toolCallId: 'read_1',
status: ToolCallStatus.completed,
content: [
ContentToolCallContent(
content: TextContentBlock(
text: response.content,
),
),
],
rawOutput: {'content': response.content},
),
),
);
} catch (e) {
// Report failure
await _connection.sessionUpdate(
SessionNotification(
sessionId: sessionId,
update: ToolCallUpdateSessionUpdate(
toolCallId: 'read_1',
status: ToolCallStatus.failed,
content: [
ContentToolCallContent(
content: TextContentBlock(
text: 'Failed to read file: $e',
),
),
],
),
),
);
rethrow;
}
}
Writing Files
Future<void> writeFile(
String sessionId,
String path,
String content,
) async {
await _connection.writeTextFile(
WriteTextFileRequest(
sessionId: sessionId,
path: path,
content: content,
),
);
}
Future<void> writeWithPermission(
String sessionId,
String path,
String content,
) async {
// Report the pending write operation
await _connection.sessionUpdate(
SessionNotification(
sessionId: sessionId,
update: ToolCallSessionUpdate(
toolCallId: 'write_1',
title: 'Writing to $path',
kind: ToolKind.edit,
status: ToolCallStatus.pending,
locations: [ToolCallLocation(path: path)],
rawInput: {'path': path, 'content': content},
),
),
);
// Request permission
final permissionResponse = await _connection.requestPermission(
RequestPermissionRequest(
sessionId: sessionId,
options: [
PermissionOption(
optionId: 'allow',
name: 'Allow write',
kind: PermissionOptionKind.allowOnce,
),
PermissionOption(
optionId: 'reject',
name: 'Cancel',
kind: PermissionOptionKind.rejectOnce,
),
],
toolCall: ToolCallUpdate(
toolCallId: 'write_1',
title: 'Writing to $path',
kind: ToolKind.edit,
status: ToolCallStatus.pending,
locations: [ToolCallLocation(path: path)],
rawInput: {'path': path, 'preview': content.substring(0, 100)},
),
),
);
// Handle permission response
final outcome = permissionResponse.outcome;
switch (outcome) {
case SelectedOutcome(optionId: final id) when id == 'allow':
// Write the file
await _connection.writeTextFile(
WriteTextFileRequest(
sessionId: sessionId,
path: path,
content: content,
),
);
// Update status
await _connection.sessionUpdate(
SessionNotification(
sessionId: sessionId,
update: ToolCallUpdateSessionUpdate(
toolCallId: 'write_1',
status: ToolCallStatus.completed,
),
),
);
break;
case SelectedOutcome(optionId: final id) when id == 'reject':
case CancelledOutcome():
// Update as failed/cancelled
await _connection.sessionUpdate(
SessionNotification(
sessionId: sessionId,
update: ToolCallUpdateSessionUpdate(
toolCallId: 'write_1',
status: ToolCallStatus.failed,
content: [
ContentToolCallContent(
content: TextContentBlock(
text: 'Write operation cancelled by user',
),
),
],
),
),
);
break;
}
}
Client Side
Clients implement file operations to provide file system access to agents.Implementing Read Operation
Implementing Write Operation
Adding Permission Checks
Implement security checks before file operations:Best Practices
Always validate file paths and check permissions before executing file operations.
Do:
✅ Validate paths are within allowed directories:Don’t:
❌ Allow access to system files or credentials:Complete Example
See the complete implementations:Next Steps
Terminal Operations
Execute commands in terminals
Error Handling
Handle file system errors