Skip to main content

ACP-compatible agent for Claude Code (Python version)

Project description

Claude Code ACP (Python)

PyPI Python License

Python implementation of ACP (Agent Client Protocol) for Claude Code.

This package bridges the Claude Agent SDK with the Agent Client Protocol (ACP), providing two ways to use Claude:

  1. ACP Server - Connect Claude to any ACP-compatible editor (Zed, Neovim, JetBrains, etc.)
  2. Python Client - Event-driven API for building Python applications with Claude

Features

  • Uses Claude CLI subscription - No API key needed, uses your existing Claude subscription
  • Full ACP protocol support - Compatible with Zed, Neovim, and other ACP clients
  • Bidirectional communication - Permission requests, tool calls, streaming responses
  • Event-driven Python API - Decorator-based handlers for easy integration
  • Session management - Create, fork, resume, list sessions
  • Multiple permission modes - default, acceptEdits, plan, bypassPermissions

Installation

pip install claude-code-acp

Or with uv:

uv tool install claude-code-acp

Requirements

  • Python 3.10+
  • Claude CLI installed and authenticated (claude /login)

Components

Class Type Description
ClaudeAcpAgent ACP Server For editors (Zed, Neovim) to connect
ClaudeClient Python API Event-driven wrapper (uses agent internally)
AcpClient ACP Client Connect to any ACP agent via subprocess

Usage 1: ACP Server for Editors

Run as an ACP server to connect Claude to your editor:

claude-code-acp

Zed Editor

Add to your Zed settings.json:

{
  "agent_servers": {
    "Claude Code Python": {
      "type": "custom",
      "command": "claude-code-acp",
      "args": [],
      "env": {}
    }
  }
}

Then open the Agent Panel (Ctrl+? / Cmd+?) and select "Claude Code Python" from the + menu.

Other Editors

Any ACP-compatible client can connect by spawning claude-code-acp as a subprocess and communicating via stdio.


Usage 2: Python Event-Driven API

Use ClaudeClient for building Python applications with Claude:

import asyncio
from claude_code_acp import ClaudeClient

async def main():
    client = ClaudeClient(cwd=".")

    @client.on_text
    async def handle_text(text: str):
        """Called for each text chunk from Claude."""
        print(text, end="", flush=True)

    @client.on_tool_start
    async def handle_tool_start(tool_id: str, name: str, input: dict):
        """Called when Claude starts using a tool."""
        print(f"\n๐Ÿ”ง {name}")

    @client.on_tool_end
    async def handle_tool_end(tool_id: str, status: str, output):
        """Called when a tool completes."""
        icon = "โœ…" if status == "completed" else "โŒ"
        print(f" {icon}")

    @client.on_permission
    async def handle_permission(name: str, input: dict) -> bool:
        """Called when Claude needs permission. Return True to allow."""
        print(f"๐Ÿ” Permission requested: {name}")
        return True  # or prompt user

    @client.on_complete
    async def handle_complete():
        """Called when the query completes."""
        print("\n--- Done ---")

    # Send a query
    response = await client.query("Create a hello.py file that prints Hello World")
    print(f"\nFull response: {response}")

asyncio.run(main())

Event Handlers

Decorator Arguments Description
@client.on_text (text: str) Streaming text chunks from Claude
@client.on_thinking (text: str) Thinking/reasoning blocks
@client.on_tool_start (tool_id, name, input) Tool execution started
@client.on_tool_end (tool_id, status, output) Tool execution completed
@client.on_permission (name, input) -> bool Permission request (return True/False)
@client.on_error (exception) Error occurred
@client.on_complete () Query completed

Client Methods

# Start a new session
session_id = await client.start_session()

# Send a query (returns full response text)
response = await client.query("Your prompt here")

# Set permission mode
await client.set_mode("acceptEdits")  # or "default", "plan", "bypassPermissions"

Usage 3: ACP Client (Connect to Any Agent)

Use AcpClient to connect to any ACP-compatible agent:

import asyncio
from claude_code_acp import AcpClient

async def main():
    # Connect to claude-code-acp (Python version)
    client = AcpClient(command="claude-code-acp")

    # Or connect to the TypeScript version
    # client = AcpClient(command="npx", args=["@zed-industries/claude-code-acp"])

    # Or any other ACP agent
    # client = AcpClient(command="my-custom-agent")

    @client.on_text
    async def handle_text(text: str):
        print(text, end="", flush=True)

    @client.on_tool_start
    async def handle_tool(tool_id: str, name: str, input: dict):
        print(f"\n๐Ÿ”ง {name}")

    @client.on_permission
    async def handle_permission(name: str, input: dict, options: list) -> str:
        """Return option_id: 'allow', 'reject', or 'allow_always'"""
        print(f"๐Ÿ” Permission: {name}")
        return "allow"

    @client.on_complete
    async def handle_complete():
        print("\n--- Done ---")

    async with client:
        response = await client.prompt("What files are here?")

asyncio.run(main())

Connect to Different Agents

from claude_code_acp import AcpClient

# Connect to our Claude ACP server
claude = AcpClient(command="claude-code-acp")

# Connect to Gemini CLI
gemini = AcpClient(command="gemini", args=["--experimental-acp"])

# Connect to TypeScript version
ts_claude = AcpClient(command="npx", args=["@zed-industries/claude-code-acp"])

AcpClient vs ClaudeClient

Feature ClaudeClient AcpClient
Uses Claude Agent SDK directly Any ACP agent via subprocess
Connection In-process Subprocess + stdio
Agents Claude only Any ACP-compatible agent
Use case Simple Python apps Multi-agent, testing, flexibility

Tested Agents

Agent Command Status
claude-code-acp (this package) claude-code-acp โœ… Works
Gemini CLI gemini --experimental-acp โœ… Works
TypeScript version npx @zed-industries/claude-code-acp โœ… Compatible

Architecture

โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”
โ”‚                          claude-code-acp Package                              โ”‚
โ”œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ค
โ”‚                                                                              โ”‚
โ”‚  ACP SERVER (for editors)              ACP CLIENT (for Python apps)          โ”‚
โ”‚  โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€             โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€          โ”‚
โ”‚                                                                              โ”‚
โ”‚  โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”                       โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”                       โ”‚
โ”‚  โ”‚ Zed/Neovim  โ”‚                       โ”‚ Your Python โ”‚                       โ”‚
โ”‚  โ”‚   Editor    โ”‚                       โ”‚    App      โ”‚                       โ”‚
โ”‚  โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”ฌโ”€โ”€โ”€โ”€โ”€โ”€โ”˜                       โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”ฌโ”€โ”€โ”€โ”€โ”€โ”€โ”˜                       โ”‚
โ”‚         โ”‚                                     โ”‚                              โ”‚
โ”‚         โ”‚ ACP                                 โ”‚ uses                         โ”‚
โ”‚         โ–ผ                                     โ–ผ                              โ”‚
โ”‚  โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”               โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”                      โ”‚
โ”‚  โ”‚ ClaudeAcpAgent   โ”‚               โ”‚   AcpClient     โ”‚โ”€โ”€โ”€โ”                  โ”‚
โ”‚  โ”‚  (ACP Server)    โ”‚               โ”‚  (ACP Client)   โ”‚   โ”‚                  โ”‚
โ”‚  โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ฌโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜               โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ฌโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜   โ”‚                  โ”‚
โ”‚           โ”‚                                  โ”‚            โ”‚ can connect to   โ”‚
โ”‚           โ”‚                                  โ”‚ ACP        โ”‚ any ACP agent    โ”‚
โ”‚           โ–ผ                                  โ–ผ            โ”‚                  โ”‚
โ”‚  โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”               โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”  โ”‚                  โ”‚
โ”‚  โ”‚  ClaudeClient    โ”‚               โ”‚  claude-code-acpโ”‚โ—„โ”€โ”˜                  โ”‚
โ”‚  โ”‚ (Python wrapper) โ”‚               โ”‚  Gemini CLI     โ”‚                      โ”‚
โ”‚  โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ฌโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜               โ”‚  Other agents   โ”‚                      โ”‚
โ”‚           โ”‚                         โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜                      โ”‚
โ”‚           โ”‚                                                                  โ”‚
โ”‚           โ–ผ                                                                  โ”‚
โ”‚  โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”                                                        โ”‚
โ”‚  โ”‚ Claude Agent SDK โ”‚                                                        โ”‚
โ”‚  โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ฌโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜                                                        โ”‚
โ”‚           โ”‚                                                                  โ”‚
โ”‚           โ–ผ                                                                  โ”‚
โ”‚  โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”                                                        โ”‚
โ”‚  โ”‚   Claude CLI     โ”‚                                                        โ”‚
โ”‚  โ”‚ (Subscription)   โ”‚                                                        โ”‚
โ”‚  โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜                                                        โ”‚
โ”‚                                                                              โ”‚
โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜

What We Built

This project combines two official SDKs to create a complete Python solution:

Integrated Components

Component Source Purpose
Agent Client Protocol SDK Anthropic ACP server/client protocol implementation
Claude Agent SDK Anthropic Claude CLI wrapper with streaming support

Our Contributions

  1. ClaudeAcpAgent (agent.py)

    • Bridges Claude Agent SDK with ACP protocol
    • Converts Claude messages to ACP session updates
    • Handles bidirectional permission requests
    • Session management (create, fork, resume, list)
  2. ClaudeClient (client.py)

    • Event-driven Python API with decorators
    • Smart text deduplication for streaming
    • Simple permission handling
    • Clean async/await interface
  3. ACP Server Entry Point

    • Standalone claude-code-acp command
    • Direct integration with Zed and other ACP clients
    • No configuration needed

Why This Package?

Approach API Key Subscription ACP Support Event-Driven
Anthropic API directly โœ… Required โŒ โŒ โŒ
Claude Agent SDK โŒ โœ… Uses CLI โŒ Partial
claude-code-acp โŒ โœ… Uses CLI โœ… Full โœ… Full

Examples

Simple Chat

import asyncio
from claude_code_acp import ClaudeClient

async def main():
    client = ClaudeClient()

    @client.on_text
    async def on_text(text):
        print(text, end="")

    while True:
        user_input = input("\nYou: ")
        if user_input.lower() == "quit":
            break
        await client.query(user_input)

asyncio.run(main())

File Operations with Permission Control

import asyncio
from claude_code_acp import ClaudeClient

async def main():
    client = ClaudeClient(cwd="/path/to/project")

    @client.on_text
    async def on_text(text):
        print(text, end="")

    @client.on_permission
    async def on_permission(name, input):
        response = input(f"Allow '{name}'? [y/N]: ")
        return response.lower() == "y"

    await client.query("Refactor the main.py file to use async/await")

asyncio.run(main())

Auto-approve Mode

import asyncio
from claude_code_acp import ClaudeClient

async def main():
    client = ClaudeClient(cwd=".")

    # Bypass all permission checks
    await client.set_mode("bypassPermissions")

    @client.on_text
    async def on_text(text):
        print(text, end="")

    await client.query("Create a complete Flask app with tests")

asyncio.run(main())

Development

# Clone
git clone https://github.com/yazelin/claude-code-acp-py
cd claude-code-acp-py

# Install dependencies
uv sync

# Run locally
uv run claude-code-acp

# Run tests
uv run python -c "from claude_code_acp import ClaudeClient; print('OK')"

Related Projects


License

MIT

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

claude_code_acp-0.3.1.tar.gz (59.1 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

claude_code_acp-0.3.1-py3-none-any.whl (17.4 kB view details)

Uploaded Python 3

File details

Details for the file claude_code_acp-0.3.1.tar.gz.

File metadata

  • Download URL: claude_code_acp-0.3.1.tar.gz
  • Upload date:
  • Size: 59.1 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.7

File hashes

Hashes for claude_code_acp-0.3.1.tar.gz
Algorithm Hash digest
SHA256 140b34e75983234acce2c74cf4aaab5f340a38b193cd45529f088be949190211
MD5 e78eea9eca063676049d29379993c82d
BLAKE2b-256 7f5bad31d43c3685c899c4298881d24c736e8624a013943632921e8d06758031

See more details on using hashes here.

Provenance

The following attestation bundles were made for claude_code_acp-0.3.1.tar.gz:

Publisher: publish.yml on yazelin/claude-code-acp-py

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file claude_code_acp-0.3.1-py3-none-any.whl.

File metadata

File hashes

Hashes for claude_code_acp-0.3.1-py3-none-any.whl
Algorithm Hash digest
SHA256 a44ed13cd264fe6767326909e40b1bb119e9a015a328e28857ce7f981883aa2d
MD5 105689eabd1e16541efd9d03eb44c4fb
BLAKE2b-256 08bd67078a8ed7ebb25dc206332f74fcc466e9a4507d7d220e40f69b5f55a949

See more details on using hashes here.

Provenance

The following attestation bundles were made for claude_code_acp-0.3.1-py3-none-any.whl:

Publisher: publish.yml on yazelin/claude-code-acp-py

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page