GitHub
ESC

Basic Usage

Client Lifecycle

The ACP::Client follows a state machine pattern:

Created → Initialized → SessionActive → Closed

Each state transition happens through specific method calls:

client = ACP::Client.new(transport, client_name: "app", client_version: "1.0")
# State: Created

result = client.initialize_connection
# State: Initialized

session_result = client.session_new(Dir.current)
# State: SessionActive

client.close
# State: Closed

Transport Options

ProcessTransport

Spawns an agent process and communicates over stdio:

transport = ACP::ProcessTransport.new(
  "npx",
  args: ["-y", "@anthropic-ai/claude-code", "--agent"],
  env: {"ANTHROPIC_API_KEY" => "sk-..."},
  chdir: Dir.current,
  stderr: STDERR
)

You can check the process status:

transport.terminated?  # => false
transport.wait         # blocks until process exits

StdioTransport

For working with existing IO pairs (e.g., pre-spawned processes):

transport = ACP::StdioTransport.new(
  reader: io_read,
  writer: io_write,
  buffer_size: 256
)

Sessions

Creating a New Session

session = ACP::Session.create(client, Dir.current)

Loading an Existing Session

session = ACP::Session.load(client, "session-id-123", Dir.current)

Sending Prompts

Simple text prompt:

result = session.prompt("Explain this code")

Multiple text blocks:

result = session.prompt("First context", "Now do this")

Using the PromptBuilder DSL:

result = session.prompt do |b|
  b.text("Review this file")
  b.resource_link("/path/to/file.cr", "text/x-crystal")
end

Session Modes

Switch between available agent modes:

# List available modes
session.available_mode_ids  # => ["code", "plan", "architect"]

# Switch mode
session.mode = "plan"

Configuration Options

session.set_config_option("model", "claude-sonnet")

Handling Callbacks

Set up handlers on the client to process streaming updates and agent requests:

client.on_update = ->(update : ACP::Protocol::SessionUpdate) {
  case update
  when ACP::Protocol::AgentMessageChunkUpdate
    print update.delta
  when ACP::Protocol::ToolCallUpdate
    puts "Tool: #{update.tool_name}"
  end
}

client.on_agent_request = ->(method : String, params : JSON::Any?) {
  # Handle permission requests, fs operations, etc.
  nil
}

Typed Client Method Handlers

For specific agent-initiated requests, use typed handlers:

client.on_read_text_file = ->(params : ACP::Protocol::ReadTextFileParams) {
  content = File.read(params.path)
  ACP::Protocol::ReadTextFileResult.new(content: content)
}

client.on_write_text_file = ->(params : ACP::Protocol::WriteTextFileParams) {
  File.write(params.path, params.content)
  ACP::Protocol::WriteTextFileResult.new(success: true)
}

Error Handling

All errors inherit from ACP::Error. Wrap operations in begin/rescue:

begin
  result = session.prompt("Do something")
rescue ex : ACP::RequestTimeoutError
  puts "Timed out: #{ex.message}"
rescue ex : ACP::ConnectionClosedError
  puts "Connection lost"
rescue ex : ACP::Error
  puts "ACP error: #{ex.message}"
end

See the Error Handling guide for detailed patterns.