chuk-acp


Namechuk-acp JSON
Version 0.3.0 PyPI version JSON
download
home_pageNone
SummaryAgent Client Protocol (ACP) implementation for Python
upload_time2025-11-09 12:18:32
maintainerNone
docs_urlNone
authorNone
requires_python>=3.11
licenseApache-2.0
keywords acp agent-client-protocol ai coding-agent editor
VCS
bugtrack_url
requirements No requirements were recorded.
Travis-CI No Travis.
coveralls test coverage No coveralls.
            # chuk-acp

[![CI](https://github.com/chuk-ai/chuk-acp/actions/workflows/ci.yml/badge.svg)](https://github.com/chuk-ai/chuk-acp/actions/workflows/ci.yml)
[![PyPI version](https://badge.fury.io/py/chuk-acp.svg)](https://badge.fury.io/py/chuk-acp)
[![Python 3.11+](https://img.shields.io/badge/python-3.11+-blue.svg)](https://www.python.org/downloads/)
[![License](https://img.shields.io/badge/License-Apache_2.0-blue.svg)](https://opensource.org/licenses/Apache-2.0)
[![Code style: black](https://img.shields.io/badge/code%20style-black-000000.svg)](https://github.com/psf/black)
[![Ruff](https://img.shields.io/endpoint?url=https://raw.githubusercontent.com/astral-sh/ruff/main/assets/badge/v2.json)](https://github.com/astral-sh/ruff)
[![codecov](https://codecov.io/gh/chuk-ai/chuk-acp/branch/main/graph/badge.svg)](https://codecov.io/gh/chuk-ai/chuk-acp)

A Python implementation of the [Agent Client Protocol (ACP)](https://agentclientprotocol.com) - the standard protocol for communication between code editors and AI coding agents.

---

## πŸ“– Table of Contents

- [Overview](#overview)
- [Why ACP?](#why-acp)
- [Features](#features)
- [Installation](#installation)
- [Quick Start](#quick-start)
  - [CLI Tool](#cli-tool)
  - [Building an Agent](#building-an-agent)
  - [Building a Client](#building-a-client)
- [Core Concepts](#core-concepts)
- [Complete Examples](#complete-examples)
- [API Reference](#api-reference)
- [Protocol Support](#protocol-support)
- [Architecture](#architecture)
- [Testing](#testing)
- [Relationship to MCP](#relationship-to-mcp)
- [Contributing](#contributing)
- [License](#license)
- [Links](#links)

---

## Overview

The **Agent Client Protocol (ACP)** is to AI coding agents what the Language Server Protocol (LSP) is to programming languages. It standardizes communication between code editors/IDEs and coding agentsβ€”programs that use generative AI to autonomously modify code.

**chuk-acp** provides a complete, production-ready Python implementation of ACP, enabling you to:

- πŸ’¬ **Interact with agents instantly** using the CLI (`uvx chuk-acp claude-code-acp` or `echo "hello" | uvx chuk-acp client -- kimi --acp`)
- πŸ€– **Build ACP-compliant coding agents** easily with the high-level `ACPAgent` API
- πŸ–₯️ **Build editors/IDEs** that can connect to any ACP-compliant agent with `ACPClient`
- πŸ”Œ **Integrate AI capabilities** into existing development tools
- πŸ§ͺ **Test and develop** against the ACP specification

---

## Why ACP?

### The Problem

Without a standard protocol, every AI coding tool creates its own proprietary interface, leading to:

- Fragmentation across different tools and editors
- Inability to switch agents or editors without rewriting integration code
- Duplicated effort implementing similar functionality
- Limited interoperability

### The Solution

ACP provides a **standard, open protocol** that:

- βœ… Enables **any agent to work with any editor**
- βœ… Provides **consistent user experience** across tools
- βœ… Allows **innovation at both the editor and agent level**
- βœ… Built on proven standards (JSON-RPC 2.0)
- βœ… Supports **async/streaming** for real-time AI interactions

Think LSP for language tooling, but for AI coding agents.

---

## Features

### 🎯 Complete ACP Implementation

- Full support for ACP v1 specification
- All baseline methods and content types
- Optional capabilities (modes, session loading, file system, terminal)
- Protocol compliance test suite
- **MCP Servers Support**: Automatically sends empty `mcpServers: []` for compatibility with agents that require it
- **Flexible AgentInfo**: Handles agents that return incomplete initialization data

### πŸ”§ Developer-Friendly

- **CLI Tool**: Interactive command-line client for testing agents (`uvx chuk-acp`)
- **Zero Installation**: Run with `uvx` - no setup required
- **Type-Safe**: Comprehensive type hints throughout
- **Async-First**: Built on `anyio` for efficient async/await patterns
- **Optional Pydantic**: Use Pydantic for validation, or go dependency-free with fallback
- **Well-Documented**: Extensive examples and API documentation
- **Production-Ready**: Tested across Python 3.11, 3.12 on Linux, macOS, Windows

### πŸš€ Flexible & Extensible

- **Multiple transports**: Stdio (with more coming)
- **Custom methods**: Extend protocol with `_meta` fields and custom methods
- **Pluggable**: Easy to integrate into existing tools
- **MCP Integration**: Seamless compatibility with Model Context Protocol

### πŸ›‘οΈ Quality & Security

- Comprehensive test coverage
- Security scanning with Bandit and CodeQL
- Type checking with mypy
- Automated dependency updates
- CI/CD with GitHub Actions

---

## Quick Start (60 Seconds!)

### πŸš€ Try Without Installation

The absolute fastest way to get started - no cloning, no installation:

```bash
# 1. Download a standalone agent example
curl -O https://raw.githubusercontent.com/chuk-ai/chuk-acp/main/examples/standalone_agent.py

# 2. Run it with uvx (automatically installs chuk-acp temporarily)
uvx chuk-acp client python standalone_agent.py

# That's it! Start chatting with your agent.
```

See [QUICKSTART.md](QUICKSTART.md) for full details.

### Or Connect to External Agents

```bash
# Claude Code (requires ANTHROPIC_API_KEY)
ANTHROPIC_API_KEY=sk-... uvx chuk-acp claude-code-acp

# Kimi (Chinese AI assistant)
uvx chuk-acp client -- kimi --acp
```

**That's it!** `uvx` automatically handles installation. Perfect for quick testing.

---

## Installation

### Using uvx (Recommended for One-Off Usage)

No installation needed! `uvx` runs the CLI directly:

```bash
# Single prompt mode (interactive with stdin)
echo "Create a Python function to calculate fibonacci" | uvx chuk-acp client -- kimi --acp

# With faster validation (optional)
uvx --from 'chuk-acp[pydantic]' chuk-acp claude-code-acp
```

### Using uv (Recommended for Development)

[uv](https://github.com/astral-sh/uv) is a fast Python package installer:

```bash
# Basic installation (includes CLI)
uv pip install chuk-acp

# With Pydantic validation support (recommended for better performance)
uv pip install chuk-acp[pydantic]

# Or add to your project
uv add chuk-acp
```

### Using pip

```bash
# Basic installation (includes CLI)
pip install chuk-acp

# With Pydantic support (recommended)
pip install chuk-acp[pydantic]
```

### Development Installation

```bash
git clone https://github.com/chuk-ai/chuk-acp.git
cd chuk-acp
uv venv
source .venv/bin/activate  # On Windows: .venv\Scripts\activate
uv pip install -e ".[dev,pydantic]"
```

### Requirements

- Python 3.11 or higher
- Dependencies: `anyio`, `typing-extensions`
- Optional: `pydantic` (for faster validation - works without it using fallback mechanism)

---

## Quick Start

### CLI Tool - Interactive Chat with Any Agent

The easiest way to interact with ACP agents is using the built-in CLI. Works instantly with `uvx` or after installation.

The CLI supports three modes:

1. **Agent passthrough** - For editors/tools (explicitly use `agent` subcommand)
2. **Interactive client** - For humans (explicitly use `client` subcommand)
3. **Auto-detect** - Smart mode selection (TTY = interactive, piped = passthrough)

#### Try It Now (No Installation!)

```bash
# Connect to Claude Code (requires ANTHROPIC_API_KEY)
ANTHROPIC_API_KEY=sk-... uvx chuk-acp client claude-code-acp

# Connect to Kimi agent (use -- to separate flags)
echo "hello" | uvx chuk-acp client -- kimi --acp

# Interactive mode with verbose output
echo "your question" | uvx chuk-acp --verbose client -- kimi --acp

# Interactive chat opens automatically
# Just start typing your questions!
```

#### After Installation

```bash
# Interactive client mode (explicit)
chuk-acp client python examples/echo_agent.py

# Agent passthrough mode (for editors like Zed)
chuk-acp agent python my_agent.py

# Auto-detect mode (interactive if TTY, passthrough if piped)
chuk-acp python examples/echo_agent.py

# Single prompt (requires stdin for kimi)
echo "Create a Python function to calculate factorial" | chuk-acp client -- kimi --acp

# Using a config file
chuk-acp --config examples/kimi_config.json

# With environment variables
chuk-acp client python agent.py --env DEBUG=true --env API_KEY=xyz

# Force interactive mode even when piped
chuk-acp --interactive python agent.py

# Verbose output for debugging
chuk-acp client python agent.py --verbose
```

#### Interactive Mode Commands

When in interactive chat mode, you can use these special commands:

| Command | Description |
|---------|-------------|
| `/quit` or `/exit` | Exit the client |
| `/new` | Start a new session (clears context) |
| `/info` | Show agent information and session ID |

#### Example Interactive Session

**With Claude Code:**
```bash
$ ANTHROPIC_API_KEY=sk-... uvx chuk-acp claude-code-acp

╔═══════════════════════════════════════════════════════════════╗
β•‘           ACP Interactive Client                              β•‘
β•šβ•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•

You: Create a Python function to check if a string is a palindrome

Agent: Here's a Python function to check if a string is a palindrome:

def is_palindrome(s):
    # Remove spaces and convert to lowercase
    s = s.replace(" ", "").lower()
    # Check if string equals its reverse
    return s == s[::-1]

You: /quit
Goodbye!
```

**With Kimi:**
```bash
$ echo "What's the best way to handle async errors in Python?" | uvx chuk-acp client -- kimi --acp

Agent: [Kimi's response with thinking and detailed explanation...]
```

#### Configuration Files

Use standard ACP configuration format (compatible with Zed, VSCode, etc.):

**claude_code_config.json:**
```json
{
  "command": "claude-code-acp",
  "args": [],
  "env": {
    "ANTHROPIC_API_KEY": "sk-..."
  }
}
```

**kimi_config.json:**
```json
{
  "command": "kimi",
  "args": ["--acp"],
  "env": {}
}
```

Then use with:
```bash
chuk-acp --config claude_code_config.json
chuk-acp --config kimi_config.json
```

#### Important: Using `--` for Agent Flags

When an agent requires flags (like `kimi --acp`), use `--` to separate chuk-acp flags from agent arguments:

```bash
# Correct - use -- separator
chuk-acp client -- kimi --acp

# Also works via config file
chuk-acp --config kimi_config.json
```

Without `--`, argparse treats `--acp` as a chuk-acp flag and fails. Using a config file avoids this issue entirely.

**πŸ“– See [CLI.md](CLI.md) for complete CLI documentation and advanced usage.**

### Using with Editors (Zed, VSCode, etc.)

chuk-acp can run your custom agents in editors that support ACP, like Zed. The `agent` mode provides a passthrough that lets editors communicate directly with your agent via the ACP protocol.

#### Zed Configuration

Add this to your Zed `settings.json` (usually `~/.config/zed/settings.json`):

```json
{
  "agent_servers": {
    "My Custom Agent": {
      "command": "uvx",
      "args": [
        "chuk-acp",
        "agent",
        "python",
        "/absolute/path/to/my_agent.py"
      ],
      "env": {}
    }
  }
}
```

Or using auto-detect mode (will automatically use passthrough when piped from Zed):

```json
{
  "agent_servers": {
    "My Custom Agent": {
      "command": "uvx",
      "args": [
        "chuk-acp",
        "python",
        "/absolute/path/to/my_agent.py"
      ],
      "env": {}
    }
  }
}
```

**What this does:**
- `uvx` automatically installs `chuk-acp` in a temporary environment
- `chuk-acp agent` runs in passthrough mode (just executes your agent with the ACP protocol layer)
- Your agent gets access to the `chuk-acp` package (so `from chuk_acp.agent import ACPAgent` works)
- No `--from` or `--with` flags needed!

**Example agent file** (`my_agent.py`):

```python
from typing import List
from chuk_acp.agent import ACPAgent, AgentSession
from chuk_acp.protocol.types import AgentInfo, Content

class MyAgent(ACPAgent):
    def get_agent_info(self) -> AgentInfo:
        return AgentInfo(name="my-agent", version="1.0.0")

    async def handle_prompt(self, session: AgentSession, prompt: List[Content]) -> str:
        text = prompt[0].get("text", "") if prompt else ""
        return f"You said: {text}"

if __name__ == "__main__":
    MyAgent().run()
```

See [`examples/zed_config.json`](examples/zed_config.json) for more configuration examples.

### The Easiest Way: ACPClient

The fastest way to get started programmatically is with the high-level `ACPClient`, which handles all protocol details automatically:

**Option A: Direct Usage**
```python
"""quickstart.py"""
import anyio
from chuk_acp import ACPClient

async def main():
    # Connect to an agent - handles initialization, sessions, everything!
    async with ACPClient("python", ["echo_agent.py"]) as client:
        # Send a prompt and get the response
        result = await client.send_prompt("Hello!")
        print(f"Agent: {result.full_message}")

anyio.run(main)
```

**Option B: Using Standard ACP Configuration**

This matches the configuration format used by editors like Zed, VSCode, etc.:

```python
"""quickstart_config.py"""
import anyio
from chuk_acp import ACPClient, AgentConfig

async def main():
    # Standard ACP configuration format
    config = AgentConfig(
        command="kimi",           # Any ACP-compatible agent
        args=["--acp"],          # Agent-specific arguments
        env={"DEBUG": "true"}    # Optional environment variables
    )

    async with ACPClient.from_config(config) as client:
        result = await client.send_prompt("Hello!")
        print(f"Agent: {result.full_message}")

anyio.run(main)
```

Or load from a JSON file (like `~/.config/zed/settings.json`):

```python
from chuk_acp import load_agent_config

config = load_agent_config("~/.config/my-app/agent.json")
async with ACPClient.from_config(config) as client:
    result = await client.send_prompt("Hello!")
```

**What `ACPClient` does automatically:**
- βœ… Starts the agent process
- βœ… Handles protocol initialization
- βœ… Creates and manages sessions
- βœ… Captures all notifications
- βœ… Cleans up resources
- βœ… Supports standard ACP configuration format

**Want more control?** The low-level API gives you fine-grained control over the protocol. See the examples below.

---

### Building an Agent

The fastest way to build an ACP agent is with the high-level `ACPAgent` class:

```python
"""my_agent.py"""
from typing import List
from chuk_acp.agent import ACPAgent, AgentSession
from chuk_acp.protocol.types import AgentInfo, Content

class MyAgent(ACPAgent):
    """Your custom agent implementation."""

    def get_agent_info(self) -> AgentInfo:
        """Return agent information."""
        return AgentInfo(
            name="my-agent",
            version="1.0.0",
            title="My Custom Agent"
        )

    async def handle_prompt(
        self, session: AgentSession, prompt: List[Content]
    ) -> str:
        """Handle a prompt - this is where your agent logic goes."""
        # Extract text from prompt
        text = prompt[0].get("text", "") if prompt else ""

        # Your agent logic here
        response = f"I received: {text}"

        # Return the response
        return response

if __name__ == "__main__":
    agent = MyAgent()
    agent.run()
```

**Run your agent:**
```bash
# Test with CLI (using uv for environment management)
uvx chuk-acp client uv run my_agent.py

# Or if chuk-acp is installed globally
chuk-acp client python my_agent.py
```

**What `ACPAgent` does automatically:**
- βœ… Handles all protocol messages (initialize, session/new, session/prompt)
- βœ… Manages sessions and routing
- βœ… Sends responses in correct format
- βœ… Error handling and logging
- βœ… Stdin/stdout transport

**Real example:** See [`examples/echo_agent.py`](examples/echo_agent.py) - a complete working agent in just 35 lines!

#### πŸš€ Even Easier: chuk-acp-agent

For an even simpler agent-building experience, check out **[chuk-acp-agent](https://github.com/chrishayuk/chuk-acp-agent)** - an opinionated agent kit built on top of chuk-acp:

```python
from chuk_acp_agent import Agent, Context

class MyAgent(Agent):
    async def on_prompt(self, ctx: Context, prompt: str):
        # Session memory
        count = ctx.memory.get("count", 0) + 1
        ctx.memory.set("count", count)

        # Stream response
        yield f"Message #{count}: {prompt}\n"

if __name__ == "__main__":
    MyAgent().run()
```

**Features:**
- πŸ“¦ Batteries-included: session memory, streaming helpers, MCP integration
- 🎯 Minimal boilerplate: just implement `on_prompt()`
- πŸ”§ Context API: `ctx.memory`, `ctx.emit()`, `ctx.fs`, `ctx.terminal`
- πŸ› οΈ Built-in MCP tool support via `chuk-tool-processor`

Install: `pip install chuk-acp-agent` or see the [chuk-acp-agent docs](https://github.com/chrishayuk/chuk-acp-agent).

---

### More Examples

For more complete examples showing different use cases:

```bash
# Clone the repository
git clone https://github.com/chuk-ai/chuk-acp.git
cd chuk-acp

# Install
uv pip install -e ".[pydantic]"

# Run examples (all use the high-level ACPClient)
uv run python examples/simple_client.py   # Basic single prompt
uv run python examples/quick_start.py     # Multi-turn conversation
uv run python examples/config_example.py  # Configuration support (Zed/VSCode format)

# Advanced: Low-level protocol examples
uv run python examples/low_level/simple_client.py        # Manual protocol handling
uv run python examples/low_level/quick_start.py          # Self-contained with embedded agent
uv run python examples/low_level/comprehensive_demo.py   # All ACP features
```

See the [examples directory](https://github.com/chuk-ai/chuk-acp/tree/main/examples) for detailed documentation.

> **Note**: Examples are in the GitHub repository. If you installed via pip, clone the repo to access them.

### Option B: Build Your Own (10 Minutes)

Create a complete ACP client and agent from scratch.

#### Step 1: Install

```bash
uv pip install chuk-acp[pydantic]
```

#### Step 2: Create an Agent

Save this as `echo_agent.py`:

```python
"""echo_agent.py - A simple ACP agent"""
import json
import sys
import uuid

from chuk_acp.protocol import (
    create_response,
    create_notification,
    METHOD_INITIALIZE,
    METHOD_SESSION_NEW,
    METHOD_SESSION_PROMPT,
    METHOD_SESSION_UPDATE,
)
from chuk_acp.protocol.types import AgentInfo, AgentCapabilities, TextContent

# Read messages from stdin, write to stdout
for line in sys.stdin:
    msg = json.loads(line.strip())
    method = msg.get("method")
    params = msg.get("params", {})
    msg_id = msg.get("id")

    # Route to handlers
    if method == METHOD_INITIALIZE:
        result = {
            "protocolVersion": 1,
            "agentInfo": AgentInfo(name="echo-agent", version="1.0.0").model_dump(),
            "agentCapabilities": AgentCapabilities().model_dump(),
        }
        response = create_response(id=msg_id, result=result)

    elif method == METHOD_SESSION_NEW:
        session_id = f"session-{uuid.uuid4().hex[:8]}"
        response = create_response(id=msg_id, result={"sessionId": session_id})

    elif method == METHOD_SESSION_PROMPT:
        session_id = params["sessionId"]
        user_text = params["prompt"][0].get("text", "")

        # Send a notification with the echo
        from chuk_acp.protocol.types import SessionUpdate

        session_update = SessionUpdate(
            sessionUpdate="agent_message_chunk",
            content=TextContent(text=f"Echo: {user_text}")
        )
        notification = create_notification(
            method=METHOD_SESSION_UPDATE,
            params={
                "sessionId": session_id,
                "update": session_update.model_dump(exclude_none=True),
            },
        )
        sys.stdout.write(json.dumps(notification.model_dump()) + "\n")
        sys.stdout.flush()

        # Send the response
        response = create_response(id=msg_id, result={"stopReason": "end_turn"})

    else:
        continue

    sys.stdout.write(json.dumps(response.model_dump()) + "\n")
    sys.stdout.flush()
```

#### Step 3: Create a Client

Save this as `my_client.py`:

```python
"""my_client.py - Connect to the echo agent using ACPClient"""
import anyio
from chuk_acp import ACPClient


async def main():
    # Connect to the agent - handles everything automatically!
    async with ACPClient("python", ["echo_agent.py"]) as client:
        # Send a prompt and get the response
        result = await client.send_prompt("Hello!")
        print(f"Agent says: {result.full_message}")


if __name__ == "__main__":
    anyio.run(main())
```

#### Step 4: Run It!

```bash
uv run python my_client.py
```

**Output:**
```
βœ“ Connected to echo-agent
βœ“ Session: session-a1b2c3d4

Sending: Hello!
Agent says: Echo: Hello!
βœ“ Done!
```

πŸŽ‰ **That's it!** You've built a working ACP agent and client.

### What You Learned

**Option A** showed you the fastest path - running pre-built examples.

**Option B** taught you:
- **Agents**: Read JSON-RPC from stdin, write to stdout using `create_response()` and `create_notification()`
- **Clients**: Connect via `stdio_transport`, use `send_initialize()` and `send_session_new()`, manually handle messages to capture notifications
- **Protocol flow**: Initialize β†’ Create Session β†’ Send Prompts (with notifications) β†’ Get Response
- **Best practices**: Use library types (`TextContent`, `AgentInfo`) and method constants (`METHOD_INITIALIZE`)

### Next Steps

**Explore More Features:**

Check out the complete examples in the [GitHub repository](https://github.com/chuk-ai/chuk-acp/tree/main/examples):
- [simple_client.py](https://github.com/chuk-ai/chuk-acp/blob/main/examples/simple_client.py) - Clean client with notification handling
- [echo_agent.py](https://github.com/chuk-ai/chuk-acp/blob/main/examples/echo_agent.py) - Production-ready agent with error handling
- [comprehensive_demo.py](https://github.com/chuk-ai/chuk-acp/blob/main/examples/comprehensive_demo.py) - Filesystem, terminal, all ACP features

**Build Something:**
- Add file system access to your agent (see Example 3 below)
- Implement tool calls and permission requests
- Support multiple concurrent sessions
- Add streaming for long responses

**Learn More:**
- [API Reference](#api-reference) - Complete API documentation
- [Protocol Support](#protocol-support) - What's supported in ACP v1
- [ACP Specification](https://agentclientprotocol.com) - Official protocol docs

---

## Core Concepts

### The Agent-Client Model

```
β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”                  β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚   Client    β”‚ ←── JSON-RPC ──→ β”‚    Agent     β”‚
β”‚  (Editor)   β”‚     over stdio   β”‚  (AI Tool)   β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜                  β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
      ↑                                 ↑
      β”‚                                 β”‚
  User Interface                   AI Model
  File System                      Code Analysis
  Permissions                      Code Generation
```

### Key Components

#### 1. **Protocol Layer** (`chuk_acp.protocol`)

The core protocol implementation:

- **JSON-RPC 2.0**: Request/response and notification messages
- **Message Types**: Initialize, session management, prompts
- **Content Types**: Text, images, audio, resources, annotations
- **Capabilities**: Negotiate features between client and agent

#### 2. **Transport Layer** (`chuk_acp.transport`)

Communication mechanism:

- **Stdio Transport**: Process-based communication (current)
- **Extensible**: WebSocket, HTTP, etc. (future)

#### 3. **Type System** (`chuk_acp.protocol.types`)

Strongly-typed protocol structures:

- Content types (text, image, audio)
- Capabilities and features
- Session modes and states
- Tool calls and permissions

### The ACP Flow

```
1. INITIALIZE
   Client ──→ Agent: Protocol version, capabilities
   Agent  ──→ Client: Agent info, supported features

2. SESSION CREATION
   Client ──→ Agent: Working directory, MCP servers
   Agent  ──→ Client: Session ID

3. PROMPT TURN
   Client ──→ Agent: User prompt (text, images, etc.)
   Agent  ──→ Client: [Streaming updates]
   Agent  ──→ Client: Stop reason (end_turn, max_tokens, etc.)

4. ONGOING INTERACTION
   - Session updates (thoughts, tool calls, messages)
   - Permission requests (file access, terminal, etc.)
   - Mode changes (ask β†’ code β†’ architect)
   - Cancellation support
```

---

## Complete Examples

### Example 1: Echo Agent (Using Library)

A minimal agent that echoes user input using chuk-acp library helpers:

```python
"""echo_agent.py - Agent using chuk-acp library"""
import json
import sys
import uuid
from typing import Dict, Any

from chuk_acp.protocol import (
    create_response,
    create_error_response,
    create_notification,
    METHOD_INITIALIZE,
    METHOD_SESSION_NEW,
    METHOD_SESSION_PROMPT,
    METHOD_SESSION_UPDATE,
)
from chuk_acp.protocol.types import (
    AgentInfo,
    AgentCapabilities,
    SessionUpdate,
    TextContent,
)

class EchoAgent:
    def __init__(self):
        self.sessions = {}

    def handle_initialize(self, params: Dict[str, Any]) -> Dict[str, Any]:
        """Use library types instead of manual dict construction."""
        agent_info = AgentInfo(name="echo-agent", version="0.1.0")
        agent_capabilities = AgentCapabilities()

        return {
            "protocolVersion": 1,
            "agentInfo": agent_info.model_dump(exclude_none=True),
            "agentCapabilities": agent_capabilities.model_dump(exclude_none=True),
        }

    def handle_session_new(self, params: Dict[str, Any]) -> Dict[str, Any]:
        session_id = f"session_{uuid.uuid4().hex[:8]}"
        self.sessions[session_id] = {"cwd": params.get("cwd")}
        return {"sessionId": session_id}

    def handle_session_prompt(self, params: Dict[str, Any]) -> Dict[str, Any]:
        session_id = params["sessionId"]
        prompt = params["prompt"]

        # Use library helpers to create notification
        text_content = TextContent(
            text=f"Echo: You said '{prompt[0].get('text', '')}'"
        )

        session_update = SessionUpdate(
            sessionUpdate="agent_message_chunk",
            content=text_content
        )

        notification = create_notification(
            method=METHOD_SESSION_UPDATE,
            params={
                "sessionId": session_id,
                "update": session_update.model_dump(exclude_none=True),
            },
        )

        sys.stdout.write(json.dumps(notification.model_dump(exclude_none=True)) + "\n")
        sys.stdout.flush()

        return {"stopReason": "end_turn"}

    def run(self):
        for line in sys.stdin:
            message = json.loads(line.strip())
            method = message.get("method")
            msg_id = message.get("id")

            try:
                # Route to handler using method constants
                if method == METHOD_INITIALIZE:
                    result = self.handle_initialize(message.get("params", {}))
                elif method == METHOD_SESSION_NEW:
                    result = self.handle_session_new(message.get("params", {}))
                elif method == METHOD_SESSION_PROMPT:
                    result = self.handle_session_prompt(message.get("params", {}))
                else:
                    raise Exception(f"Unknown method: {method}")

                # Use library helper to create response
                response = create_response(id=msg_id, result=result)
            except Exception as e:
                # Use library helper for error responses
                response = create_error_response(id=msg_id, code=-32603, message=str(e))

            sys.stdout.write(json.dumps(response.model_dump(exclude_none=True)) + "\n")
            sys.stdout.flush()

if __name__ == "__main__":
    EchoAgent().run()
```

> **Note**: This demonstrates using the library's protocol helpers (`create_response`, `create_notification`, `TextContent`, etc.) instead of manual JSON construction. See `examples/echo_agent.py` for the complete implementation.

### Example 2: Client with Session Updates

Capture and handle streaming updates from agent:

```python
"""client_with_updates.py - Capture session/update notifications"""
import asyncio
import uuid

import anyio

from chuk_acp import (
    stdio_transport,
    send_initialize,
    send_session_new,
    ClientInfo,
    ClientCapabilities,
    TextContent,
)
from chuk_acp.protocol import (
    create_request,
    JSONRPCNotification,
    JSONRPCResponse,
    METHOD_SESSION_PROMPT,
    METHOD_SESSION_UPDATE,
)

async def main():
    async with stdio_transport("python", ["examples/echo_agent.py"]) as (read, write):
        # Initialize
        init_result = await send_initialize(
            read, write,
            protocol_version=1,
            client_info=ClientInfo(name="client", version="1.0.0"),
            capabilities=ClientCapabilities()
        )
        print(f"Connected to {init_result.agentInfo.name}")

        # Create session
        session = await send_session_new(read, write, cwd="/tmp")

        # Send prompt and capture notifications
        prompt_text = "Write a hello world function"
        print(f"User: {prompt_text}")

        request_id = str(uuid.uuid4())
        request = create_request(
            method=METHOD_SESSION_PROMPT,
            params={
                "sessionId": session.sessionId,
                "prompt": [TextContent(text=prompt_text).model_dump(exclude_none=True)],
            },
            id=request_id,
        )
        await write.send(request)

        # Collect notifications and response
        agent_messages = []
        stop_reason = None

        with anyio.fail_after(60.0):
            while stop_reason is None:
                message = await read.receive()

                # Handle session/update notifications
                if isinstance(message, JSONRPCNotification):
                    if message.method == METHOD_SESSION_UPDATE:
                        params = message.params or {}

                        # Agent message chunks
                        update = params.get("update", {})
                        if update.get("sessionUpdate") == "agent_message_chunk":
                            content = update.get("content", {})
                            if isinstance(content, dict) and "text" in content:
                                agent_messages.append(content["text"])

                        # Thoughts (optional)
                        if "thought" in params:
                            print(f"[Thinking: {params['thought']}]")

                        # Tool calls (optional)
                        if "toolCall" in params:
                            tool = params["toolCall"]
                            print(f"[Calling: {tool.get('name')}]")

                # Handle response
                elif isinstance(message, JSONRPCResponse):
                    if message.id == request_id:
                        result = message.result
                        if isinstance(result, dict):
                            stop_reason = result.get("stopReason")

        # Display captured agent messages
        if agent_messages:
            print(f"Agent: {''.join(agent_messages)}")

        print(f"Completed: {stop_reason}")

asyncio.run(main())
```

> **Key Point**: To capture `session/update` notifications, you need to manually handle the request/response loop instead of using `send_session_prompt()`, which discards notifications. See `examples/simple_client.py` for a complete working example.

### Example 3: Agent with File System Access

Agent that can read/write files:

```python
"""file_agent.py - Agent with filesystem capabilities"""
from chuk_acp.protocol.types import AgentCapabilities

# Declare filesystem capabilities
capabilities = AgentCapabilities(
    filesystem=True  # Enables fs/read_text_file and fs/write_text_file
)

async def handle_file_operation(session_id: str, operation: str, path: str):
    """Request file access from client."""

    # Request permission
    permission = await send_session_request_permission(
        read, write,
        session_id=session_id,
        request=PermissionRequest(
            id="perm-123",
            description=f"Read file: {path}",
            tools=[{"name": "fs/read_text_file", "arguments": {"path": path}}]
        )
    )

    if permission.granted:
        # Read the file via client
        # (Client implements fs/read_text_file method)
        pass
```

### Example 4: Multi-Session Client

Manage multiple concurrent sessions:

```python
"""multi_session_client.py"""
import asyncio
from chuk_acp import stdio_transport, send_session_new, send_session_prompt

async def create_and_run_session(read, write, cwd: str, prompt: str):
    """Create a session and send a prompt."""
    session = await send_session_new(read, write, cwd=cwd)
    result = await send_session_prompt(
        read, write,
        session_id=session.sessionId,
        prompt=[TextContent(text=prompt)]
    )
    return result

async def main():
    async with stdio_transport("python", ["my_agent.py"]) as (read, write):
        # Initialize once
        await send_initialize(...)

        # Run multiple sessions concurrently
        tasks = [
            create_and_run_session(read, write, "/project1", "Refactor auth"),
            create_and_run_session(read, write, "/project2", "Add tests"),
            create_and_run_session(read, write, "/project3", "Fix bug #123"),
        ]

        results = await asyncio.gather(*tasks)
        print(f"Completed {len(results)} sessions")

asyncio.run(main())
```

---

## API Reference

### High-Level Client

The `ACPClient` provides the simplest way to interact with ACP agents:

#### Direct Usage

```python
from chuk_acp import ACPClient

async with ACPClient("python", ["agent.py"]) as client:
    # Access agent information
    print(f"Agent: {client.agent_info.name}")
    print(f"Session: {client.current_session.sessionId}")

    # Send prompts
    result = await client.send_prompt("Hello!")
    print(result.full_message)  # Complete agent response
    print(result.stop_reason)   # Why agent stopped

    # Create new sessions
    new_session = await client.new_session(cwd="/other/path")
```

#### Configuration-Based Usage

Use standard ACP configuration format (compatible with Zed, VSCode, etc.):

```python
from chuk_acp import ACPClient, AgentConfig, load_agent_config

# Method 1: Create config directly
config = AgentConfig(
    command="kimi",
    args=["--acp"],
    env={"DEBUG": "true"},
    cwd="/optional/path"
)

async with ACPClient.from_config(config) as client:
    result = await client.send_prompt("Hello!")

# Method 2: Load from JSON file
config = load_agent_config("~/.config/my-app/agent.json")
async with ACPClient.from_config(config) as client:
    result = await client.send_prompt("Hello!")

# Method 3: From dictionary (like editor configs)
config = AgentConfig(**{
    "command": "kimi",
    "args": ["--acp"],
    "env": {}
})
async with ACPClient.from_config(config) as client:
    result = await client.send_prompt("Hello!")
```

**Example JSON config file:**
```json
{
  "command": "kimi",
  "args": ["--acp"],
  "env": {
    "DEBUG": "true",
    "LOG_LEVEL": "info"
  },
  "cwd": "/optional/path"
}
```

**Key Classes:**
- `ACPClient` - Main client class
- `AgentConfig` - Standard ACP configuration format
- `load_agent_config()` - Load config from JSON file
- `PromptResult` - Contains response and all notifications
- `SessionInfo` - Session information
- `SessionUpdate` - Individual notification from agent

### Low-Level Protocol API

For fine-grained control over the protocol:

### Protocol Helpers

#### JSON-RPC Message Helpers

Build protocol messages using library helpers:

```python
from chuk_acp.protocol import (
    create_request,
    create_response,
    create_error_response,
    create_notification,
)

# Create a request
request = create_request(
    method="session/prompt",
    params={"sessionId": "session-1", "prompt": [...]},
    id="req-123"
)

# Create a response
response = create_response(id="req-123", result={"stopReason": "end_turn"})

# Create an error response
error = create_error_response(id="req-123", code=-32603, message="Internal error")

# Create a notification
from chuk_acp.protocol.types import SessionUpdate, TextContent

session_update = SessionUpdate(
    sessionUpdate="agent_message_chunk",
    content=TextContent(text="Hello!")
)
notification = create_notification(
    method="session/update",
    params={
        "sessionId": "session-1",
        "update": session_update.model_dump(exclude_none=True)
    }
)
```

#### Method Constants

Use constants instead of string literals for protocol methods:

```python
from chuk_acp.protocol import (
    METHOD_INITIALIZE,
    METHOD_SESSION_NEW,
    METHOD_SESSION_PROMPT,
    METHOD_SESSION_UPDATE,
    METHOD_SESSION_CANCEL,
    METHOD_FS_READ_TEXT_FILE,
    METHOD_FS_WRITE_TEXT_FILE,
    METHOD_TERMINAL_CREATE,
    # ... and more
)

# Use in message routing
if method == METHOD_INITIALIZE:
    # Handle initialize
    pass
elif method == METHOD_SESSION_PROMPT:
    # Handle prompt
    pass
```

### Transport

#### `stdio_transport(command, args)`

Create a stdio transport connection to an agent.

```python
async with stdio_transport("python", ["agent.py"]) as (read_stream, write_stream):
    # Use streams for communication
    pass
```

### Initialization

#### `send_initialize(read, write, protocol_version, client_info, capabilities)`

Initialize the connection and negotiate capabilities.

```python
result = await send_initialize(
    read_stream,
    write_stream,
    protocol_version=1,
    client_info=ClientInfo(name="my-client", version="1.0.0"),
    capabilities=ClientCapabilities(filesystem=True)
)
# result.agentInfo, result.capabilities, result.protocolVersion
```

### Session Management

#### `send_session_new(read, write, cwd, mcp_servers=None, mode=None)`

Create a new session.

```python
session = await send_session_new(
    read_stream,
    write_stream,
    cwd="/absolute/path",
    mode="code"  # Optional: ask, architect, code
)
# session.sessionId
```

#### `send_session_prompt(read, write, session_id, prompt)`

Send a prompt to the agent.

```python
result = await send_session_prompt(
    read_stream,
    write_stream,
    session_id="session-123",
    prompt=[
        TextContent(text="Write a function"),
        ImageContent(data="base64...", mimeType="image/png")
    ]
)
# result.stopReason: end_turn, max_tokens, cancelled, refusal
```

> **Note**: `send_session_prompt` discards `session/update` notifications from the agent. To capture agent responses (message chunks, thoughts, tool calls), manually handle the request/response loop. See Example 2 or `examples/simple_client.py` for details.

#### `send_session_cancel(write, session_id)`

Cancel an ongoing prompt turn.

```python
await send_session_cancel(write_stream, session_id="session-123")
```

### Content Types

#### `TextContent(text)`

Plain text content.

```python
content = TextContent(text="Hello, world!")
```

#### `ImageContent(data, mimeType)`

Base64-encoded image.

```python
content = ImageContent(
    data="iVBORw0KGgoAAAANSUhEUgA...",
    mimeType="image/png"
)
```

#### `AudioContent(data, mimeType)`

Base64-encoded audio.

```python
content = AudioContent(
    data="SUQzBAA...",
    mimeType="audio/mpeg"
)
```

---

## Protocol Support

chuk-acp implements the **complete ACP v1 specification**.

### βœ… Baseline Agent Methods (Required)

| Method | Description | Status |
|--------|-------------|--------|
| `initialize` | Protocol handshake and capability negotiation | βœ… |
| `authenticate` | Optional authentication | βœ… |
| `session/new` | Create new conversation sessions | βœ… |
| `session/prompt` | Process user prompts | βœ… |
| `session/cancel` | Cancel ongoing operations | βœ… |

### βœ… Optional Agent Methods

| Method | Capability | Status |
|--------|------------|--------|
| `session/load` | Resume previous sessions | βœ… |
| `session/set_mode` | Change session modes | βœ… |

### βœ… Client Methods (Callbacks)

| Method | Description | Status |
|--------|-------------|--------|
| `session/request_permission` | Request user approval for actions | βœ… |
| `fs/read_text_file` | Read file contents | βœ… |
| `fs/write_text_file` | Write file contents | βœ… |
| `terminal/create` | Create terminal sessions | βœ… |
| `terminal/output` | Stream terminal output | βœ… |
| `terminal/release` | Release terminal control | βœ… |
| `terminal/wait_for_exit` | Wait for command completion | βœ… |
| `terminal/kill` | Terminate running commands | βœ… |

### βœ… Content Types

- Text content (baseline - always supported)
- Image content (base64-encoded)
- Audio content (base64-encoded)
- Embedded resources
- Resource links
- Annotations

### βœ… Session Features

- Session management (create, load, cancel)
- Multiple parallel sessions
- Session modes: `ask`, `architect`, `code`
- Session history replay
- MCP server integration

### βœ… Tool Integration

- Tool calls with status tracking (`pending`, `in_progress`, `completed`, `failed`)
- Permission requests
- File location tracking
- Structured output (diffs, terminals, content)
- Slash commands (optional)

### βœ… Protocol Requirements

- **File paths**: All paths must be absolute βœ…
- **Line numbers**: 1-based indexing βœ…
- **JSON-RPC 2.0**: Strict compliance βœ…
- **Extensibility**: `_meta` fields and custom methods βœ…

---

## Architecture

### Project Structure

```
chuk-acp/
β”œβ”€β”€ src/chuk_acp/
β”‚   β”œβ”€β”€ protocol/              # Core protocol implementation
β”‚   β”‚   β”œβ”€β”€ jsonrpc.py         # JSON-RPC 2.0 (requests, responses, errors)
β”‚   β”‚   β”œβ”€β”€ acp_pydantic_base.py # Optional Pydantic support
β”‚   β”‚   β”œβ”€β”€ types/             # Protocol type definitions
β”‚   β”‚   β”‚   β”œβ”€β”€ content.py     # Content types (text, image, audio)
β”‚   β”‚   β”‚   β”œβ”€β”€ capabilities.py # Client/agent capabilities
β”‚   β”‚   β”‚   β”œβ”€β”€ session.py     # Session types and modes
β”‚   β”‚   β”‚   β”œβ”€β”€ tools.py       # Tool calls and permissions
β”‚   β”‚   β”‚   β”œβ”€β”€ plan.py        # Task planning types
β”‚   β”‚   β”‚   β”œβ”€β”€ terminal.py    # Terminal integration
β”‚   β”‚   β”‚   └── ...
β”‚   β”‚   └── messages/          # Message handling
β”‚   β”‚       β”œβ”€β”€ initialize.py  # Initialize/authenticate
β”‚   β”‚       β”œβ”€β”€ session.py     # Session management
β”‚   β”‚       β”œβ”€β”€ filesystem.py  # File operations
β”‚   β”‚       β”œβ”€β”€ terminal.py    # Terminal operations
β”‚   β”‚       └── send_message.py # Core messaging utilities
β”‚   β”‚
β”‚   β”œβ”€β”€ transport/             # Transport layer
β”‚   β”‚   β”œβ”€β”€ base.py            # Abstract transport interface
β”‚   β”‚   └── stdio.py           # Stdio transport (subprocess)
β”‚   β”‚
β”‚   └── __init__.py            # Public API exports
β”‚
β”œβ”€β”€ examples/                  # Working examples
β”‚   β”œβ”€β”€ echo_agent.py          # Simple echo agent
β”‚   β”œβ”€β”€ simple_client.py       # Basic client
β”‚   β”œβ”€β”€ quick_start.py         # Getting started
β”‚   └── comprehensive_demo.py  # Full-featured demo
β”‚
β”œβ”€β”€ tests/                     # Test suite
β”‚   β”œβ”€β”€ test_protocol_compliance.py  # Spec compliance
β”‚   β”œβ”€β”€ test_jsonrpc.py        # JSON-RPC tests
β”‚   β”œβ”€β”€ test_types.py          # Type system tests
β”‚   β”œβ”€β”€ test_messages.py       # Message handling
β”‚   └── test_stdio_transport.py # Transport tests
β”‚
└── .github/                   # CI/CD workflows
    β”œβ”€β”€ workflows/
    β”‚   β”œβ”€β”€ ci.yml             # Testing and linting
    β”‚   β”œβ”€β”€ publish.yml        # PyPI publishing
    β”‚   └── codeql.yml         # Security scanning
    └── ...
```

### Design Principles

1. **Protocol First**: Strict adherence to ACP specification
2. **Type Safety**: Comprehensive type hints throughout
3. **Optional Dependencies**: Pydantic is optional, not required
4. **Async by Default**: Built on `anyio` for async/await
5. **Extensibility**: Custom methods and `_meta` fields supported
6. **Testability**: Loosely coupled, dependency injection
7. **Zero-Config**: Works out of the box with sensible defaults

### Layer Separation

```
β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚     User Code (Agents/Clients)      β”‚
β”œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€
β”‚     High-Level API (messages/)      β”‚  ← send_initialize, send_prompt, etc.
β”œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€
β”‚    Protocol Layer (types/, jsonrpc) β”‚  ← Content types, capabilities
β”œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€
β”‚    Transport Layer (transport/)     β”‚  ← Stdio, future: WebSocket, HTTP
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
```

---

## Testing

### Running Tests

```bash
# Run all tests
make test

# Run with coverage
make test-cov

# Run specific test file
uv run pytest tests/test_protocol_compliance.py -v

# Test without Pydantic (fallback mode)
uv pip uninstall pydantic
uv run pytest
```

### Test Categories

- **Protocol Compliance** (`test_protocol_compliance.py`): Validates ACP spec adherence
- **JSON-RPC** (`test_jsonrpc.py`): JSON-RPC 2.0 implementation
- **Types** (`test_types.py`): Type system and content types
- **Messages** (`test_messages.py`): Message handling and serialization
- **Transport** (`test_stdio_transport.py`): Transport layer

### Code Quality Checks

```bash
# Format code
make format

# Lint
make lint

# Type check
make mypy

# Security scan
make security

# All checks
make check
```

---

## Relationship to MCP

**ACP** and **MCP** (Model Context Protocol) are complementary protocols:

| Protocol | Purpose | Focus |
|----------|---------|-------|
| **MCP** | What data/tools agents can access | Context & tools |
| **ACP** | Where the agent lives in your workflow | Agent lifecycle |

### Integration

ACP reuses MCP data structures for content types and resources:

```python
from chuk_acp.protocol.types import (
    TextContent,      # From MCP
    ImageContent,     # From MCP
    ResourceContent,  # From MCP
)

# ACP sessions can specify MCP servers
session = await send_session_new(
    read, write,
    cwd="/project",
    mcp_servers=[
        MCPServer(
            name="filesystem",
            command="npx",
            args=["-y", "@modelcontextprotocol/server-filesystem", "/path"]
        )
    ]
)
```

### When to Use What

- **Use ACP** to build AI coding agents that integrate with editors
- **Use MCP** to provide context and tools to language models
- **Use both** for a complete AI-powered development environment

---

## Contributing

We welcome contributions! Please see our [Contributing Guide](CONTRIBUTING.md) for:

- Development setup
- Code style and standards
- Testing requirements
- Pull request process
- Release workflow

### Quick Start for Contributors

```bash
# Clone and setup
git clone https://github.com/chuk-ai/chuk-acp.git
cd chuk-acp
uv venv
source .venv/bin/activate
uv pip install -e ".[dev,pydantic]"

# Run checks
make check

# Run examples
cd examples && python simple_client.py
```

### Areas for Contribution

- πŸ› Bug fixes and issue resolution
- ✨ New features (check ACP spec for ideas)
- πŸ“š Documentation improvements
- πŸ§ͺ Additional test coverage
- 🌐 Additional transports (WebSocket, HTTP, etc.)
- 🎨 Example agents and clients
- πŸ”§ Tooling and developer experience

---

## License

This project is licensed under the **Apache License 2.0**.

See [LICENSE](LICENSE) for full details.

---

## Links

### Official Resources

- **ACP Specification**: https://agentclientprotocol.com
- **GitHub Repository**: https://github.com/chuk-ai/chuk-acp
- **PyPI Package**: https://pypi.org/project/chuk-acp/
- **Issue Tracker**: https://github.com/chuk-ai/chuk-acp/issues
- **Discussions**: https://github.com/chuk-ai/chuk-acp/discussions
- **CLI Documentation**: [CLI.md](CLI.md)

### Related Projects

**ACP Agents:**
- **Claude Code**: https://github.com/zed-industries/claude-code-acp - Anthropic's official Claude adapter
- **Kimi**: https://github.com/MoonshotAI/kimi-cli - AI coding agent from Moonshot AI

**Protocols:**
- **Model Context Protocol (MCP)**: https://modelcontextprotocol.io - Data & tool access for agents
- **Language Server Protocol (LSP)**: https://microsoft.github.io/language-server-protocol/ - Inspiration for ACP

### Community

- Report bugs: [GitHub Issues](https://github.com/chuk-ai/chuk-acp/issues)
- Ask questions: [GitHub Discussions](https://github.com/chuk-ai/chuk-acp/discussions)
- Contribute: See [CONTRIBUTING.md](CONTRIBUTING.md)

---

<div align="center">

**Built with ❀️ for the AI coding community**

[⭐ Star us on GitHub](https://github.com/chuk-ai/chuk-acp) | [πŸ“¦ Install from PyPI](https://pypi.org/project/chuk-acp/) | [πŸ“– Read the Spec](https://agentclientprotocol.com)

</div>

            

Raw data

            {
    "_id": null,
    "home_page": null,
    "name": "chuk-acp",
    "maintainer": null,
    "docs_url": null,
    "requires_python": ">=3.11",
    "maintainer_email": null,
    "keywords": "acp, agent-client-protocol, ai, coding-agent, editor",
    "author": null,
    "author_email": "Christopher Hay <chrishayuk@somejunkmailbox.com>",
    "download_url": "https://files.pythonhosted.org/packages/29/dc/6698b6d148f768d7e238491aa46daf07139c01fae3a785d6a0c7f3b3b660/chuk_acp-0.3.0.tar.gz",
    "platform": null,
    "description": "# chuk-acp\n\n[![CI](https://github.com/chuk-ai/chuk-acp/actions/workflows/ci.yml/badge.svg)](https://github.com/chuk-ai/chuk-acp/actions/workflows/ci.yml)\n[![PyPI version](https://badge.fury.io/py/chuk-acp.svg)](https://badge.fury.io/py/chuk-acp)\n[![Python 3.11+](https://img.shields.io/badge/python-3.11+-blue.svg)](https://www.python.org/downloads/)\n[![License](https://img.shields.io/badge/License-Apache_2.0-blue.svg)](https://opensource.org/licenses/Apache-2.0)\n[![Code style: black](https://img.shields.io/badge/code%20style-black-000000.svg)](https://github.com/psf/black)\n[![Ruff](https://img.shields.io/endpoint?url=https://raw.githubusercontent.com/astral-sh/ruff/main/assets/badge/v2.json)](https://github.com/astral-sh/ruff)\n[![codecov](https://codecov.io/gh/chuk-ai/chuk-acp/branch/main/graph/badge.svg)](https://codecov.io/gh/chuk-ai/chuk-acp)\n\nA Python implementation of the [Agent Client Protocol (ACP)](https://agentclientprotocol.com) - the standard protocol for communication between code editors and AI coding agents.\n\n---\n\n## \ud83d\udcd6 Table of Contents\n\n- [Overview](#overview)\n- [Why ACP?](#why-acp)\n- [Features](#features)\n- [Installation](#installation)\n- [Quick Start](#quick-start)\n  - [CLI Tool](#cli-tool)\n  - [Building an Agent](#building-an-agent)\n  - [Building a Client](#building-a-client)\n- [Core Concepts](#core-concepts)\n- [Complete Examples](#complete-examples)\n- [API Reference](#api-reference)\n- [Protocol Support](#protocol-support)\n- [Architecture](#architecture)\n- [Testing](#testing)\n- [Relationship to MCP](#relationship-to-mcp)\n- [Contributing](#contributing)\n- [License](#license)\n- [Links](#links)\n\n---\n\n## Overview\n\nThe **Agent Client Protocol (ACP)** is to AI coding agents what the Language Server Protocol (LSP) is to programming languages. It standardizes communication between code editors/IDEs and coding agents\u2014programs that use generative AI to autonomously modify code.\n\n**chuk-acp** provides a complete, production-ready Python implementation of ACP, enabling you to:\n\n- \ud83d\udcac **Interact with agents instantly** using the CLI (`uvx chuk-acp claude-code-acp` or `echo \"hello\" | uvx chuk-acp client -- kimi --acp`)\n- \ud83e\udd16 **Build ACP-compliant coding agents** easily with the high-level `ACPAgent` API\n- \ud83d\udda5\ufe0f **Build editors/IDEs** that can connect to any ACP-compliant agent with `ACPClient`\n- \ud83d\udd0c **Integrate AI capabilities** into existing development tools\n- \ud83e\uddea **Test and develop** against the ACP specification\n\n---\n\n## Why ACP?\n\n### The Problem\n\nWithout a standard protocol, every AI coding tool creates its own proprietary interface, leading to:\n\n- Fragmentation across different tools and editors\n- Inability to switch agents or editors without rewriting integration code\n- Duplicated effort implementing similar functionality\n- Limited interoperability\n\n### The Solution\n\nACP provides a **standard, open protocol** that:\n\n- \u2705 Enables **any agent to work with any editor**\n- \u2705 Provides **consistent user experience** across tools\n- \u2705 Allows **innovation at both the editor and agent level**\n- \u2705 Built on proven standards (JSON-RPC 2.0)\n- \u2705 Supports **async/streaming** for real-time AI interactions\n\nThink LSP for language tooling, but for AI coding agents.\n\n---\n\n## Features\n\n### \ud83c\udfaf Complete ACP Implementation\n\n- Full support for ACP v1 specification\n- All baseline methods and content types\n- Optional capabilities (modes, session loading, file system, terminal)\n- Protocol compliance test suite\n- **MCP Servers Support**: Automatically sends empty `mcpServers: []` for compatibility with agents that require it\n- **Flexible AgentInfo**: Handles agents that return incomplete initialization data\n\n### \ud83d\udd27 Developer-Friendly\n\n- **CLI Tool**: Interactive command-line client for testing agents (`uvx chuk-acp`)\n- **Zero Installation**: Run with `uvx` - no setup required\n- **Type-Safe**: Comprehensive type hints throughout\n- **Async-First**: Built on `anyio` for efficient async/await patterns\n- **Optional Pydantic**: Use Pydantic for validation, or go dependency-free with fallback\n- **Well-Documented**: Extensive examples and API documentation\n- **Production-Ready**: Tested across Python 3.11, 3.12 on Linux, macOS, Windows\n\n### \ud83d\ude80 Flexible & Extensible\n\n- **Multiple transports**: Stdio (with more coming)\n- **Custom methods**: Extend protocol with `_meta` fields and custom methods\n- **Pluggable**: Easy to integrate into existing tools\n- **MCP Integration**: Seamless compatibility with Model Context Protocol\n\n### \ud83d\udee1\ufe0f Quality & Security\n\n- Comprehensive test coverage\n- Security scanning with Bandit and CodeQL\n- Type checking with mypy\n- Automated dependency updates\n- CI/CD with GitHub Actions\n\n---\n\n## Quick Start (60 Seconds!)\n\n### \ud83d\ude80 Try Without Installation\n\nThe absolute fastest way to get started - no cloning, no installation:\n\n```bash\n# 1. Download a standalone agent example\ncurl -O https://raw.githubusercontent.com/chuk-ai/chuk-acp/main/examples/standalone_agent.py\n\n# 2. Run it with uvx (automatically installs chuk-acp temporarily)\nuvx chuk-acp client python standalone_agent.py\n\n# That's it! Start chatting with your agent.\n```\n\nSee [QUICKSTART.md](QUICKSTART.md) for full details.\n\n### Or Connect to External Agents\n\n```bash\n# Claude Code (requires ANTHROPIC_API_KEY)\nANTHROPIC_API_KEY=sk-... uvx chuk-acp claude-code-acp\n\n# Kimi (Chinese AI assistant)\nuvx chuk-acp client -- kimi --acp\n```\n\n**That's it!** `uvx` automatically handles installation. Perfect for quick testing.\n\n---\n\n## Installation\n\n### Using uvx (Recommended for One-Off Usage)\n\nNo installation needed! `uvx` runs the CLI directly:\n\n```bash\n# Single prompt mode (interactive with stdin)\necho \"Create a Python function to calculate fibonacci\" | uvx chuk-acp client -- kimi --acp\n\n# With faster validation (optional)\nuvx --from 'chuk-acp[pydantic]' chuk-acp claude-code-acp\n```\n\n### Using uv (Recommended for Development)\n\n[uv](https://github.com/astral-sh/uv) is a fast Python package installer:\n\n```bash\n# Basic installation (includes CLI)\nuv pip install chuk-acp\n\n# With Pydantic validation support (recommended for better performance)\nuv pip install chuk-acp[pydantic]\n\n# Or add to your project\nuv add chuk-acp\n```\n\n### Using pip\n\n```bash\n# Basic installation (includes CLI)\npip install chuk-acp\n\n# With Pydantic support (recommended)\npip install chuk-acp[pydantic]\n```\n\n### Development Installation\n\n```bash\ngit clone https://github.com/chuk-ai/chuk-acp.git\ncd chuk-acp\nuv venv\nsource .venv/bin/activate  # On Windows: .venv\\Scripts\\activate\nuv pip install -e \".[dev,pydantic]\"\n```\n\n### Requirements\n\n- Python 3.11 or higher\n- Dependencies: `anyio`, `typing-extensions`\n- Optional: `pydantic` (for faster validation - works without it using fallback mechanism)\n\n---\n\n## Quick Start\n\n### CLI Tool - Interactive Chat with Any Agent\n\nThe easiest way to interact with ACP agents is using the built-in CLI. Works instantly with `uvx` or after installation.\n\nThe CLI supports three modes:\n\n1. **Agent passthrough** - For editors/tools (explicitly use `agent` subcommand)\n2. **Interactive client** - For humans (explicitly use `client` subcommand)\n3. **Auto-detect** - Smart mode selection (TTY = interactive, piped = passthrough)\n\n#### Try It Now (No Installation!)\n\n```bash\n# Connect to Claude Code (requires ANTHROPIC_API_KEY)\nANTHROPIC_API_KEY=sk-... uvx chuk-acp client claude-code-acp\n\n# Connect to Kimi agent (use -- to separate flags)\necho \"hello\" | uvx chuk-acp client -- kimi --acp\n\n# Interactive mode with verbose output\necho \"your question\" | uvx chuk-acp --verbose client -- kimi --acp\n\n# Interactive chat opens automatically\n# Just start typing your questions!\n```\n\n#### After Installation\n\n```bash\n# Interactive client mode (explicit)\nchuk-acp client python examples/echo_agent.py\n\n# Agent passthrough mode (for editors like Zed)\nchuk-acp agent python my_agent.py\n\n# Auto-detect mode (interactive if TTY, passthrough if piped)\nchuk-acp python examples/echo_agent.py\n\n# Single prompt (requires stdin for kimi)\necho \"Create a Python function to calculate factorial\" | chuk-acp client -- kimi --acp\n\n# Using a config file\nchuk-acp --config examples/kimi_config.json\n\n# With environment variables\nchuk-acp client python agent.py --env DEBUG=true --env API_KEY=xyz\n\n# Force interactive mode even when piped\nchuk-acp --interactive python agent.py\n\n# Verbose output for debugging\nchuk-acp client python agent.py --verbose\n```\n\n#### Interactive Mode Commands\n\nWhen in interactive chat mode, you can use these special commands:\n\n| Command | Description |\n|---------|-------------|\n| `/quit` or `/exit` | Exit the client |\n| `/new` | Start a new session (clears context) |\n| `/info` | Show agent information and session ID |\n\n#### Example Interactive Session\n\n**With Claude Code:**\n```bash\n$ ANTHROPIC_API_KEY=sk-... uvx chuk-acp claude-code-acp\n\n\u2554\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2557\n\u2551           ACP Interactive Client                              \u2551\n\u255a\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u255d\n\nYou: Create a Python function to check if a string is a palindrome\n\nAgent: Here's a Python function to check if a string is a palindrome:\n\ndef is_palindrome(s):\n    # Remove spaces and convert to lowercase\n    s = s.replace(\" \", \"\").lower()\n    # Check if string equals its reverse\n    return s == s[::-1]\n\nYou: /quit\nGoodbye!\n```\n\n**With Kimi:**\n```bash\n$ echo \"What's the best way to handle async errors in Python?\" | uvx chuk-acp client -- kimi --acp\n\nAgent: [Kimi's response with thinking and detailed explanation...]\n```\n\n#### Configuration Files\n\nUse standard ACP configuration format (compatible with Zed, VSCode, etc.):\n\n**claude_code_config.json:**\n```json\n{\n  \"command\": \"claude-code-acp\",\n  \"args\": [],\n  \"env\": {\n    \"ANTHROPIC_API_KEY\": \"sk-...\"\n  }\n}\n```\n\n**kimi_config.json:**\n```json\n{\n  \"command\": \"kimi\",\n  \"args\": [\"--acp\"],\n  \"env\": {}\n}\n```\n\nThen use with:\n```bash\nchuk-acp --config claude_code_config.json\nchuk-acp --config kimi_config.json\n```\n\n#### Important: Using `--` for Agent Flags\n\nWhen an agent requires flags (like `kimi --acp`), use `--` to separate chuk-acp flags from agent arguments:\n\n```bash\n# Correct - use -- separator\nchuk-acp client -- kimi --acp\n\n# Also works via config file\nchuk-acp --config kimi_config.json\n```\n\nWithout `--`, argparse treats `--acp` as a chuk-acp flag and fails. Using a config file avoids this issue entirely.\n\n**\ud83d\udcd6 See [CLI.md](CLI.md) for complete CLI documentation and advanced usage.**\n\n### Using with Editors (Zed, VSCode, etc.)\n\nchuk-acp can run your custom agents in editors that support ACP, like Zed. The `agent` mode provides a passthrough that lets editors communicate directly with your agent via the ACP protocol.\n\n#### Zed Configuration\n\nAdd this to your Zed `settings.json` (usually `~/.config/zed/settings.json`):\n\n```json\n{\n  \"agent_servers\": {\n    \"My Custom Agent\": {\n      \"command\": \"uvx\",\n      \"args\": [\n        \"chuk-acp\",\n        \"agent\",\n        \"python\",\n        \"/absolute/path/to/my_agent.py\"\n      ],\n      \"env\": {}\n    }\n  }\n}\n```\n\nOr using auto-detect mode (will automatically use passthrough when piped from Zed):\n\n```json\n{\n  \"agent_servers\": {\n    \"My Custom Agent\": {\n      \"command\": \"uvx\",\n      \"args\": [\n        \"chuk-acp\",\n        \"python\",\n        \"/absolute/path/to/my_agent.py\"\n      ],\n      \"env\": {}\n    }\n  }\n}\n```\n\n**What this does:**\n- `uvx` automatically installs `chuk-acp` in a temporary environment\n- `chuk-acp agent` runs in passthrough mode (just executes your agent with the ACP protocol layer)\n- Your agent gets access to the `chuk-acp` package (so `from chuk_acp.agent import ACPAgent` works)\n- No `--from` or `--with` flags needed!\n\n**Example agent file** (`my_agent.py`):\n\n```python\nfrom typing import List\nfrom chuk_acp.agent import ACPAgent, AgentSession\nfrom chuk_acp.protocol.types import AgentInfo, Content\n\nclass MyAgent(ACPAgent):\n    def get_agent_info(self) -> AgentInfo:\n        return AgentInfo(name=\"my-agent\", version=\"1.0.0\")\n\n    async def handle_prompt(self, session: AgentSession, prompt: List[Content]) -> str:\n        text = prompt[0].get(\"text\", \"\") if prompt else \"\"\n        return f\"You said: {text}\"\n\nif __name__ == \"__main__\":\n    MyAgent().run()\n```\n\nSee [`examples/zed_config.json`](examples/zed_config.json) for more configuration examples.\n\n### The Easiest Way: ACPClient\n\nThe fastest way to get started programmatically is with the high-level `ACPClient`, which handles all protocol details automatically:\n\n**Option A: Direct Usage**\n```python\n\"\"\"quickstart.py\"\"\"\nimport anyio\nfrom chuk_acp import ACPClient\n\nasync def main():\n    # Connect to an agent - handles initialization, sessions, everything!\n    async with ACPClient(\"python\", [\"echo_agent.py\"]) as client:\n        # Send a prompt and get the response\n        result = await client.send_prompt(\"Hello!\")\n        print(f\"Agent: {result.full_message}\")\n\nanyio.run(main)\n```\n\n**Option B: Using Standard ACP Configuration**\n\nThis matches the configuration format used by editors like Zed, VSCode, etc.:\n\n```python\n\"\"\"quickstart_config.py\"\"\"\nimport anyio\nfrom chuk_acp import ACPClient, AgentConfig\n\nasync def main():\n    # Standard ACP configuration format\n    config = AgentConfig(\n        command=\"kimi\",           # Any ACP-compatible agent\n        args=[\"--acp\"],          # Agent-specific arguments\n        env={\"DEBUG\": \"true\"}    # Optional environment variables\n    )\n\n    async with ACPClient.from_config(config) as client:\n        result = await client.send_prompt(\"Hello!\")\n        print(f\"Agent: {result.full_message}\")\n\nanyio.run(main)\n```\n\nOr load from a JSON file (like `~/.config/zed/settings.json`):\n\n```python\nfrom chuk_acp import load_agent_config\n\nconfig = load_agent_config(\"~/.config/my-app/agent.json\")\nasync with ACPClient.from_config(config) as client:\n    result = await client.send_prompt(\"Hello!\")\n```\n\n**What `ACPClient` does automatically:**\n- \u2705 Starts the agent process\n- \u2705 Handles protocol initialization\n- \u2705 Creates and manages sessions\n- \u2705 Captures all notifications\n- \u2705 Cleans up resources\n- \u2705 Supports standard ACP configuration format\n\n**Want more control?** The low-level API gives you fine-grained control over the protocol. See the examples below.\n\n---\n\n### Building an Agent\n\nThe fastest way to build an ACP agent is with the high-level `ACPAgent` class:\n\n```python\n\"\"\"my_agent.py\"\"\"\nfrom typing import List\nfrom chuk_acp.agent import ACPAgent, AgentSession\nfrom chuk_acp.protocol.types import AgentInfo, Content\n\nclass MyAgent(ACPAgent):\n    \"\"\"Your custom agent implementation.\"\"\"\n\n    def get_agent_info(self) -> AgentInfo:\n        \"\"\"Return agent information.\"\"\"\n        return AgentInfo(\n            name=\"my-agent\",\n            version=\"1.0.0\",\n            title=\"My Custom Agent\"\n        )\n\n    async def handle_prompt(\n        self, session: AgentSession, prompt: List[Content]\n    ) -> str:\n        \"\"\"Handle a prompt - this is where your agent logic goes.\"\"\"\n        # Extract text from prompt\n        text = prompt[0].get(\"text\", \"\") if prompt else \"\"\n\n        # Your agent logic here\n        response = f\"I received: {text}\"\n\n        # Return the response\n        return response\n\nif __name__ == \"__main__\":\n    agent = MyAgent()\n    agent.run()\n```\n\n**Run your agent:**\n```bash\n# Test with CLI (using uv for environment management)\nuvx chuk-acp client uv run my_agent.py\n\n# Or if chuk-acp is installed globally\nchuk-acp client python my_agent.py\n```\n\n**What `ACPAgent` does automatically:**\n- \u2705 Handles all protocol messages (initialize, session/new, session/prompt)\n- \u2705 Manages sessions and routing\n- \u2705 Sends responses in correct format\n- \u2705 Error handling and logging\n- \u2705 Stdin/stdout transport\n\n**Real example:** See [`examples/echo_agent.py`](examples/echo_agent.py) - a complete working agent in just 35 lines!\n\n#### \ud83d\ude80 Even Easier: chuk-acp-agent\n\nFor an even simpler agent-building experience, check out **[chuk-acp-agent](https://github.com/chrishayuk/chuk-acp-agent)** - an opinionated agent kit built on top of chuk-acp:\n\n```python\nfrom chuk_acp_agent import Agent, Context\n\nclass MyAgent(Agent):\n    async def on_prompt(self, ctx: Context, prompt: str):\n        # Session memory\n        count = ctx.memory.get(\"count\", 0) + 1\n        ctx.memory.set(\"count\", count)\n\n        # Stream response\n        yield f\"Message #{count}: {prompt}\\n\"\n\nif __name__ == \"__main__\":\n    MyAgent().run()\n```\n\n**Features:**\n- \ud83d\udce6 Batteries-included: session memory, streaming helpers, MCP integration\n- \ud83c\udfaf Minimal boilerplate: just implement `on_prompt()`\n- \ud83d\udd27 Context API: `ctx.memory`, `ctx.emit()`, `ctx.fs`, `ctx.terminal`\n- \ud83d\udee0\ufe0f Built-in MCP tool support via `chuk-tool-processor`\n\nInstall: `pip install chuk-acp-agent` or see the [chuk-acp-agent docs](https://github.com/chrishayuk/chuk-acp-agent).\n\n---\n\n### More Examples\n\nFor more complete examples showing different use cases:\n\n```bash\n# Clone the repository\ngit clone https://github.com/chuk-ai/chuk-acp.git\ncd chuk-acp\n\n# Install\nuv pip install -e \".[pydantic]\"\n\n# Run examples (all use the high-level ACPClient)\nuv run python examples/simple_client.py   # Basic single prompt\nuv run python examples/quick_start.py     # Multi-turn conversation\nuv run python examples/config_example.py  # Configuration support (Zed/VSCode format)\n\n# Advanced: Low-level protocol examples\nuv run python examples/low_level/simple_client.py        # Manual protocol handling\nuv run python examples/low_level/quick_start.py          # Self-contained with embedded agent\nuv run python examples/low_level/comprehensive_demo.py   # All ACP features\n```\n\nSee the [examples directory](https://github.com/chuk-ai/chuk-acp/tree/main/examples) for detailed documentation.\n\n> **Note**: Examples are in the GitHub repository. If you installed via pip, clone the repo to access them.\n\n### Option B: Build Your Own (10 Minutes)\n\nCreate a complete ACP client and agent from scratch.\n\n#### Step 1: Install\n\n```bash\nuv pip install chuk-acp[pydantic]\n```\n\n#### Step 2: Create an Agent\n\nSave this as `echo_agent.py`:\n\n```python\n\"\"\"echo_agent.py - A simple ACP agent\"\"\"\nimport json\nimport sys\nimport uuid\n\nfrom chuk_acp.protocol import (\n    create_response,\n    create_notification,\n    METHOD_INITIALIZE,\n    METHOD_SESSION_NEW,\n    METHOD_SESSION_PROMPT,\n    METHOD_SESSION_UPDATE,\n)\nfrom chuk_acp.protocol.types import AgentInfo, AgentCapabilities, TextContent\n\n# Read messages from stdin, write to stdout\nfor line in sys.stdin:\n    msg = json.loads(line.strip())\n    method = msg.get(\"method\")\n    params = msg.get(\"params\", {})\n    msg_id = msg.get(\"id\")\n\n    # Route to handlers\n    if method == METHOD_INITIALIZE:\n        result = {\n            \"protocolVersion\": 1,\n            \"agentInfo\": AgentInfo(name=\"echo-agent\", version=\"1.0.0\").model_dump(),\n            \"agentCapabilities\": AgentCapabilities().model_dump(),\n        }\n        response = create_response(id=msg_id, result=result)\n\n    elif method == METHOD_SESSION_NEW:\n        session_id = f\"session-{uuid.uuid4().hex[:8]}\"\n        response = create_response(id=msg_id, result={\"sessionId\": session_id})\n\n    elif method == METHOD_SESSION_PROMPT:\n        session_id = params[\"sessionId\"]\n        user_text = params[\"prompt\"][0].get(\"text\", \"\")\n\n        # Send a notification with the echo\n        from chuk_acp.protocol.types import SessionUpdate\n\n        session_update = SessionUpdate(\n            sessionUpdate=\"agent_message_chunk\",\n            content=TextContent(text=f\"Echo: {user_text}\")\n        )\n        notification = create_notification(\n            method=METHOD_SESSION_UPDATE,\n            params={\n                \"sessionId\": session_id,\n                \"update\": session_update.model_dump(exclude_none=True),\n            },\n        )\n        sys.stdout.write(json.dumps(notification.model_dump()) + \"\\n\")\n        sys.stdout.flush()\n\n        # Send the response\n        response = create_response(id=msg_id, result={\"stopReason\": \"end_turn\"})\n\n    else:\n        continue\n\n    sys.stdout.write(json.dumps(response.model_dump()) + \"\\n\")\n    sys.stdout.flush()\n```\n\n#### Step 3: Create a Client\n\nSave this as `my_client.py`:\n\n```python\n\"\"\"my_client.py - Connect to the echo agent using ACPClient\"\"\"\nimport anyio\nfrom chuk_acp import ACPClient\n\n\nasync def main():\n    # Connect to the agent - handles everything automatically!\n    async with ACPClient(\"python\", [\"echo_agent.py\"]) as client:\n        # Send a prompt and get the response\n        result = await client.send_prompt(\"Hello!\")\n        print(f\"Agent says: {result.full_message}\")\n\n\nif __name__ == \"__main__\":\n    anyio.run(main())\n```\n\n#### Step 4: Run It!\n\n```bash\nuv run python my_client.py\n```\n\n**Output:**\n```\n\u2713 Connected to echo-agent\n\u2713 Session: session-a1b2c3d4\n\nSending: Hello!\nAgent says: Echo: Hello!\n\u2713 Done!\n```\n\n\ud83c\udf89 **That's it!** You've built a working ACP agent and client.\n\n### What You Learned\n\n**Option A** showed you the fastest path - running pre-built examples.\n\n**Option B** taught you:\n- **Agents**: Read JSON-RPC from stdin, write to stdout using `create_response()` and `create_notification()`\n- **Clients**: Connect via `stdio_transport`, use `send_initialize()` and `send_session_new()`, manually handle messages to capture notifications\n- **Protocol flow**: Initialize \u2192 Create Session \u2192 Send Prompts (with notifications) \u2192 Get Response\n- **Best practices**: Use library types (`TextContent`, `AgentInfo`) and method constants (`METHOD_INITIALIZE`)\n\n### Next Steps\n\n**Explore More Features:**\n\nCheck out the complete examples in the [GitHub repository](https://github.com/chuk-ai/chuk-acp/tree/main/examples):\n- [simple_client.py](https://github.com/chuk-ai/chuk-acp/blob/main/examples/simple_client.py) - Clean client with notification handling\n- [echo_agent.py](https://github.com/chuk-ai/chuk-acp/blob/main/examples/echo_agent.py) - Production-ready agent with error handling\n- [comprehensive_demo.py](https://github.com/chuk-ai/chuk-acp/blob/main/examples/comprehensive_demo.py) - Filesystem, terminal, all ACP features\n\n**Build Something:**\n- Add file system access to your agent (see Example 3 below)\n- Implement tool calls and permission requests\n- Support multiple concurrent sessions\n- Add streaming for long responses\n\n**Learn More:**\n- [API Reference](#api-reference) - Complete API documentation\n- [Protocol Support](#protocol-support) - What's supported in ACP v1\n- [ACP Specification](https://agentclientprotocol.com) - Official protocol docs\n\n---\n\n## Core Concepts\n\n### The Agent-Client Model\n\n```\n\u250c\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2510                  \u250c\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2510\n\u2502   Client    \u2502 \u2190\u2500\u2500 JSON-RPC \u2500\u2500\u2192 \u2502    Agent     \u2502\n\u2502  (Editor)   \u2502     over stdio   \u2502  (AI Tool)   \u2502\n\u2514\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2518                  \u2514\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2518\n      \u2191                                 \u2191\n      \u2502                                 \u2502\n  User Interface                   AI Model\n  File System                      Code Analysis\n  Permissions                      Code Generation\n```\n\n### Key Components\n\n#### 1. **Protocol Layer** (`chuk_acp.protocol`)\n\nThe core protocol implementation:\n\n- **JSON-RPC 2.0**: Request/response and notification messages\n- **Message Types**: Initialize, session management, prompts\n- **Content Types**: Text, images, audio, resources, annotations\n- **Capabilities**: Negotiate features between client and agent\n\n#### 2. **Transport Layer** (`chuk_acp.transport`)\n\nCommunication mechanism:\n\n- **Stdio Transport**: Process-based communication (current)\n- **Extensible**: WebSocket, HTTP, etc. (future)\n\n#### 3. **Type System** (`chuk_acp.protocol.types`)\n\nStrongly-typed protocol structures:\n\n- Content types (text, image, audio)\n- Capabilities and features\n- Session modes and states\n- Tool calls and permissions\n\n### The ACP Flow\n\n```\n1. INITIALIZE\n   Client \u2500\u2500\u2192 Agent: Protocol version, capabilities\n   Agent  \u2500\u2500\u2192 Client: Agent info, supported features\n\n2. SESSION CREATION\n   Client \u2500\u2500\u2192 Agent: Working directory, MCP servers\n   Agent  \u2500\u2500\u2192 Client: Session ID\n\n3. PROMPT TURN\n   Client \u2500\u2500\u2192 Agent: User prompt (text, images, etc.)\n   Agent  \u2500\u2500\u2192 Client: [Streaming updates]\n   Agent  \u2500\u2500\u2192 Client: Stop reason (end_turn, max_tokens, etc.)\n\n4. ONGOING INTERACTION\n   - Session updates (thoughts, tool calls, messages)\n   - Permission requests (file access, terminal, etc.)\n   - Mode changes (ask \u2192 code \u2192 architect)\n   - Cancellation support\n```\n\n---\n\n## Complete Examples\n\n### Example 1: Echo Agent (Using Library)\n\nA minimal agent that echoes user input using chuk-acp library helpers:\n\n```python\n\"\"\"echo_agent.py - Agent using chuk-acp library\"\"\"\nimport json\nimport sys\nimport uuid\nfrom typing import Dict, Any\n\nfrom chuk_acp.protocol import (\n    create_response,\n    create_error_response,\n    create_notification,\n    METHOD_INITIALIZE,\n    METHOD_SESSION_NEW,\n    METHOD_SESSION_PROMPT,\n    METHOD_SESSION_UPDATE,\n)\nfrom chuk_acp.protocol.types import (\n    AgentInfo,\n    AgentCapabilities,\n    SessionUpdate,\n    TextContent,\n)\n\nclass EchoAgent:\n    def __init__(self):\n        self.sessions = {}\n\n    def handle_initialize(self, params: Dict[str, Any]) -> Dict[str, Any]:\n        \"\"\"Use library types instead of manual dict construction.\"\"\"\n        agent_info = AgentInfo(name=\"echo-agent\", version=\"0.1.0\")\n        agent_capabilities = AgentCapabilities()\n\n        return {\n            \"protocolVersion\": 1,\n            \"agentInfo\": agent_info.model_dump(exclude_none=True),\n            \"agentCapabilities\": agent_capabilities.model_dump(exclude_none=True),\n        }\n\n    def handle_session_new(self, params: Dict[str, Any]) -> Dict[str, Any]:\n        session_id = f\"session_{uuid.uuid4().hex[:8]}\"\n        self.sessions[session_id] = {\"cwd\": params.get(\"cwd\")}\n        return {\"sessionId\": session_id}\n\n    def handle_session_prompt(self, params: Dict[str, Any]) -> Dict[str, Any]:\n        session_id = params[\"sessionId\"]\n        prompt = params[\"prompt\"]\n\n        # Use library helpers to create notification\n        text_content = TextContent(\n            text=f\"Echo: You said '{prompt[0].get('text', '')}'\"\n        )\n\n        session_update = SessionUpdate(\n            sessionUpdate=\"agent_message_chunk\",\n            content=text_content\n        )\n\n        notification = create_notification(\n            method=METHOD_SESSION_UPDATE,\n            params={\n                \"sessionId\": session_id,\n                \"update\": session_update.model_dump(exclude_none=True),\n            },\n        )\n\n        sys.stdout.write(json.dumps(notification.model_dump(exclude_none=True)) + \"\\n\")\n        sys.stdout.flush()\n\n        return {\"stopReason\": \"end_turn\"}\n\n    def run(self):\n        for line in sys.stdin:\n            message = json.loads(line.strip())\n            method = message.get(\"method\")\n            msg_id = message.get(\"id\")\n\n            try:\n                # Route to handler using method constants\n                if method == METHOD_INITIALIZE:\n                    result = self.handle_initialize(message.get(\"params\", {}))\n                elif method == METHOD_SESSION_NEW:\n                    result = self.handle_session_new(message.get(\"params\", {}))\n                elif method == METHOD_SESSION_PROMPT:\n                    result = self.handle_session_prompt(message.get(\"params\", {}))\n                else:\n                    raise Exception(f\"Unknown method: {method}\")\n\n                # Use library helper to create response\n                response = create_response(id=msg_id, result=result)\n            except Exception as e:\n                # Use library helper for error responses\n                response = create_error_response(id=msg_id, code=-32603, message=str(e))\n\n            sys.stdout.write(json.dumps(response.model_dump(exclude_none=True)) + \"\\n\")\n            sys.stdout.flush()\n\nif __name__ == \"__main__\":\n    EchoAgent().run()\n```\n\n> **Note**: This demonstrates using the library's protocol helpers (`create_response`, `create_notification`, `TextContent`, etc.) instead of manual JSON construction. See `examples/echo_agent.py` for the complete implementation.\n\n### Example 2: Client with Session Updates\n\nCapture and handle streaming updates from agent:\n\n```python\n\"\"\"client_with_updates.py - Capture session/update notifications\"\"\"\nimport asyncio\nimport uuid\n\nimport anyio\n\nfrom chuk_acp import (\n    stdio_transport,\n    send_initialize,\n    send_session_new,\n    ClientInfo,\n    ClientCapabilities,\n    TextContent,\n)\nfrom chuk_acp.protocol import (\n    create_request,\n    JSONRPCNotification,\n    JSONRPCResponse,\n    METHOD_SESSION_PROMPT,\n    METHOD_SESSION_UPDATE,\n)\n\nasync def main():\n    async with stdio_transport(\"python\", [\"examples/echo_agent.py\"]) as (read, write):\n        # Initialize\n        init_result = await send_initialize(\n            read, write,\n            protocol_version=1,\n            client_info=ClientInfo(name=\"client\", version=\"1.0.0\"),\n            capabilities=ClientCapabilities()\n        )\n        print(f\"Connected to {init_result.agentInfo.name}\")\n\n        # Create session\n        session = await send_session_new(read, write, cwd=\"/tmp\")\n\n        # Send prompt and capture notifications\n        prompt_text = \"Write a hello world function\"\n        print(f\"User: {prompt_text}\")\n\n        request_id = str(uuid.uuid4())\n        request = create_request(\n            method=METHOD_SESSION_PROMPT,\n            params={\n                \"sessionId\": session.sessionId,\n                \"prompt\": [TextContent(text=prompt_text).model_dump(exclude_none=True)],\n            },\n            id=request_id,\n        )\n        await write.send(request)\n\n        # Collect notifications and response\n        agent_messages = []\n        stop_reason = None\n\n        with anyio.fail_after(60.0):\n            while stop_reason is None:\n                message = await read.receive()\n\n                # Handle session/update notifications\n                if isinstance(message, JSONRPCNotification):\n                    if message.method == METHOD_SESSION_UPDATE:\n                        params = message.params or {}\n\n                        # Agent message chunks\n                        update = params.get(\"update\", {})\n                        if update.get(\"sessionUpdate\") == \"agent_message_chunk\":\n                            content = update.get(\"content\", {})\n                            if isinstance(content, dict) and \"text\" in content:\n                                agent_messages.append(content[\"text\"])\n\n                        # Thoughts (optional)\n                        if \"thought\" in params:\n                            print(f\"[Thinking: {params['thought']}]\")\n\n                        # Tool calls (optional)\n                        if \"toolCall\" in params:\n                            tool = params[\"toolCall\"]\n                            print(f\"[Calling: {tool.get('name')}]\")\n\n                # Handle response\n                elif isinstance(message, JSONRPCResponse):\n                    if message.id == request_id:\n                        result = message.result\n                        if isinstance(result, dict):\n                            stop_reason = result.get(\"stopReason\")\n\n        # Display captured agent messages\n        if agent_messages:\n            print(f\"Agent: {''.join(agent_messages)}\")\n\n        print(f\"Completed: {stop_reason}\")\n\nasyncio.run(main())\n```\n\n> **Key Point**: To capture `session/update` notifications, you need to manually handle the request/response loop instead of using `send_session_prompt()`, which discards notifications. See `examples/simple_client.py` for a complete working example.\n\n### Example 3: Agent with File System Access\n\nAgent that can read/write files:\n\n```python\n\"\"\"file_agent.py - Agent with filesystem capabilities\"\"\"\nfrom chuk_acp.protocol.types import AgentCapabilities\n\n# Declare filesystem capabilities\ncapabilities = AgentCapabilities(\n    filesystem=True  # Enables fs/read_text_file and fs/write_text_file\n)\n\nasync def handle_file_operation(session_id: str, operation: str, path: str):\n    \"\"\"Request file access from client.\"\"\"\n\n    # Request permission\n    permission = await send_session_request_permission(\n        read, write,\n        session_id=session_id,\n        request=PermissionRequest(\n            id=\"perm-123\",\n            description=f\"Read file: {path}\",\n            tools=[{\"name\": \"fs/read_text_file\", \"arguments\": {\"path\": path}}]\n        )\n    )\n\n    if permission.granted:\n        # Read the file via client\n        # (Client implements fs/read_text_file method)\n        pass\n```\n\n### Example 4: Multi-Session Client\n\nManage multiple concurrent sessions:\n\n```python\n\"\"\"multi_session_client.py\"\"\"\nimport asyncio\nfrom chuk_acp import stdio_transport, send_session_new, send_session_prompt\n\nasync def create_and_run_session(read, write, cwd: str, prompt: str):\n    \"\"\"Create a session and send a prompt.\"\"\"\n    session = await send_session_new(read, write, cwd=cwd)\n    result = await send_session_prompt(\n        read, write,\n        session_id=session.sessionId,\n        prompt=[TextContent(text=prompt)]\n    )\n    return result\n\nasync def main():\n    async with stdio_transport(\"python\", [\"my_agent.py\"]) as (read, write):\n        # Initialize once\n        await send_initialize(...)\n\n        # Run multiple sessions concurrently\n        tasks = [\n            create_and_run_session(read, write, \"/project1\", \"Refactor auth\"),\n            create_and_run_session(read, write, \"/project2\", \"Add tests\"),\n            create_and_run_session(read, write, \"/project3\", \"Fix bug #123\"),\n        ]\n\n        results = await asyncio.gather(*tasks)\n        print(f\"Completed {len(results)} sessions\")\n\nasyncio.run(main())\n```\n\n---\n\n## API Reference\n\n### High-Level Client\n\nThe `ACPClient` provides the simplest way to interact with ACP agents:\n\n#### Direct Usage\n\n```python\nfrom chuk_acp import ACPClient\n\nasync with ACPClient(\"python\", [\"agent.py\"]) as client:\n    # Access agent information\n    print(f\"Agent: {client.agent_info.name}\")\n    print(f\"Session: {client.current_session.sessionId}\")\n\n    # Send prompts\n    result = await client.send_prompt(\"Hello!\")\n    print(result.full_message)  # Complete agent response\n    print(result.stop_reason)   # Why agent stopped\n\n    # Create new sessions\n    new_session = await client.new_session(cwd=\"/other/path\")\n```\n\n#### Configuration-Based Usage\n\nUse standard ACP configuration format (compatible with Zed, VSCode, etc.):\n\n```python\nfrom chuk_acp import ACPClient, AgentConfig, load_agent_config\n\n# Method 1: Create config directly\nconfig = AgentConfig(\n    command=\"kimi\",\n    args=[\"--acp\"],\n    env={\"DEBUG\": \"true\"},\n    cwd=\"/optional/path\"\n)\n\nasync with ACPClient.from_config(config) as client:\n    result = await client.send_prompt(\"Hello!\")\n\n# Method 2: Load from JSON file\nconfig = load_agent_config(\"~/.config/my-app/agent.json\")\nasync with ACPClient.from_config(config) as client:\n    result = await client.send_prompt(\"Hello!\")\n\n# Method 3: From dictionary (like editor configs)\nconfig = AgentConfig(**{\n    \"command\": \"kimi\",\n    \"args\": [\"--acp\"],\n    \"env\": {}\n})\nasync with ACPClient.from_config(config) as client:\n    result = await client.send_prompt(\"Hello!\")\n```\n\n**Example JSON config file:**\n```json\n{\n  \"command\": \"kimi\",\n  \"args\": [\"--acp\"],\n  \"env\": {\n    \"DEBUG\": \"true\",\n    \"LOG_LEVEL\": \"info\"\n  },\n  \"cwd\": \"/optional/path\"\n}\n```\n\n**Key Classes:**\n- `ACPClient` - Main client class\n- `AgentConfig` - Standard ACP configuration format\n- `load_agent_config()` - Load config from JSON file\n- `PromptResult` - Contains response and all notifications\n- `SessionInfo` - Session information\n- `SessionUpdate` - Individual notification from agent\n\n### Low-Level Protocol API\n\nFor fine-grained control over the protocol:\n\n### Protocol Helpers\n\n#### JSON-RPC Message Helpers\n\nBuild protocol messages using library helpers:\n\n```python\nfrom chuk_acp.protocol import (\n    create_request,\n    create_response,\n    create_error_response,\n    create_notification,\n)\n\n# Create a request\nrequest = create_request(\n    method=\"session/prompt\",\n    params={\"sessionId\": \"session-1\", \"prompt\": [...]},\n    id=\"req-123\"\n)\n\n# Create a response\nresponse = create_response(id=\"req-123\", result={\"stopReason\": \"end_turn\"})\n\n# Create an error response\nerror = create_error_response(id=\"req-123\", code=-32603, message=\"Internal error\")\n\n# Create a notification\nfrom chuk_acp.protocol.types import SessionUpdate, TextContent\n\nsession_update = SessionUpdate(\n    sessionUpdate=\"agent_message_chunk\",\n    content=TextContent(text=\"Hello!\")\n)\nnotification = create_notification(\n    method=\"session/update\",\n    params={\n        \"sessionId\": \"session-1\",\n        \"update\": session_update.model_dump(exclude_none=True)\n    }\n)\n```\n\n#### Method Constants\n\nUse constants instead of string literals for protocol methods:\n\n```python\nfrom chuk_acp.protocol import (\n    METHOD_INITIALIZE,\n    METHOD_SESSION_NEW,\n    METHOD_SESSION_PROMPT,\n    METHOD_SESSION_UPDATE,\n    METHOD_SESSION_CANCEL,\n    METHOD_FS_READ_TEXT_FILE,\n    METHOD_FS_WRITE_TEXT_FILE,\n    METHOD_TERMINAL_CREATE,\n    # ... and more\n)\n\n# Use in message routing\nif method == METHOD_INITIALIZE:\n    # Handle initialize\n    pass\nelif method == METHOD_SESSION_PROMPT:\n    # Handle prompt\n    pass\n```\n\n### Transport\n\n#### `stdio_transport(command, args)`\n\nCreate a stdio transport connection to an agent.\n\n```python\nasync with stdio_transport(\"python\", [\"agent.py\"]) as (read_stream, write_stream):\n    # Use streams for communication\n    pass\n```\n\n### Initialization\n\n#### `send_initialize(read, write, protocol_version, client_info, capabilities)`\n\nInitialize the connection and negotiate capabilities.\n\n```python\nresult = await send_initialize(\n    read_stream,\n    write_stream,\n    protocol_version=1,\n    client_info=ClientInfo(name=\"my-client\", version=\"1.0.0\"),\n    capabilities=ClientCapabilities(filesystem=True)\n)\n# result.agentInfo, result.capabilities, result.protocolVersion\n```\n\n### Session Management\n\n#### `send_session_new(read, write, cwd, mcp_servers=None, mode=None)`\n\nCreate a new session.\n\n```python\nsession = await send_session_new(\n    read_stream,\n    write_stream,\n    cwd=\"/absolute/path\",\n    mode=\"code\"  # Optional: ask, architect, code\n)\n# session.sessionId\n```\n\n#### `send_session_prompt(read, write, session_id, prompt)`\n\nSend a prompt to the agent.\n\n```python\nresult = await send_session_prompt(\n    read_stream,\n    write_stream,\n    session_id=\"session-123\",\n    prompt=[\n        TextContent(text=\"Write a function\"),\n        ImageContent(data=\"base64...\", mimeType=\"image/png\")\n    ]\n)\n# result.stopReason: end_turn, max_tokens, cancelled, refusal\n```\n\n> **Note**: `send_session_prompt` discards `session/update` notifications from the agent. To capture agent responses (message chunks, thoughts, tool calls), manually handle the request/response loop. See Example 2 or `examples/simple_client.py` for details.\n\n#### `send_session_cancel(write, session_id)`\n\nCancel an ongoing prompt turn.\n\n```python\nawait send_session_cancel(write_stream, session_id=\"session-123\")\n```\n\n### Content Types\n\n#### `TextContent(text)`\n\nPlain text content.\n\n```python\ncontent = TextContent(text=\"Hello, world!\")\n```\n\n#### `ImageContent(data, mimeType)`\n\nBase64-encoded image.\n\n```python\ncontent = ImageContent(\n    data=\"iVBORw0KGgoAAAANSUhEUgA...\",\n    mimeType=\"image/png\"\n)\n```\n\n#### `AudioContent(data, mimeType)`\n\nBase64-encoded audio.\n\n```python\ncontent = AudioContent(\n    data=\"SUQzBAA...\",\n    mimeType=\"audio/mpeg\"\n)\n```\n\n---\n\n## Protocol Support\n\nchuk-acp implements the **complete ACP v1 specification**.\n\n### \u2705 Baseline Agent Methods (Required)\n\n| Method | Description | Status |\n|--------|-------------|--------|\n| `initialize` | Protocol handshake and capability negotiation | \u2705 |\n| `authenticate` | Optional authentication | \u2705 |\n| `session/new` | Create new conversation sessions | \u2705 |\n| `session/prompt` | Process user prompts | \u2705 |\n| `session/cancel` | Cancel ongoing operations | \u2705 |\n\n### \u2705 Optional Agent Methods\n\n| Method | Capability | Status |\n|--------|------------|--------|\n| `session/load` | Resume previous sessions | \u2705 |\n| `session/set_mode` | Change session modes | \u2705 |\n\n### \u2705 Client Methods (Callbacks)\n\n| Method | Description | Status |\n|--------|-------------|--------|\n| `session/request_permission` | Request user approval for actions | \u2705 |\n| `fs/read_text_file` | Read file contents | \u2705 |\n| `fs/write_text_file` | Write file contents | \u2705 |\n| `terminal/create` | Create terminal sessions | \u2705 |\n| `terminal/output` | Stream terminal output | \u2705 |\n| `terminal/release` | Release terminal control | \u2705 |\n| `terminal/wait_for_exit` | Wait for command completion | \u2705 |\n| `terminal/kill` | Terminate running commands | \u2705 |\n\n### \u2705 Content Types\n\n- Text content (baseline - always supported)\n- Image content (base64-encoded)\n- Audio content (base64-encoded)\n- Embedded resources\n- Resource links\n- Annotations\n\n### \u2705 Session Features\n\n- Session management (create, load, cancel)\n- Multiple parallel sessions\n- Session modes: `ask`, `architect`, `code`\n- Session history replay\n- MCP server integration\n\n### \u2705 Tool Integration\n\n- Tool calls with status tracking (`pending`, `in_progress`, `completed`, `failed`)\n- Permission requests\n- File location tracking\n- Structured output (diffs, terminals, content)\n- Slash commands (optional)\n\n### \u2705 Protocol Requirements\n\n- **File paths**: All paths must be absolute \u2705\n- **Line numbers**: 1-based indexing \u2705\n- **JSON-RPC 2.0**: Strict compliance \u2705\n- **Extensibility**: `_meta` fields and custom methods \u2705\n\n---\n\n## Architecture\n\n### Project Structure\n\n```\nchuk-acp/\n\u251c\u2500\u2500 src/chuk_acp/\n\u2502   \u251c\u2500\u2500 protocol/              # Core protocol implementation\n\u2502   \u2502   \u251c\u2500\u2500 jsonrpc.py         # JSON-RPC 2.0 (requests, responses, errors)\n\u2502   \u2502   \u251c\u2500\u2500 acp_pydantic_base.py # Optional Pydantic support\n\u2502   \u2502   \u251c\u2500\u2500 types/             # Protocol type definitions\n\u2502   \u2502   \u2502   \u251c\u2500\u2500 content.py     # Content types (text, image, audio)\n\u2502   \u2502   \u2502   \u251c\u2500\u2500 capabilities.py # Client/agent capabilities\n\u2502   \u2502   \u2502   \u251c\u2500\u2500 session.py     # Session types and modes\n\u2502   \u2502   \u2502   \u251c\u2500\u2500 tools.py       # Tool calls and permissions\n\u2502   \u2502   \u2502   \u251c\u2500\u2500 plan.py        # Task planning types\n\u2502   \u2502   \u2502   \u251c\u2500\u2500 terminal.py    # Terminal integration\n\u2502   \u2502   \u2502   \u2514\u2500\u2500 ...\n\u2502   \u2502   \u2514\u2500\u2500 messages/          # Message handling\n\u2502   \u2502       \u251c\u2500\u2500 initialize.py  # Initialize/authenticate\n\u2502   \u2502       \u251c\u2500\u2500 session.py     # Session management\n\u2502   \u2502       \u251c\u2500\u2500 filesystem.py  # File operations\n\u2502   \u2502       \u251c\u2500\u2500 terminal.py    # Terminal operations\n\u2502   \u2502       \u2514\u2500\u2500 send_message.py # Core messaging utilities\n\u2502   \u2502\n\u2502   \u251c\u2500\u2500 transport/             # Transport layer\n\u2502   \u2502   \u251c\u2500\u2500 base.py            # Abstract transport interface\n\u2502   \u2502   \u2514\u2500\u2500 stdio.py           # Stdio transport (subprocess)\n\u2502   \u2502\n\u2502   \u2514\u2500\u2500 __init__.py            # Public API exports\n\u2502\n\u251c\u2500\u2500 examples/                  # Working examples\n\u2502   \u251c\u2500\u2500 echo_agent.py          # Simple echo agent\n\u2502   \u251c\u2500\u2500 simple_client.py       # Basic client\n\u2502   \u251c\u2500\u2500 quick_start.py         # Getting started\n\u2502   \u2514\u2500\u2500 comprehensive_demo.py  # Full-featured demo\n\u2502\n\u251c\u2500\u2500 tests/                     # Test suite\n\u2502   \u251c\u2500\u2500 test_protocol_compliance.py  # Spec compliance\n\u2502   \u251c\u2500\u2500 test_jsonrpc.py        # JSON-RPC tests\n\u2502   \u251c\u2500\u2500 test_types.py          # Type system tests\n\u2502   \u251c\u2500\u2500 test_messages.py       # Message handling\n\u2502   \u2514\u2500\u2500 test_stdio_transport.py # Transport tests\n\u2502\n\u2514\u2500\u2500 .github/                   # CI/CD workflows\n    \u251c\u2500\u2500 workflows/\n    \u2502   \u251c\u2500\u2500 ci.yml             # Testing and linting\n    \u2502   \u251c\u2500\u2500 publish.yml        # PyPI publishing\n    \u2502   \u2514\u2500\u2500 codeql.yml         # Security scanning\n    \u2514\u2500\u2500 ...\n```\n\n### Design Principles\n\n1. **Protocol First**: Strict adherence to ACP specification\n2. **Type Safety**: Comprehensive type hints throughout\n3. **Optional Dependencies**: Pydantic is optional, not required\n4. **Async by Default**: Built on `anyio` for async/await\n5. **Extensibility**: Custom methods and `_meta` fields supported\n6. **Testability**: Loosely coupled, dependency injection\n7. **Zero-Config**: Works out of the box with sensible defaults\n\n### Layer Separation\n\n```\n\u250c\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2510\n\u2502     User Code (Agents/Clients)      \u2502\n\u251c\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2524\n\u2502     High-Level API (messages/)      \u2502  \u2190 send_initialize, send_prompt, etc.\n\u251c\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2524\n\u2502    Protocol Layer (types/, jsonrpc) \u2502  \u2190 Content types, capabilities\n\u251c\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2524\n\u2502    Transport Layer (transport/)     \u2502  \u2190 Stdio, future: WebSocket, HTTP\n\u2514\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2518\n```\n\n---\n\n## Testing\n\n### Running Tests\n\n```bash\n# Run all tests\nmake test\n\n# Run with coverage\nmake test-cov\n\n# Run specific test file\nuv run pytest tests/test_protocol_compliance.py -v\n\n# Test without Pydantic (fallback mode)\nuv pip uninstall pydantic\nuv run pytest\n```\n\n### Test Categories\n\n- **Protocol Compliance** (`test_protocol_compliance.py`): Validates ACP spec adherence\n- **JSON-RPC** (`test_jsonrpc.py`): JSON-RPC 2.0 implementation\n- **Types** (`test_types.py`): Type system and content types\n- **Messages** (`test_messages.py`): Message handling and serialization\n- **Transport** (`test_stdio_transport.py`): Transport layer\n\n### Code Quality Checks\n\n```bash\n# Format code\nmake format\n\n# Lint\nmake lint\n\n# Type check\nmake mypy\n\n# Security scan\nmake security\n\n# All checks\nmake check\n```\n\n---\n\n## Relationship to MCP\n\n**ACP** and **MCP** (Model Context Protocol) are complementary protocols:\n\n| Protocol | Purpose | Focus |\n|----------|---------|-------|\n| **MCP** | What data/tools agents can access | Context & tools |\n| **ACP** | Where the agent lives in your workflow | Agent lifecycle |\n\n### Integration\n\nACP reuses MCP data structures for content types and resources:\n\n```python\nfrom chuk_acp.protocol.types import (\n    TextContent,      # From MCP\n    ImageContent,     # From MCP\n    ResourceContent,  # From MCP\n)\n\n# ACP sessions can specify MCP servers\nsession = await send_session_new(\n    read, write,\n    cwd=\"/project\",\n    mcp_servers=[\n        MCPServer(\n            name=\"filesystem\",\n            command=\"npx\",\n            args=[\"-y\", \"@modelcontextprotocol/server-filesystem\", \"/path\"]\n        )\n    ]\n)\n```\n\n### When to Use What\n\n- **Use ACP** to build AI coding agents that integrate with editors\n- **Use MCP** to provide context and tools to language models\n- **Use both** for a complete AI-powered development environment\n\n---\n\n## Contributing\n\nWe welcome contributions! Please see our [Contributing Guide](CONTRIBUTING.md) for:\n\n- Development setup\n- Code style and standards\n- Testing requirements\n- Pull request process\n- Release workflow\n\n### Quick Start for Contributors\n\n```bash\n# Clone and setup\ngit clone https://github.com/chuk-ai/chuk-acp.git\ncd chuk-acp\nuv venv\nsource .venv/bin/activate\nuv pip install -e \".[dev,pydantic]\"\n\n# Run checks\nmake check\n\n# Run examples\ncd examples && python simple_client.py\n```\n\n### Areas for Contribution\n\n- \ud83d\udc1b Bug fixes and issue resolution\n- \u2728 New features (check ACP spec for ideas)\n- \ud83d\udcda Documentation improvements\n- \ud83e\uddea Additional test coverage\n- \ud83c\udf10 Additional transports (WebSocket, HTTP, etc.)\n- \ud83c\udfa8 Example agents and clients\n- \ud83d\udd27 Tooling and developer experience\n\n---\n\n## License\n\nThis project is licensed under the **Apache License 2.0**.\n\nSee [LICENSE](LICENSE) for full details.\n\n---\n\n## Links\n\n### Official Resources\n\n- **ACP Specification**: https://agentclientprotocol.com\n- **GitHub Repository**: https://github.com/chuk-ai/chuk-acp\n- **PyPI Package**: https://pypi.org/project/chuk-acp/\n- **Issue Tracker**: https://github.com/chuk-ai/chuk-acp/issues\n- **Discussions**: https://github.com/chuk-ai/chuk-acp/discussions\n- **CLI Documentation**: [CLI.md](CLI.md)\n\n### Related Projects\n\n**ACP Agents:**\n- **Claude Code**: https://github.com/zed-industries/claude-code-acp - Anthropic's official Claude adapter\n- **Kimi**: https://github.com/MoonshotAI/kimi-cli - AI coding agent from Moonshot AI\n\n**Protocols:**\n- **Model Context Protocol (MCP)**: https://modelcontextprotocol.io - Data & tool access for agents\n- **Language Server Protocol (LSP)**: https://microsoft.github.io/language-server-protocol/ - Inspiration for ACP\n\n### Community\n\n- Report bugs: [GitHub Issues](https://github.com/chuk-ai/chuk-acp/issues)\n- Ask questions: [GitHub Discussions](https://github.com/chuk-ai/chuk-acp/discussions)\n- Contribute: See [CONTRIBUTING.md](CONTRIBUTING.md)\n\n---\n\n<div align=\"center\">\n\n**Built with \u2764\ufe0f for the AI coding community**\n\n[\u2b50 Star us on GitHub](https://github.com/chuk-ai/chuk-acp) | [\ud83d\udce6 Install from PyPI](https://pypi.org/project/chuk-acp/) | [\ud83d\udcd6 Read the Spec](https://agentclientprotocol.com)\n\n</div>\n",
    "bugtrack_url": null,
    "license": "Apache-2.0",
    "summary": "Agent Client Protocol (ACP) implementation for Python",
    "version": "0.3.0",
    "project_urls": {
        "Documentation": "https://agentclientprotocol.com",
        "Homepage": "https://github.com/chuk-ai/chuk-acp",
        "Repository": "https://github.com/chuk-ai/chuk-acp"
    },
    "split_keywords": [
        "acp",
        " agent-client-protocol",
        " ai",
        " coding-agent",
        " editor"
    ],
    "urls": [
        {
            "comment_text": null,
            "digests": {
                "blake2b_256": "211071a43af2f9c4548db58b7054f5a1283f9efe4e5e0b98dbfd112e180f318d",
                "md5": "a96865e489d1158c297d9f472428b1c3",
                "sha256": "f3df8763e20b5af57127b3bfc3c6b3fdda5d057dfd9c10cb64f30fc6094b1eb9"
            },
            "downloads": -1,
            "filename": "chuk_acp-0.3.0-py3-none-any.whl",
            "has_sig": false,
            "md5_digest": "a96865e489d1158c297d9f472428b1c3",
            "packagetype": "bdist_wheel",
            "python_version": "py3",
            "requires_python": ">=3.11",
            "size": 16206,
            "upload_time": "2025-11-09T12:18:31",
            "upload_time_iso_8601": "2025-11-09T12:18:31.293852Z",
            "url": "https://files.pythonhosted.org/packages/21/10/71a43af2f9c4548db58b7054f5a1283f9efe4e5e0b98dbfd112e180f318d/chuk_acp-0.3.0-py3-none-any.whl",
            "yanked": false,
            "yanked_reason": null
        },
        {
            "comment_text": null,
            "digests": {
                "blake2b_256": "29dc6698b6d148f768d7e238491aa46daf07139c01fae3a785d6a0c7f3b3b660",
                "md5": "a571e06b5ffca80b9402cfedbad9db90",
                "sha256": "61de553f9c1d2ff046b7d0514c1e8c6eda1199f4fa29c4b2e7ea59ba66115c48"
            },
            "downloads": -1,
            "filename": "chuk_acp-0.3.0.tar.gz",
            "has_sig": false,
            "md5_digest": "a571e06b5ffca80b9402cfedbad9db90",
            "packagetype": "sdist",
            "python_version": "source",
            "requires_python": ">=3.11",
            "size": 29954,
            "upload_time": "2025-11-09T12:18:32",
            "upload_time_iso_8601": "2025-11-09T12:18:32.754019Z",
            "url": "https://files.pythonhosted.org/packages/29/dc/6698b6d148f768d7e238491aa46daf07139c01fae3a785d6a0c7f3b3b660/chuk_acp-0.3.0.tar.gz",
            "yanked": false,
            "yanked_reason": null
        }
    ],
    "upload_time": "2025-11-09 12:18:32",
    "github": true,
    "gitlab": false,
    "bitbucket": false,
    "codeberg": false,
    "github_user": "chuk-ai",
    "github_project": "chuk-acp",
    "github_not_found": true,
    "lcname": "chuk-acp"
}
        
Elapsed time: 2.34565s