> ## 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.

# Connections

> Establishing and managing ACP connections between agents and clients

Connections are the foundation of communication in ACP. The SDK provides two connection classes that handle bidirectional message passing between agents and clients.

## Connection Classes

The SDK provides two connection classes, one for each side:

<CardGroup cols={2}>
  <Card title="AgentSideConnection" icon="robot">
    Used by agents to communicate with clients
  </Card>

  <Card title="ClientSideConnection" icon="desktop">
    Used by clients to communicate with agents
  </Card>
</CardGroup>

## AgentSideConnection

The `AgentSideConnection` class provides the agent's view of an ACP connection.

### Creating a Connection

```typescript src/acp.ts theme={null}
import { AgentSideConnection, ndJsonStream, Agent } from "@anoma/acp-sdk";

class MyAgent implements Agent {
  constructor(private connection: AgentSideConnection) {}
  
  async initialize(params) {
    // Implementation
  }
  
  // ... other Agent methods
}

// Create stream from stdio
const stream = ndJsonStream(
  Deno.stdout.writable,
  Deno.stdin.readable
);

// Create connection
const connection = new AgentSideConnection(
  (conn) => new MyAgent(conn),
  stream
);

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

<Note>
  The agent factory function receives the connection instance, allowing your agent to send requests back to the client.
</Note>

### Agent → Client Methods

The connection provides methods for agents to communicate with clients:

```typescript theme={null}
// Send session updates (notification)
await connection.sessionUpdate({
  sessionId: "session-123",
  content: {
    type: "text",
    text: "Processing your request..."
  }
});

// Request permission from user
const response = await connection.requestPermission({
  sessionId: "session-123",
  toolCallId: "call-1",
  options: [
    { id: "allow", kind: "allow", label: "Allow" },
    { id: "deny", kind: "deny", label: "Deny" }
  ]
});

// Read a file from client's filesystem
const fileContent = await connection.readTextFile({
  sessionId: "session-123",
  path: "/path/to/file.txt"
});

// Write a file to client's filesystem
await connection.writeTextFile({
  sessionId: "session-123",
  path: "/path/to/output.txt",
  content: "Hello, world!"
});

// Create a terminal
const terminal = await connection.createTerminal({
  sessionId: "session-123",
  command: "npm",
  args: ["test"],
  cwd: "/project"
});
```

## ClientSideConnection

The `ClientSideConnection` class provides the client's view of an ACP connection.

### Creating a Connection

```typescript src/acp.ts theme={null}
import { ClientSideConnection, ndJsonStream, Client } from "@anoma/acp-sdk";

class MyClient implements Client {
  async requestPermission(params) {
    // Show UI and get user choice
  }
  
  async sessionUpdate(params) {
    // Display in UI
  }
  
  // ... other Client methods
}

// Create stream connected to agent process
const agentProcess = new Deno.Command("./my-agent", {
  stdin: "piped",
  stdout: "piped"
}).spawn();

const stream = ndJsonStream(
  agentProcess.stdin,
  agentProcess.stdout
);

// Create connection
const agent = new ClientSideConnection(
  () => new MyClient(),
  stream
);

// Now you can call agent methods
const initResponse = await agent.initialize({
  protocolVersion: 1,
  capabilities: { fs: { readTextFile: true } },
  clientInfo: { name: "My Editor", version: "1.0.0" }
});
```

### Client → Agent Methods

```typescript theme={null}
// Initialize the connection
const initResponse = await agent.initialize({
  protocolVersion: 1,
  capabilities: {
    fs: { readTextFile: true, writeTextFile: true },
    terminal: true
  },
  clientInfo: {
    name: "My Editor",
    version: "1.0.0"
  }
});

// Create a new session
const session = await agent.newSession({
  cwd: "/project",
  mcpServers: []
});

// Send a prompt
const response = await agent.prompt({
  sessionId: session.sessionId,
  messages: [
    {
      role: "user",
      content: { type: "text", text: "Help me fix this bug" }
    }
  ]
});

// Cancel an ongoing operation
await agent.cancel({
  sessionId: session.sessionId
});

// Load an existing session
await agent.loadSession({
  sessionId: "existing-session-id",
  mcpServers: []
});
```

## Stream-Based Communication

Both connection classes use the `Stream` interface for message passing:

```typescript src/stream.ts theme={null}
export type Stream = {
  writable: WritableStream<AnyMessage>;
  readable: ReadableStream<AnyMessage>;
};
```

### Creating Streams

The SDK provides `ndJsonStream()` to create streams from stdin/stdout:

```typescript theme={null}
import { ndJsonStream } from "@anoma/acp-sdk";

// Agent: Use stdin/stdout
const stream = ndJsonStream(
  Deno.stdout.writable,
  Deno.stdin.readable
);

// Client: Connect to spawned agent process
const agentProcess = new Deno.Command("./agent", {
  stdin: "piped",
  stdout: "piped"
}).spawn();

const stream = ndJsonStream(
  agentProcess.stdin,
  agentProcess.stdout
);
```

<Info>
  Messages are sent as newline-delimited JSON (NDJSON), with each message on a single line.
</Info>

### Message Format

All messages follow the JSON-RPC 2.0 format:

```typescript theme={null}
// Request (expects response)
{
  "jsonrpc": "2.0",
  "id": 1,
  "method": "session/prompt",
  "params": { "sessionId": "abc", "messages": [...] }
}

// Response
{
  "jsonrpc": "2.0",
  "id": 1,
  "result": { "stopReason": "endTurn" }
}

// Notification (no response)
{
  "jsonrpc": "2.0",
  "method": "session/update",
  "params": { "sessionId": "abc", "content": {...} }
}
```

## Connection Lifecycle

Connections have a well-defined lifecycle with monitoring capabilities:

### Initialization Flow

```mermaid theme={null}
sequenceDiagram
    participant Client
    participant Stream
    participant Agent
    
    Client->>Stream: Create connection
    Stream->>Agent: Spawn/Connect
    Client->>Agent: initialize request
    Agent-->>Client: InitializeResponse
    Note over Client,Agent: Connection ready
    Client->>Agent: newSession
    Agent-->>Client: NewSessionResponse
    Client->>Agent: prompt
    Agent->>Client: session/update notifications
    Agent-->>Client: PromptResponse
```

### Monitoring Connection Status

Both connection classes provide properties to monitor connection status:

```typescript src/acp.ts theme={null}
// AbortSignal that fires when connection closes
connection.signal.addEventListener('abort', () => {
  console.log('Connection closed');
});

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

// Wait for connection to close (async)
await connection.closed;
console.log('Connection closed - cleanup complete');

// Use signal with other APIs
fetch(url, { signal: connection.signal });
setTimeout(() => {}, 1000, { signal: connection.signal });
```

<Warning>
  Once a connection is closed, it cannot be reopened. You must create a new connection.
</Warning>

### Closing Connections

Connections close when:

1. **Stream ends normally** - Agent process exits or stream is closed
2. **Error occurs** - Network error, parse error, or protocol violation
3. **Abort signal triggered** - External cancellation

```typescript theme={null}
// Agent: Connection closes when process exits
await connection.closed;
Deno.exit(0);

// Client: Connection closes when agent process exits
const connection = new ClientSideConnection(client, stream);
try {
  await connection.closed;
} catch (error) {
  console.error('Connection error:', error);
}
```

## Error Handling

Connections handle errors according to JSON-RPC 2.0 specification:

```typescript theme={null}
import { RequestError } from "@anoma/acp-sdk";

class MyAgent implements Agent {
  async prompt(params) {
    // Return standard error responses
    if (!sessionExists(params.sessionId)) {
      throw RequestError.invalidParams(
        { sessionId: params.sessionId },
        "Session not found"
      );
    }
    
    // Other built-in error types:
    throw RequestError.methodNotFound("unknown/method");
    throw RequestError.authRequired();
    throw RequestError.internalError({ details: "..." });
  }
}
```

### Error Response Format

```typescript theme={null}
{
  "jsonrpc": "2.0",
  "id": 1,
  "error": {
    "code": -32602,
    "message": "Invalid params: Session not found",
    "data": { "sessionId": "invalid-id" }
  }
}
```

## Extension Methods

Both connection classes support custom methods beyond the ACP specification:

```typescript theme={null}
// Agent sends custom request
const result = await connection.extMethod("myeditor.showPanel", {
  panelId: "debugger"
});

// Agent sends custom notification
await connection.extNotification("myeditor.highlight", {
  line: 42
});

// Client sends custom request to agent
const result = await agent.extMethod("myagent.getStats", {});

// Client sends custom notification to agent
await agent.extNotification("myagent.clearCache", {});
```

<Note>
  Extension methods should be prefixed with a unique identifier to avoid conflicts (e.g., domain name).
</Note>

## Terminal Handling

Agents can create and manage terminals through the connection:

```typescript src/acp.ts theme={null}
// Create a terminal
const terminal = await connection.createTerminal({
  sessionId,
  command: "npm",
  args: ["test"],
  cwd: "/project"
});

// Get current output (non-blocking)
const output = await terminal.currentOutput();
console.log(output.output);

// Wait for command to complete
const exitStatus = await terminal.waitForExit();
if (exitStatus.exitCode === 0) {
  console.log("Command succeeded");
}

// Kill the command
await terminal.kill();

// Always release when done
await terminal.release();

// Or use automatic cleanup
await using terminal = await connection.createTerminal({...});
// Automatically released when out of scope
```

<Warning>
  Always call `release()` on terminals to free resources, or use `await using` for automatic cleanup.
</Warning>

## Best Practices

<AccordionGroup>
  <Accordion title="Handle Connection Closure">
    Always monitor connection status and handle cleanup:

    ```typescript theme={null}
    connection.signal.addEventListener('abort', () => {
      // Clean up resources
      closeDatabase();
      saveState();
    });
    ```
  </Accordion>

  <Accordion title="Validate Messages">
    The SDK automatically validates messages using Zod schemas. Validation errors are returned as JSON-RPC errors.
  </Accordion>

  <Accordion title="Stream Progress">
    Send frequent updates to keep users informed:

    ```typescript theme={null}
    await connection.sessionUpdate({
      sessionId,
      content: { type: "text", text: "Analyzing code..." }
    });

    // Do work

    await connection.sessionUpdate({
      sessionId,
      content: { type: "text", text: "Generating fix..." }
    });
    ```
  </Accordion>

  <Accordion title="Use AbortSignal">
    Pass the connection signal to operations for automatic cancellation:

    ```typescript theme={null}
    await fetch(url, { signal: connection.signal });
    ```
  </Accordion>
</AccordionGroup>

## Learn More

<CardGroup cols={2}>
  <Card title="Protocol Overview" icon="book" href="/concepts/protocol-overview">
    Understanding the ACP specification
  </Card>

  <Card title="Agents and Clients" icon="users" href="/concepts/agents-and-clients">
    Learn about the two sides of ACP
  </Card>

  <Card title="Sessions" icon="comments" href="/concepts/sessions">
    Managing conversation contexts
  </Card>

  <Card title="Build an Agent" icon="robot" href="/examples/simple-agent">
    Complete agent implementation guide
  </Card>
</CardGroup>
