Skip to main content
This quickstart will guide you through creating a minimal ACP agent that responds to prompts and sends updates back to the client.
This example builds an Agent. If you want to build a Client instead, see the Building Clients guide.

Prerequisites

Before starting, make sure you have:
  • Node.js 18+ installed
  • The ACP SDK and Zod installed (see Installation)
  • A TypeScript project set up

Build a Minimal Agent

1

Import the SDK

Create a new file agent.ts and import the necessary modules:
agent.ts
import * as acp from "@agentclientprotocol/sdk";
import { Readable, Writable } from "node:stream";
The SDK provides all core types and classes, while Node.js streams enable stdio communication.
2

Implement the Agent interface

Create a class that implements the Agent interface with the required methods:
agent.ts
class MinimalAgent implements acp.Agent {
  private connection: acp.AgentSideConnection;
  private sessions: Map<string, { pendingPrompt: AbortController | null }>;

  constructor(connection: acp.AgentSideConnection) {
    this.connection = connection;
    this.sessions = new Map();
  }

  async initialize(
    _params: acp.InitializeRequest
  ): Promise<acp.InitializeResponse> {
    return {
      protocolVersion: acp.PROTOCOL_VERSION,
      agentCapabilities: {
        loadSession: false,
      },
    };
  }

  async newSession(
    _params: acp.NewSessionRequest
  ): Promise<acp.NewSessionResponse> {
    const sessionId = Array.from(crypto.getRandomValues(new Uint8Array(16)))
      .map((b) => b.toString(16).padStart(2, "0"))
      .join("");

    this.sessions.set(sessionId, { pendingPrompt: null });

    return { sessionId };
  }

  async authenticate(
    _params: acp.AuthenticateRequest
  ): Promise<acp.AuthenticateResponse | void> {
    return {}; // No authentication required
  }

  async prompt(params: acp.PromptRequest): Promise<acp.PromptResponse> {
    const session = this.sessions.get(params.sessionId);
    if (!session) {
      throw new Error(`Session ${params.sessionId} not found`);
    }

    // Send a text response back to the client
    await this.connection.sessionUpdate({
      sessionId: params.sessionId,
      update: {
        sessionUpdate: "agent_message_chunk",
        content: {
          type: "text",
          text: "Hello! I received your message.",
        },
      },
    });

    return { stopReason: "end_turn" };
  }

  async cancel(params: acp.CancelNotification): Promise<void> {
    this.sessions.get(params.sessionId)?.pendingPrompt?.abort();
  }
}
Key methods explained:
  • initialize(): Establishes the connection and declares agent capabilities
  • newSession(): Creates a new conversation session with a unique ID
  • authenticate(): Handles authentication (empty for this example)
  • prompt(): Processes user prompts and sends responses via sessionUpdate()
  • cancel(): Handles cancellation requests from the client
3

Create the connection

Set up stdio streams and create an AgentSideConnection:
agent.ts
// Create stdio streams for communication
const input = Writable.toWeb(process.stdout);
const output = Readable.toWeb(process.stdin) as ReadableStream<Uint8Array>;

// Create the ndjson stream
const stream = acp.ndJsonStream(input, output);

// Initialize the agent connection
new acp.AgentSideConnection((conn) => new MinimalAgent(conn), stream);
The AgentSideConnection automatically:
  • Routes incoming requests to your agent’s methods
  • Validates all messages using Zod schemas
  • Handles JSON-RPC 2.0 protocol details
4

Run your agent

Execute your agent using tsx or compile with tsc:
npx tsx agent.ts
Your agent is now listening on stdin for ACP messages from a client!
5

Test with a client

You can test your agent by:
  1. Using an ACP-compatible client like Zed
  2. Running the example client from the SDK (see Building Clients)
  3. Sending JSON-RPC messages manually via stdin
When a client sends a prompt, your agent will respond with “Hello! I received your message.”

Complete Example

Here’s the full minimal agent code:
agent.ts
import * as acp from "@agentclientprotocol/sdk";
import { Readable, Writable } from "node:stream";

class MinimalAgent implements acp.Agent {
  private connection: acp.AgentSideConnection;
  private sessions: Map<string, { pendingPrompt: AbortController | null }>;

  constructor(connection: acp.AgentSideConnection) {
    this.connection = connection;
    this.sessions = new Map();
  }

  async initialize(
    _params: acp.InitializeRequest
  ): Promise<acp.InitializeResponse> {
    return {
      protocolVersion: acp.PROTOCOL_VERSION,
      agentCapabilities: { loadSession: false },
    };
  }

  async newSession(
    _params: acp.NewSessionRequest
  ): Promise<acp.NewSessionResponse> {
    const sessionId = Array.from(crypto.getRandomValues(new Uint8Array(16)))
      .map((b) => b.toString(16).padStart(2, "0"))
      .join("");
    this.sessions.set(sessionId, { pendingPrompt: null });
    return { sessionId };
  }

  async authenticate(
    _params: acp.AuthenticateRequest
  ): Promise<acp.AuthenticateResponse | void> {
    return {};
  }

  async prompt(params: acp.PromptRequest): Promise<acp.PromptResponse> {
    const session = this.sessions.get(params.sessionId);
    if (!session) {
      throw new Error(`Session ${params.sessionId} not found`);
    }

    await this.connection.sessionUpdate({
      sessionId: params.sessionId,
      update: {
        sessionUpdate: "agent_message_chunk",
        content: { type: "text", text: "Hello! I received your message." },
      },
    });

    return { stopReason: "end_turn" };
  }

  async cancel(params: acp.CancelNotification): Promise<void> {
    this.sessions.get(params.sessionId)?.pendingPrompt?.abort();
  }
}

const input = Writable.toWeb(process.stdout);
const output = Readable.toWeb(process.stdin) as ReadableStream<Uint8Array>;
const stream = acp.ndJsonStream(input, output);
new acp.AgentSideConnection((conn) => new MinimalAgent(conn), stream);

Understanding the Flow

1

Connection Initialization

The client sends an initialize request. Your agent responds with its protocol version and capabilities.
2

Session Creation

The client calls newSession to create a conversation context. Your agent generates a unique session ID.
3

Prompt Processing

The client sends prompts via the prompt method. Your agent:
  • Processes the prompt (call an LLM, execute tools, etc.)
  • Sends real-time updates via sessionUpdate() notifications
  • Returns a final response with a stop reason
The example agent from src/examples/agent.ts in the SDK repository includes advanced features like tool calls, permission requests, and terminal support. Check it out for a more complete implementation!

Next Steps

Now that you have a working agent, explore more advanced features:

Building Agents

Learn about tool calls, permissions, and advanced agent patterns

Building Clients

Build the client side to connect to your agent

API Reference

Explore the full SDK API documentation

Protocol Docs

Deep dive into the ACP protocol specification

Build docs developers (and LLMs) love