or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

docs

agent-connection.mdclient-connection.mderrors.mdindex.mdinterfaces.mdprotocol-types.mdstream.md
tile.json

tessl/npm-agentclientprotocol--sdk

TypeScript implementation of the Agent Client Protocol (ACP) for standardized communication between code editors and AI-powered coding agents.

Workspace
tessl
Visibility
Public
Created
Last updated
Describes
npmpkg:npm/@agentclientprotocol/sdk@0.5.x

To install, run

npx @tessl/cli install tessl/npm-agentclientprotocol--sdk@0.5.0

index.mddocs/

Agent Client Protocol SDK

The Agent Client Protocol (ACP) TypeScript SDK provides a standardized communication protocol between code editors (clients) and AI-powered coding agents. It enables bidirectional JSON-RPC 2.0-based communication over streams with full TypeScript type safety and runtime validation via Zod.

Package Information

  • Package Name: @agentclientprotocol/sdk
  • Package Type: npm
  • Language: TypeScript
  • Installation: npm install @agentclientprotocol/sdk
  • Documentation: https://agentclientprotocol.github.io/typescript-sdk
  • Protocol Spec: https://agentclientprotocol.com

Core Imports

import {
  AgentSideConnection,
  ClientSideConnection,
  Agent,
  Client,
  ndJsonStream,
  RequestError,
  TerminalHandle
} from '@agentclientprotocol/sdk';

For protocol types and interfaces:

import type {
  InitializeRequest,
  InitializeResponse,
  NewSessionRequest,
  PromptRequest,
  SessionNotification,
  // ... and many more
} from '@agentclientprotocol/sdk';

Basic Usage

Implementing an Agent

import { AgentSideConnection, Agent, ndJsonStream } from '@agentclientprotocol/sdk';

const agent: Agent = {
  async initialize(params) {
    return {
      protocolVersion: 1,
      agentCapabilities: {
        /* agent capabilities */
      },
      agentInfo: {
        name: 'my-agent',
        version: '1.0.0'
      }
    };
  },

  async newSession(params) {
    const sessionId = generateSessionId();
    return {
      sessionId,
      modes: {
        availableModes: [{
          id: 'code',
          name: 'Code Mode',
          description: 'AI-powered code editing'
        }],
        currentModeId: 'code'
      }
    };
  },

  async authenticate(params) {
    // Handle authentication if required
    return {};
  },

  async prompt(params) {
    // Process user prompt and return response
    // Send updates via connection.sessionUpdate()
    return {
      stopReason: 'end_turn'
    };
  },

  async cancel(params) {
    // Cancel ongoing operations
  }
};

// Create connection with stdio streams
const connection = new AgentSideConnection(
  () => agent,
  ndJsonStream(
    new WritableStream({ write: (chunk) => process.stdout.write(chunk) }),
    readableStreamFromNodeStream(process.stdin)
  )
);

// Wait for connection to close
await connection.closed;

Implementing a Client

import { ClientSideConnection, Client, ndJsonStream } from '@agentclientprotocol/sdk';

const client: Client = {
  async requestPermission(params) {
    // Present permission request to user
    const approved = await askUser(params);
    return {
      outcome: approved
        ? { outcome: 'selected', optionId: params.options[0].optionId }
        : { outcome: 'cancelled' }
    };
  },

  async sessionUpdate(params) {
    // Handle session updates (message chunks, tool calls, etc.)
    if (params.update.sessionUpdate === 'agent_message_chunk') {
      displayMessageChunk(params.update.content);
    } else if (params.update.sessionUpdate === 'tool_call') {
      displayToolCall(params.update);
    }
  },

  // Optional capabilities
  async readTextFile(params) {
    const content = await fs.readFile(params.path, 'utf-8');
    return { content };
  },

  async writeTextFile(params) {
    await fs.writeFile(params.path, params.content);
    return {};
  }
};

const connection = new ClientSideConnection(
  () => client,
  ndJsonStream(output, input)
);

// Initialize the agent
const initResponse = await connection.initialize({
  protocolVersion: 1,
  clientCapabilities: { fs: { readTextFile: true, writeTextFile: true } },
  clientInfo: { name: 'my-editor', version: '1.0.0' }
});

// Create a session
const session = await connection.newSession({
  cwd: '/path/to/project',
  mcpServers: []
});

// Send a prompt
const response = await connection.prompt({
  sessionId: session.sessionId,
  prompt: [{ type: 'text', text: 'Hello, agent!' }]
});

Architecture

The ACP SDK is built around several key components:

Connection Types

  • AgentSideConnection: Provides the agent's view of a connection, enabling agents to communicate with clients
  • ClientSideConnection: Provides the client's view of a connection, enabling clients to communicate with agents

Core Interfaces

  • Agent Interface: Defines the methods that agents must implement (initialize, newSession, prompt, etc.)
  • Client Interface: Defines the methods that clients must implement (requestPermission, sessionUpdate, etc.)

Communication Model

  • JSON-RPC 2.0: All communication uses JSON-RPC 2.0 protocol
  • Bidirectional Streams: Messages flow over ReadableStream/WritableStream pairs
  • ndJsonStream: Utility for creating streams from newline-delimited JSON

Type Safety

  • TypeScript Types: Complete type definitions for all protocol messages
  • Zod Schemas: Runtime validation for all request and response types
  • Type Inference: Full IDE autocomplete and type checking

Capabilities

Agent-Side Connection

The AgentSideConnection class provides methods for agents to interact with clients.

class AgentSideConnection {
  constructor(
    toAgent: (conn: AgentSideConnection) => Agent,
    stream: Stream
  );

  async sessionUpdate(params: SessionNotification): Promise<void>;
  async requestPermission(params: RequestPermissionRequest): Promise<RequestPermissionResponse>;
  async readTextFile(params: ReadTextFileRequest): Promise<ReadTextFileResponse>;
  async writeTextFile(params: WriteTextFileRequest): Promise<WriteTextFileResponse>;
  async createTerminal(params: CreateTerminalRequest): Promise<TerminalHandle>;
  async extMethod(method: string, params: Record<string, unknown>): Promise<Record<string, unknown>>;
  async extNotification(method: string, params: Record<string, unknown>): Promise<void>;

  get signal(): AbortSignal;
  get closed(): Promise<void>;
}

Agent-Side Connection

Client-Side Connection

The ClientSideConnection class provides methods for clients to interact with agents.

class ClientSideConnection implements Agent {
  constructor(
    toClient: (agent: Agent) => Client,
    stream: Stream
  );

  async initialize(params: InitializeRequest): Promise<InitializeResponse>;
  async newSession(params: NewSessionRequest): Promise<NewSessionResponse>;
  async loadSession(params: LoadSessionRequest): Promise<LoadSessionResponse>;
  async setSessionMode(params: SetSessionModeRequest): Promise<SetSessionModeResponse>;
  async setSessionModel(params: SetSessionModelRequest): Promise<SetSessionModelResponse>;
  async authenticate(params: AuthenticateRequest): Promise<AuthenticateResponse>;
  async prompt(params: PromptRequest): Promise<PromptResponse>;
  async cancel(params: CancelNotification): Promise<void>;
  async extMethod(method: string, params: Record<string, unknown>): Promise<Record<string, unknown>>;
  async extNotification(method: string, params: Record<string, unknown>): Promise<void>;

  get signal(): AbortSignal;
  get closed(): Promise<void>;
}

Client-Side Connection

Protocol Interfaces

The SDK defines two core interfaces that must be implemented:

interface Agent {
  initialize(params: InitializeRequest): Promise<InitializeResponse>;
  newSession(params: NewSessionRequest): Promise<NewSessionResponse>;
  authenticate(params: AuthenticateRequest): Promise<AuthenticateResponse | void>;
  prompt(params: PromptRequest): Promise<PromptResponse>;
  cancel(params: CancelNotification): Promise<void>;

  // Optional methods
  loadSession?(params: LoadSessionRequest): Promise<LoadSessionResponse>;
  setSessionMode?(params: SetSessionModeRequest): Promise<SetSessionModeResponse | void>;
  setSessionModel?(params: SetSessionModelRequest): Promise<SetSessionModelResponse | void>;
  extMethod?(method: string, params: Record<string, unknown>): Promise<Record<string, unknown>>;
  extNotification?(method: string, params: Record<string, unknown>): Promise<void>;
}

interface Client {
  requestPermission(params: RequestPermissionRequest): Promise<RequestPermissionResponse>;
  sessionUpdate(params: SessionNotification): Promise<void>;

  // Optional capabilities
  readTextFile?(params: ReadTextFileRequest): Promise<ReadTextFileResponse>;
  writeTextFile?(params: WriteTextFileRequest): Promise<WriteTextFileResponse>;
  createTerminal?(params: CreateTerminalRequest): Promise<CreateTerminalResponse>;
  terminalOutput?(params: TerminalOutputRequest): Promise<TerminalOutputResponse>;
  releaseTerminal?(params: ReleaseTerminalRequest): Promise<ReleaseTerminalResponse | void>;
  waitForTerminalExit?(params: WaitForTerminalExitRequest): Promise<WaitForTerminalExitResponse>;
  killTerminal?(params: KillTerminalCommandRequest): Promise<KillTerminalResponse | void>;
  extMethod?(method: string, params: Record<string, unknown>): Promise<Record<string, unknown>>;
  extNotification?(method: string, params: Record<string, unknown>): Promise<void>;
}

Protocol Interfaces

Protocol Types

Complete type definitions for all protocol messages including requests, responses, and notifications.

// Initialization
interface InitializeRequest {
  protocolVersion: number;
  clientCapabilities?: ClientCapabilities;
  clientInfo?: Implementation;
  _meta?: Record<string, unknown>;
}

interface InitializeResponse {
  protocolVersion: number;
  agentCapabilities?: AgentCapabilities;
  agentInfo?: Implementation;
  authMethods?: AuthMethod[];
  _meta?: Record<string, unknown>;
}

// Sessions
interface NewSessionRequest {
  cwd: string;
  mcpServers: McpServer[];
  _meta?: Record<string, unknown>;
}

interface NewSessionResponse {
  sessionId: string;
  modes?: SessionModeState;
  models?: SessionModelState;
  _meta?: Record<string, unknown>;
}

// Prompts
interface PromptRequest {
  sessionId: string;
  prompt: ContentBlock[];
  _meta?: Record<string, unknown>;
}

interface PromptResponse {
  stopReason: "end_turn" | "max_tokens" | "max_turn_requests" | "refusal" | "cancelled";
  _meta?: Record<string, unknown>;
}

Protocol Types

Stream Management

Utilities for creating and managing bidirectional message streams.

type Stream = {
  writable: WritableStream<AnyMessage>;
  readable: ReadableStream<AnyMessage>;
};

function ndJsonStream(
  output: WritableStream<Uint8Array>,
  input: ReadableStream<Uint8Array>
): Stream;

Stream Management

Error Handling

JSON-RPC error handling with standard error codes.

class RequestError extends Error {
  constructor(code: number, message: string, data?: unknown);

  code: number;
  data?: unknown;

  static parseError(data?: unknown, additionalMessage?: string): RequestError;
  static invalidRequest(data?: unknown, additionalMessage?: string): RequestError;
  static methodNotFound(method: string): RequestError;
  static invalidParams(data?: unknown, additionalMessage?: string): RequestError;
  static internalError(data?: unknown, additionalMessage?: string): RequestError;
  static authRequired(data?: unknown, additionalMessage?: string): RequestError;
  static resourceNotFound(uri?: string): RequestError;

  toResult<T>(): Result<T>;
  toErrorResponse(): ErrorResponse;
}

Error Handling

Terminal Management

Create and manage terminal instances for command execution.

class TerminalHandle {
  constructor(id: string, sessionId: string, conn: Connection);

  id: string;

  async currentOutput(): Promise<TerminalOutputResponse>;
  async waitForExit(): Promise<WaitForTerminalExitResponse>;
  async kill(): Promise<KillTerminalResponse>;
  async release(): Promise<ReleaseTerminalResponse | void>;
  async [Symbol.asyncDispose](): Promise<void>;
}

Terminal management is accessed via AgentSideConnection.createTerminal() which returns a TerminalHandle. Always call release() when done with a terminal to free resources, or use await using for automatic cleanup.

Constants

Protocol Constants

const PROTOCOL_VERSION: number = 1;

const AGENT_METHODS: {
  readonly authenticate: "authenticate";
  readonly initialize: "initialize";
  readonly session_cancel: "session/cancel";
  readonly session_load: "session/load";
  readonly session_new: "session/new";
  readonly session_prompt: "session/prompt";
  readonly session_set_mode: "session/set_mode";
  readonly session_set_model: "session/set_model";
};

const CLIENT_METHODS: {
  readonly fs_read_text_file: "fs/read_text_file";
  readonly fs_write_text_file: "fs/write_text_file";
  readonly session_request_permission: "session/request_permission";
  readonly session_update: "session/update";
  readonly terminal_create: "terminal/create";
  readonly terminal_kill: "terminal/kill";
  readonly terminal_output: "terminal/output";
  readonly terminal_release: "terminal/release";
  readonly terminal_wait_for_exit: "terminal/wait_for_exit";
};

These constants define the method names used in the JSON-RPC protocol.

Extension Points

Both agents and clients can extend the protocol with custom methods and notifications:

// Agent sending extension method to client
await connection.extMethod('custom.action', { data: 'value' });

// Agent sending extension notification to client
await connection.extNotification('custom.event', { info: 'something happened' });

Extension methods are automatically prefixed with underscore (_) in the protocol. To avoid conflicts, use namespaced method names like domain.method.

Connection Lifecycle

Both connection types provide lifecycle management:

// AbortSignal that aborts when connection closes
connection.signal.addEventListener('abort', () => {
  console.log('Connection closed');
});

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

// Wait for connection to close
await connection.closed;
console.log('Connection has closed');

// Use signal with other APIs
fetch(url, { signal: connection.signal });

Runtime Validation

All protocol messages are validated at runtime using Zod schemas. Validation errors are automatically converted to JSON-RPC error responses:

  • Invalid parameters → InvalidParams error (-32602)
  • Unknown methods → MethodNotFound error (-32601)
  • Parsing errors → ParseError error (-32700)

Type Exports

The package exports extensive TypeScript types for all protocol messages, organized into:

  • Request Types: All request interfaces (e.g., InitializeRequest, PromptRequest)
  • Response Types: All response interfaces (e.g., InitializeResponse, PromptResponse)
  • Notification Types: Notification interfaces (e.g., SessionNotification, CancelNotification)
  • Capability Types: Capability definitions (e.g., AgentCapabilities, ClientCapabilities)
  • Content Types: Content block types (e.g., ContentBlock, ToolCallContent)
  • Utility Types: Common types (e.g., Role, ToolKind, ToolCallStatus)

See Protocol Types for the complete type reference.

Notes

  • The protocol uses JSON-RPC 2.0 for all message exchange
  • All types are fully documented with JSDoc comments
  • The package has a single runtime dependency: Zod (^3.0.0)
  • Protocol version 1 is currently supported
  • Some capabilities (e.g., setSessionModel) are marked UNSTABLE and may change