CtrlK
BlogDocsLog inGet started
Tessl Logo

tessl/npm-agentclientprotocol--sdk

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

Pending
Quality

Pending

Does it follow best practices?

Impact

Pending

No eval scenarios have been run

SecuritybySnyk

Pending

The risk profile of this skill

Overview
Eval results
Files

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
Workspace
tessl
Visibility
Public
Created
Last updated
Describes
npmpkg:npm/@agentclientprotocol/sdk@0.5.x
Publish Source
CLI
Badge
tessl/npm-agentclientprotocol--sdk badge