Skip to main content

Overview

The ClientSideConnection class provides the client’s view of an ACP connection. It implements the Agent interface, giving you access to all agent methods.

Constructor

Creates a new client-side connection to an agent.
new ClientSideConnection(
  toClient: (agent: Agent) => Client,
  stream: Stream
)
toClient
(agent: Agent) => Client
required
A function that creates a Client handler to process incoming agent requests. The function receives the connection instance (implementing the Agent interface) and should return your Client implementation.
stream
Stream
required
The bidirectional message stream for communication. Typically created using ndJsonStream() for stdio-based connections.

Example

import * as acp from "@agentclientprotocol/acp";
import { spawn } from "node:child_process";
import { Writable, Readable } from "node:stream";

// Spawn the agent process
const agentProcess = spawn("my-agent", ["--flag"]);

// Create bidirectional stream
const input = Writable.toWeb(agentProcess.stdin);
const output = Readable.toWeb(agentProcess.stdout) as ReadableStream<Uint8Array>;
const stream = acp.ndJsonStream(input, output);

// Create the connection
const client = new MyClient();
const connection = new acp.ClientSideConnection(
  (_agent) => client,
  stream
);

Methods

The ClientSideConnection implements the full Agent interface:

initialize()

Establishes the connection and negotiates protocol capabilities.
await connection.initialize(
  params: InitializeRequest
): Promise<InitializeResponse>
params.protocolVersion
string
required
The protocol version to use (e.g., acp.PROTOCOL_VERSION).
params.clientCapabilities
ClientCapabilities
required
Capabilities advertised by the client (file system, terminal support, etc.).
protocolVersion
string
The negotiated protocol version.
agentCapabilities
AgentCapabilities
Capabilities supported by the agent.
authMethods
AuthMethod[]
Available authentication methods.

Example

const initResult = await connection.initialize({
  protocolVersion: acp.PROTOCOL_VERSION,
  clientCapabilities: {
    fs: {
      readTextFile: true,
      writeTextFile: true,
    },
    terminal: true,
  },
});

console.log(`Connected using protocol v${initResult.protocolVersion}`);

newSession()

Creates a new conversation session with the agent.
await connection.newSession(
  params: NewSessionRequest
): Promise<NewSessionResponse>
params.cwd
string
required
The working directory for the session (absolute path).
params.mcpServers
McpServer[]
required
MCP servers the agent should connect to for this session.
sessionId
string
Unique identifier for the created session.
currentMode
SessionMode
The initial mode of the session.
availableModes
SessionMode[]
Modes available for this session.

Example

const session = await connection.newSession({
  cwd: "/home/user/project",
  mcpServers: [],
});

console.log(`Session created: ${session.sessionId}`);

loadSession()

Loads an existing session to resume a previous conversation.
Only available if the agent advertises the loadSession capability.
await connection.loadSession(
  params: LoadSessionRequest
): Promise<LoadSessionResponse>
params.sessionId
string
required
The ID of the session to load.
params.mcpServers
McpServer[]
required
MCP servers to connect to for this session.

prompt()

Processes a user prompt within a session.
await connection.prompt(
  params: PromptRequest
): Promise<PromptResponse>
params.sessionId
string
required
The session ID to send the prompt to.
params.prompt
PromptContent[]
required
Array of content items (text, images, files, etc.).
stopReason
StopReason
Why the agent stopped (e.g., "finished", "cancelled", "error").

Example

const result = await connection.prompt({
  sessionId: session.sessionId,
  prompt: [
    {
      type: "text",
      text: "Refactor the parseConfig function",
    },
  ],
});

console.log(`Agent stopped: ${result.stopReason}`);

cancel()

Cancels ongoing operations for a session.
await connection.cancel(
  params: CancelNotification
): Promise<void>
params.sessionId
string
required
The session ID to cancel operations for.

Example

// Cancel the ongoing prompt
await connection.cancel({
  sessionId: session.sessionId,
});

setSessionMode()

Sets the operational mode for a session.
await connection.setSessionMode(
  params: SetSessionModeRequest
): Promise<SetSessionModeResponse>
params.sessionId
string
required
The session ID.
params.mode
string
required
The mode to switch to (must be in availableModes).

authenticate()

Authenticates the client using the specified authentication method.
await connection.authenticate(
  params: AuthenticateRequest
): Promise<AuthenticateResponse>
params.methodId
string
required
The ID of the authentication method to use.

setSessionConfigOption()

Sets a configuration option for a session.
await connection.setSessionConfigOption(
  params: SetSessionConfigOptionRequest
): Promise<SetSessionConfigOptionResponse>

Properties

signal

AbortSignal that aborts when the connection closes.
const signal: AbortSignal = connection.signal;
Useful for:
  • Listening for connection closure
  • Checking connection status synchronously
  • Passing to other APIs for automatic cancellation

Example

// Listen for closure
connection.signal.addEventListener('abort', () => {
  console.log('Connection closed - cleaning up');
  cleanupResources();
});

// Check status
if (connection.signal.aborted) {
  console.log('Connection is already closed');
}

// Pass to fetch for automatic cancellation
fetch(url, { signal: connection.signal });

closed

Promise that resolves when the connection closes.
const closed: Promise<void> = connection.closed;

Example

// Wait for closure
await connection.closed;
console.log('Connection closed - performing cleanup');

// Or use it in a race
const result = await Promise.race([
  connection.prompt({ sessionId, prompt }),
  connection.closed.then(() => {
    throw new Error('Connection closed unexpectedly');
  }),
]);

Extension Methods

extMethod()

Sends an arbitrary request that is not part of the ACP spec.
await connection.extMethod(
  method: string,
  params: Record<string, unknown>
): Promise<Record<string, unknown>>
To avoid conflicts, prefix extension methods with a unique identifier (e.g., domain name).

extNotification()

Sends an arbitrary notification that is not part of the ACP spec.
await connection.extNotification(
  method: string,
  params: Record<string, unknown>
): Promise<void>

Complete Example

import * as acp from "@agentclientprotocol/acp";
import { spawn } from "node:child_process";
import { Writable, Readable } from "node:stream";

class MyClient implements acp.Client {
  async requestPermission(params: acp.RequestPermissionRequest) {
    // Implementation
    return { outcome: { outcome: "approved" } };
  }
  
  async sessionUpdate(params: acp.SessionNotification) {
    console.log("Update:", params.update);
  }
}

async function main() {
  // Spawn agent
  const agentProcess = spawn("my-agent");
  const stream = acp.ndJsonStream(
    Writable.toWeb(agentProcess.stdin),
    Readable.toWeb(agentProcess.stdout) as ReadableStream<Uint8Array>
  );

  // Create connection
  const client = new MyClient();
  const connection = new acp.ClientSideConnection(
    (_agent) => client,
    stream
  );

  // Initialize
  await connection.initialize({
    protocolVersion: acp.PROTOCOL_VERSION,
    clientCapabilities: { fs: { readTextFile: true } },
  });

  // Create session
  const session = await connection.newSession({
    cwd: process.cwd(),
    mcpServers: [],
  });

  // Send prompt
  await connection.prompt({
    sessionId: session.sessionId,
    prompt: [{ type: "text", text: "Hello!" }],
  });

  // Wait for completion
  await connection.closed;
}

main().catch(console.error);

See Also

Build docs developers (and LLMs) love