Skip to main content
All ACP stable protocol features are fully implemented and production-ready in the Dart SDK.

Agent Initialization

Establish connections and negotiate capabilities between clients and agents.

Initialize Connection

import 'package:acp_dart/acp_dart.dart';

class MyAgent implements Agent {
  @override
  Future<InitializeResponse> initialize(InitializeRequest params) async {
    return InitializeResponse(
      protocolVersion: 1,
      agentCapabilities: AgentCapabilities(
        loadSession: false,
        sessionCapabilities: SessionCapabilities(
          fork: null,
          list: null,
          resume: null,
        ),
      ),
      agentInfo: Implementation(
        name: 'my-agent',
        version: '1.0.0',
      ),
      authMethods: [],
    );
  }
}
The initialize method is called once at the beginning of the connection to negotiate protocol version and exchange capability information.

Session Management

Create and manage conversation sessions with full state tracking.

session/new

Creates a new conversation session with the agent.

session/load

Loads an existing session to resume a previous conversation.

session/set_mode

Changes the operational mode (e.g., “ask”, “code”, “architect”).

session/set_config_option

Updates session configuration options.

Create New Session

@override
Future<NewSessionResponse> newSession(NewSessionRequest params) async {
  // Create session context
  final sessionId = generateSessionId();
  
  // Connect to MCP servers
  await connectToMcpServers(params.mcpServers);
  
  return NewSessionResponse(
    sessionId: sessionId,
    modes: SessionModeState(
      availableModes: [
        SessionMode(
          id: 'ask',
          name: 'Ask',
          description: 'Q&A mode for quick questions',
        ),
        SessionMode(
          id: 'code',
          name: 'Code',
          description: 'Full coding assistance',
        ),
      ],
      currentModeId: 'ask',
    ),
  );
}

Load Existing Session

@override
Future<LoadSessionResponse>? loadSession(LoadSessionRequest params) async {
  // Restore session state
  final session = await restoreSession(params.sessionId);
  
  // Replay conversation history via session updates
  for (final message in session.history) {
    await connection.sessionUpdate(SessionNotification(
      sessionId: params.sessionId,
      update: message,
    ));
  }
  
  return LoadSessionResponse(
    modes: session.modes,
    models: session.models,
  );
}

Prompt Processing

Handle user prompts and stream responses with real-time updates.

Process Prompt

@override
Future<PromptResponse> prompt(PromptRequest params) async {
  // Send agent message chunks
  await connection.sessionUpdate(SessionNotification(
    sessionId: params.sessionId,
    update: AgentMessageChunkSessionUpdate(
      content: TextContentBlock(text: 'Processing your request...'),
    ),
  ));
  
  // Create and execute tool calls
  final toolCall = ToolCallSessionUpdate(
    toolCallId: 'tool_1',
    title: 'Read File',
    kind: ToolKind.read,
    status: ToolCallStatus.inProgress,
  );
  
  await connection.sessionUpdate(SessionNotification(
    sessionId: params.sessionId,
    update: toolCall,
  ));
  
  // Request permission if needed
  final permission = await connection.requestPermission(
    RequestPermissionRequest(
      sessionId: params.sessionId,
      toolCall: ToolCallUpdate(
        toolCallId: 'tool_1',
        title: 'Read config.json',
      ),
      options: [
        PermissionOption(
          optionId: 'allow',
          name: 'Allow',
          kind: PermissionOptionKind.allowOnce,
        ),
      ],
    ),
  );
  
  return PromptResponse(
    stopReason: StopReason.endTurn,
  );
}
The prompt method handles the entire lifecycle of a prompt turn, including content generation, tool execution, and permission requests.

Session Updates

Stream real-time progress during prompt processing.

Content Updates

// User message chunks
await connection.sessionUpdate(SessionNotification(
  sessionId: sessionId,
  update: UserMessageChunkSessionUpdate(
    content: TextContentBlock(text: 'User input...'),
  ),
));

// Agent message chunks
await connection.sessionUpdate(SessionNotification(
  sessionId: sessionId,
  update: AgentMessageChunkSessionUpdate(
    content: TextContentBlock(text: 'Agent response...'),
  ),
));

// Agent thought chunks (internal reasoning)
await connection.sessionUpdate(SessionNotification(
  sessionId: sessionId,
  update: AgentThoughtChunkSessionUpdate(
    content: TextContentBlock(text: 'Analyzing the request...'),
  ),
));

Tool Call Updates

// Initial tool call
await connection.sessionUpdate(SessionNotification(
  sessionId: sessionId,
  update: ToolCallSessionUpdate(
    toolCallId: 'tc_123',
    title: 'Read File',
    kind: ToolKind.read,
    status: ToolCallStatus.pending,
    locations: [
      ToolCallLocation(path: '/path/to/file.dart', line: 42),
    ],
  ),
));

// Update tool call progress
await connection.sessionUpdate(SessionNotification(
  sessionId: sessionId,
  update: ToolCallUpdateSessionUpdate(
    toolCallId: 'tc_123',
    status: ToolCallStatus.completed,
    content: [
      ContentToolCallContent(
        content: TextContentBlock(text: 'File contents...'),
      ),
    ],
  ),
));

Planning Updates

// Send execution plan
await connection.sessionUpdate(SessionNotification(
  sessionId: sessionId,
  update: PlanSessionUpdate(
    entries: [
      PlanEntry(
        content: 'Read the configuration file',
        priority: PlanEntryPriority.high,
        status: PlanEntryStatus.inProgress,
      ),
      PlanEntry(
        content: 'Update the settings',
        priority: PlanEntryPriority.medium,
        status: PlanEntryStatus.pending,
      ),
    ],
  ),
));

// Update available commands
await connection.sessionUpdate(SessionNotification(
  sessionId: sessionId,
  update: AvailableCommandsUpdateSessionUpdate(
    availableCommands: [
      AvailableCommand(
        name: 'create_plan',
        description: 'Create an execution plan',
      ),
    ],
  ),
));

File System Operations

Read and write text files within the client’s environment.
// Agent calls client to read a file
final response = await connection.readTextFile(
  ReadTextFileRequest(
    sessionId: sessionId,
    path: '/path/to/file.txt',
    line: 0,  // Optional: start line
    limit: 100,  // Optional: max lines
  ),
);

print('File contents: ${response.content}');
Only available if the client advertises the fs.readTextFile capability.
// Agent calls client to write a file
await connection.writeTextFile(
  WriteTextFileRequest(
    sessionId: sessionId,
    path: '/path/to/output.txt',
    content: 'New file contents',
  ),
);
Only available if the client advertises the fs.writeTextFile capability.

Terminal Operations

Create and manage terminal sessions for command execution.

Complete Terminal Workflow

// Create terminal
final terminalResponse = await connection.createTerminal(
  CreateTerminalRequest(
    sessionId: sessionId,
    command: 'npm',
    args: ['install'],
    cwd: '/project/path',
    env: [
      EnvVariable(name: 'NODE_ENV', value: 'development'),
    ],
  ),
);

final terminalId = terminalResponse.terminalId;

// Get current output
final output = await connection.terminalOutput(
  TerminalOutputRequest(
    sessionId: sessionId,
    terminalId: terminalId,
  ),
);

print('Output: ${output.output}');
print('Truncated: ${output.truncated}');

// Wait for completion
final exitStatus = await connection.waitForTerminalExit(
  WaitForTerminalExitRequest(
    sessionId: sessionId,
    terminalId: terminalId,
  ),
);

print('Exit code: ${exitStatus.exitCode}');

// Clean up
await connection.releaseTerminal(
  ReleaseTerminalRequest(
    sessionId: sessionId,
    terminalId: terminalId,
  ),
);
Use TerminalHandle for automatic resource management:
final handle = TerminalHandle(terminalId, sessionId, connection);
try {
  final output = await handle.currentOutput();
  final exit = await handle.waitForExit();
} finally {
  await handle.dispose();  // Auto-releases
}

Request Cancellation

Cancel ongoing operations using protocol-level notifications.

Session Cancellation

// Client cancels an ongoing prompt
await connection.cancel(
  CancelNotification(sessionId: sessionId),
);

Protocol-Level Cancellation

// Cancel a specific JSON-RPC request
await connection.sendCancelRequest(
  CancelRequestNotification(requestId: requestId),
);

// Or cancel a pending request
final wasCancelled = await connection.cancelPendingRequest(
  requestId,
  meta: {'reason': 'User cancelled'},
);
Cancelled requests receive a JSON-RPC error response with code -32800 (Request Cancelled).

Error Handling

The SDK maps errors to appropriate JSON-RPC error codes:
Error TypeJSON-RPC CodeDescription
Parameter validation-32602Invalid params
Unexpected failures-32603Internal error
Request cancelled-32800Cancelled
Authentication required-32000Auth required
Resource not found-32002Resource not found
Method not found-32601Method not found
import 'package:acp_dart/acp_dart.dart';

// Throw specific errors
throw RequestError.invalidParams({'field': 'sessionId required'});
throw RequestError.authRequired();
throw RequestError.resourceNotFound('/path/to/file');

Next: Unstable Features

Explore experimental protocol features

Build docs developers (and LLMs) love