> ## Documentation Index
> Fetch the complete documentation index at: https://mintlify.com/agentclientprotocol/typescript-sdk/llms.txt
> Use this file to discover all available pages before exploring further.

# Implementing the Client Interface

> Learn how to implement the Client interface to handle agent requests

## Overview

The `Client` interface defines the methods your client must implement to handle incoming requests from the agent. There are two required methods and several optional ones depending on the capabilities you advertise.

## Required Methods

### requestPermission()

Requests permission from the user for a tool call operation.

```typescript theme={null}
async requestPermission(
  params: RequestPermissionRequest
): Promise<RequestPermissionResponse>
```

<ParamField path="params.sessionId" type="string">
  The session requesting permission.
</ParamField>

<ParamField path="params.toolCall" type="ToolCall">
  Information about the tool call requiring permission, including:

  * `toolCallId`: Unique identifier
  * `title`: Human-readable description
  * `content`: Tool call details
</ParamField>

<ParamField path="params.options" type="PermissionOption[]">
  Available permission options to present to the user:

  * `optionId`: Unique identifier for this option
  * `name`: Display name (e.g., "Allow", "Deny", "Always Allow")
  * `kind`: Type of option (`"allow"`, `"deny"`, `"apply_patch"`, `"modify_request"`)
  * `data`: Additional data for the option (e.g., modified patch content)
</ParamField>

<ResponseField name="outcome" type="RequestPermissionOutcome" required>
  The user's decision:

  * `{ outcome: "selected", optionId: string }`: User selected an option
  * `{ outcome: "cancelled" }`: User cancelled the prompt (sent after client calls `session/cancel`)
</ResponseField>

#### Example Implementation

```typescript theme={null}
class MyClient implements acp.Client {
  async requestPermission(
    params: acp.RequestPermissionRequest
  ): Promise<acp.RequestPermissionResponse> {
    // Display permission prompt to user
    console.log(`\n🔐 Permission requested: ${params.toolCall.title}`);
    console.log("\nOptions:");
    
    params.options.forEach((option, index) => {
      console.log(`  ${index + 1}. ${option.name} (${option.kind})`);
    });

    // Get user input
    const answer = await promptUser("Choose an option: ");
    const optionIndex = parseInt(answer) - 1;
    
    if (optionIndex >= 0 && optionIndex < params.options.length) {
      return {
        outcome: {
          outcome: "selected",
          optionId: params.options[optionIndex].optionId,
        },
      };
    }
    
    // Invalid input - could retry or cancel
    throw new Error("Invalid option selected");
  }
}
```

<Note>
  If the client cancels the prompt turn via `session/cancel`, it MUST respond to any pending `requestPermission` request with `{ outcome: { outcome: "cancelled" } }`.
</Note>

### sessionUpdate()

Handles real-time session update notifications from the agent.

```typescript theme={null}
async sessionUpdate(
  params: SessionNotification
): Promise<void>
```

<ParamField path="params.sessionId" type="string">
  The session that generated this update.
</ParamField>

<ParamField path="params.update" type="SessionUpdate">
  The update content. The `sessionUpdate` field discriminates the type:

  * `"agent_message_chunk"`: Streaming agent response
  * `"agent_thought_chunk"`: Agent's internal reasoning
  * `"tool_call"`: New tool call initiated
  * `"tool_call_update"`: Tool call status changed
  * `"plan"`: Execution plan
  * And more...
</ParamField>

This is a notification (no response expected). See [Handling Session Updates](/clients/handling-updates) for detailed implementation.

#### Example Implementation

```typescript src/acp.ts:45-72 theme={null}
class MyClient implements acp.Client {
  async sessionUpdate(params: acp.SessionNotification): Promise<void> {
    const update = params.update;

    switch (update.sessionUpdate) {
      case "agent_message_chunk":
        if (update.content.type === "text") {
          process.stdout.write(update.content.text);
        }
        break;
        
      case "tool_call":
        console.log(`\n🔧 ${update.title} (${update.status})`);
        break;
        
      case "tool_call_update":
        console.log(`🔧 Tool ${update.toolCallId}: ${update.status}`);
        break;
        
      case "plan":
        console.log("📋 Plan:", update.description);
        break;
        
      default:
        console.log(`[${update.sessionUpdate}]`);
    }
  }
}
```

## Optional Methods

These methods are only required if you advertise the corresponding capabilities during initialization.

### readTextFile()

Reads content from a text file in the client's file system.

<Note>
  Only implement if you advertise `fs.readTextFile: true` in client capabilities.
</Note>

```typescript theme={null}
async readTextFile(
  params: ReadTextFileRequest
): Promise<ReadTextFileResponse>
```

<ParamField path="params.sessionId" type="string">
  The session making the request.
</ParamField>

<ParamField path="params.uri" type="string">
  Absolute file path to read.
</ParamField>

<ResponseField name="content" type="string" required>
  The file contents as a UTF-8 string.
</ResponseField>

#### Example Implementation

```typescript theme={null}
import { readFile } from "node:fs/promises";

class MyClient implements acp.Client {
  async readTextFile(
    params: acp.ReadTextFileRequest
  ): Promise<acp.ReadTextFileResponse> {
    // Security: Validate path is within allowed directories
    if (!isPathAllowed(params.uri)) {
      throw new Error(`Access denied: ${params.uri}`);
    }
    
    try {
      const content = await readFile(params.uri, "utf-8");
      return { content };
    } catch (error) {
      throw new Error(`Failed to read file: ${error.message}`);
    }
  }
}
```

### writeTextFile()

Writes content to a text file in the client's file system.

<Note>
  Only implement if you advertise `fs.writeTextFile: true` in client capabilities.
</Note>

```typescript theme={null}
async writeTextFile(
  params: WriteTextFileRequest
): Promise<WriteTextFileResponse>
```

<ParamField path="params.sessionId" type="string">
  The session making the request.
</ParamField>

<ParamField path="params.uri" type="string">
  Absolute file path to write.
</ParamField>

<ParamField path="params.content" type="string">
  The content to write (UTF-8).
</ParamField>

<ResponseField name="(empty)" type="object">
  Returns an empty object on success.
</ResponseField>

#### Example Implementation

```typescript theme={null}
import { writeFile, mkdir } from "node:fs/promises";
import { dirname } from "node:path";

class MyClient implements acp.Client {
  async writeTextFile(
    params: acp.WriteTextFileRequest
  ): Promise<acp.WriteTextFileResponse> {
    // Security: Validate path
    if (!isPathAllowed(params.uri)) {
      throw new Error(`Access denied: ${params.uri}`);
    }
    
    try {
      // Ensure directory exists
      await mkdir(dirname(params.uri), { recursive: true });
      
      // Write file
      await writeFile(params.uri, params.content, "utf-8");
      
      return {};
    } catch (error) {
      throw new Error(`Failed to write file: ${error.message}`);
    }
  }
}
```

See [File System Operations](/clients/file-system-operations) for more details.

### createTerminal()

Creates a new terminal to execute a command.

<Note>
  Only implement if you advertise `terminal: true` in client capabilities.
</Note>

```typescript theme={null}
async createTerminal(
  params: CreateTerminalRequest
): Promise<CreateTerminalResponse>
```

<ParamField path="params.sessionId" type="string">
  The session creating the terminal.
</ParamField>

<ParamField path="params.command" type="string">
  The command to execute.
</ParamField>

<ParamField path="params.args" type="string[]">
  Command arguments.
</ParamField>

<ParamField path="params.cwd" type="string" optional>
  Working directory for the command.
</ParamField>

<ParamField path="params.env" type="Record<string, string>" optional>
  Environment variables.
</ParamField>

<ResponseField name="terminalId" type="string" required>
  Unique identifier for this terminal.
</ResponseField>

### terminalOutput()

Gets the current output and exit status of a terminal.

```typescript theme={null}
async terminalOutput(
  params: TerminalOutputRequest
): Promise<TerminalOutputResponse>
```

<ParamField path="params.sessionId" type="string">
  The session ID.
</ParamField>

<ParamField path="params.terminalId" type="string">
  The terminal ID.
</ParamField>

<ResponseField name="output" type="string">
  The terminal output (combined stdout/stderr).
</ResponseField>

<ResponseField name="exitStatus" type="ExitStatus" optional>
  Exit status if the command has completed:

  * `{ type: "exit_code", code: number }`
  * `{ type: "signal", signal: string }`
</ResponseField>

### waitForTerminalExit()

Waits for a terminal command to exit and returns its exit status.

```typescript theme={null}
async waitForTerminalExit(
  params: WaitForTerminalExitRequest
): Promise<WaitForTerminalExitResponse>
```

### killTerminal()

Kills a terminal command without releasing the terminal.

```typescript theme={null}
async killTerminal(
  params: KillTerminalRequest
): Promise<KillTerminalResponse | void>
```

### releaseTerminal()

Releases a terminal and frees all associated resources.

```typescript theme={null}
async releaseTerminal(
  params: ReleaseTerminalRequest
): Promise<ReleaseTerminalResponse | void>
```

See [Terminal Operations](/clients/terminals) for detailed implementations.

## Extension Methods

### extMethod()

Handles arbitrary requests not part of the ACP spec.

```typescript theme={null}
async extMethod(
  method: string,
  params: Record<string, unknown>
): Promise<Record<string, unknown>>
```

<Warning>
  Prefix extension methods with a unique identifier to avoid conflicts.
</Warning>

### extNotification()

Handles arbitrary notifications not part of the ACP spec.

```typescript theme={null}
async extNotification(
  method: string,
  params: Record<string, unknown>
): Promise<void>
```

## Complete Example

Here's a complete client implementation:

```typescript theme={null}
import * as acp from "@agentclientprotocol/acp";
import { readFile, writeFile } from "node:fs/promises";

class MyClient implements acp.Client {
  // Required: Handle permission requests
  async requestPermission(
    params: acp.RequestPermissionRequest
  ): Promise<acp.RequestPermissionResponse> {
    const selectedOption = await showPermissionDialog(params);
    return {
      outcome: {
        outcome: "selected",
        optionId: selectedOption,
      },
    };
  }

  // Required: Handle session updates
  async sessionUpdate(params: acp.SessionNotification): Promise<void> {
    updateUI(params.update);
  }

  // Optional: File system support
  async readTextFile(
    params: acp.ReadTextFileRequest
  ): Promise<acp.ReadTextFileResponse> {
    const content = await readFile(params.uri, "utf-8");
    return { content };
  }

  async writeTextFile(
    params: acp.WriteTextFileRequest
  ): Promise<acp.WriteTextFileResponse> {
    await writeFile(params.uri, params.content, "utf-8");
    return {};
  }

  // Optional: Terminal support
  async createTerminal(
    params: acp.CreateTerminalRequest
  ): Promise<acp.CreateTerminalResponse> {
    const terminalId = await createTerminalSession(params);
    return { terminalId };
  }

  async terminalOutput(
    params: acp.TerminalOutputRequest
  ): Promise<acp.TerminalOutputResponse> {
    const { output, exitStatus } = getTerminalOutput(params.terminalId);
    return { output, exitStatus };
  }

  async releaseTerminal(
    params: acp.ReleaseTerminalRequest
  ): Promise<void> {
    await cleanupTerminal(params.terminalId);
  }
}
```

## See Also

* [ClientSideConnection](/clients/client-side-connection) - Using the connection class
* [Handling Session Updates](/clients/handling-updates) - Processing real-time updates
* [Permission Requests](/clients/permission-requests) - Building permission UI
* [File System Operations](/clients/file-system-operations) - Implementing file access
* [Terminal Operations](/clients/terminals) - Managing terminal execution
