Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 10 additions & 2 deletions .github/workflows/on-release-main.yml
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@ on:
types: [published]

jobs:

set-version:
runs-on: ubuntu-24.04
steps:
Expand Down Expand Up @@ -63,4 +62,13 @@ jobs:
uses: ./.github/actions/setup-python-env

- name: Deploy documentation
run: uv run mkdocs gh-deploy --force
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
GIT_AUTHOR_NAME: acp-bot
GIT_AUTHOR_EMAIL: noreply@github.com
GIT_COMMITTER_NAME: acp-bot
GIT_COMMITTER_EMAIL: noreply@github.com
run: |
git config user.name "$GIT_AUTHOR_NAME"
git config user.email "$GIT_AUTHOR_EMAIL"
uv run mkdocs gh-deploy --force --remote-branch gh-pages --remote-name origin
7 changes: 7 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -209,3 +209,10 @@ cython_debug/
marimo/_static/
marimo/_lsp/
__marimo__/

# .zed
.zed/

# others
reference/
.DS_Store
19 changes: 19 additions & 0 deletions AGENTS.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
# Repository Guidelines

## Project Structure & Module Organization
The package code lives under `src/acp`, exposing the high-level Agent, transport helpers, and generated protocol schema. Generated artifacts such as `schema/` and `src/acp/schema.py` are refreshed via `scripts/gen_all.py` against the upstream ACP schema. Integration examples are in `examples/`, including `echo_agent.py` and the mini SWE bridge. Tests reside in `tests/` with async fixtures and doctests; documentation sources live in `docs/` and publish via MkDocs. Built distributions drop into `dist/` after builds.

## Build, Test, and Development Commands
Run `make install` to create a `uv` managed virtualenv and install pre-commit hooks. `make check` executes lock verification, Ruff linting, `ty` static checks, and deptry analysis. `make test` calls `uv run python -m pytest --doctest-modules`. For release prep use `make build` or `make build-and-publish`. `make gen-all` regenerates protocol models; export `ACP_SCHEMA_VERSION=<ref>` beforehand to fetch a specific upstream schema (defaults to the cached copy). `make docs` serves MkDocs locally; `make docs-test` ensures clean builds.

## Coding Style & Naming Conventions
Target Python 3.10+ with type hints and 120-character lines enforced by Ruff (`pyproject.toml`). Prefer dataclasses/pydantic models from the schema modules rather than bare dicts. Tests may ignore security lint (see per-file ignores) but still follow snake_case names. Keep public API modules under `acp/*` lean; place utilities in internal `_`-prefixed modules when needed.

## Testing Guidelines
Pytest is the main framework with `pytest-asyncio` for coroutine tests and doctests activated on modules. Name test files `test_*.py` and co-locate fixtures under `tests/conftest.py`. Aim to cover new protocol surfaces with integration-style tests using the async agent stubs. Generate coverage reports via `tox -e py310` when assessing CI parity.

## Commit & Pull Request Guidelines
Commit history follows Conventional Commits (`feat:`, `fix:`, `docs:`). Scope commits narrowly and include context on affected protocol version or tooling. PRs should describe agent behaviors exercised, link related issues, and mention schema regeneration if applicable. Attach test output (`make check` or targeted pytest) and screenshots only when UI-adjacent docs change. Update docs/examples when altering the public agent API.

## Agent Integration Tips
Leverage `examples/mini_swe_agent/` as a template when bridging other command executors. Use `AgentSideConnection` with `stdio_streams()` for ACP-compliant clients; document any extra environment variables in README updates.
87 changes: 42 additions & 45 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,69 +1,56 @@
# Agent Client Protocol (Python)

A Python implementation of the Agent Client Protocol (ACP). Use it to build agents that communicate with ACP-capable clients (e.g. Zed) over stdio.
Python SDK for the Agent Client Protocol (ACP). Build agents that speak ACP over stdio so tools like Zed can orchestrate them.

- Package name: `agent-client-protocol` (import as `acp`)
- Repository: https://github.com/psiace/agent-client-protocol-python
- Docs: https://psiace.github.io/agent-client-protocol-python/
- Featured: Listed as the first third-party SDK on the official ACP site — see https://agentclientprotocol.com/libraries/community
> Each release tracks the matching ACP schema version. Contributions that improve coverage or tooling are very welcome.

**Highlights**

- Typed dataclasses generated from the upstream ACP schema (`acp.schema`)
- Async agent base class plus stdio transport helpers for quick bootstrapping
- Included examples that stream content updates and tool calls end-to-end

## Install

```bash
pip install agent-client-protocol
# or
# or with uv
uv add agent-client-protocol
```

## Development (contributors)
## Quickstart

```bash
make install # set up venv
make check # lint + typecheck
make test # run tests
```
1. Install the package and point your ACP-capable client at the provided echo agent:
```bash
pip install agent-client-protocol
python examples/echo_agent.py
```
2. Wire it into your client (e.g. Zed → Agents panel) so stdio is connected; the SDK handles JSON-RPC framing and lifecycle messages.

## Minimal agent example
Prefer a step-by-step walkthrough? Read the [Quickstart guide](docs/quickstart.md) or the hosted docs: https://psiace.github.io/agent-client-protocol-python/.

See a complete streaming echo example in [examples/echo_agent.py](examples/echo_agent.py). It streams back each text block using `session/update` and ends the turn.
### Minimal agent sketch

```python
import asyncio

from acp import (
Agent,
AgentSideConnection,
InitializeRequest,
InitializeResponse,
NewSessionRequest,
NewSessionResponse,
PromptRequest,
PromptResponse,
SessionNotification,
stdio_streams,
)
from acp.schema import ContentBlock1, SessionUpdate2
from acp import Agent, AgentSideConnection, PromptRequest, PromptResponse, SessionNotification, stdio_streams
from acp.schema import AgentMessageChunk, TextContentBlock


class EchoAgent(Agent):
def __init__(self, conn):
self._conn = conn

async def initialize(self, params: InitializeRequest) -> InitializeResponse:
return InitializeResponse(protocolVersion=params.protocolVersion)

async def newSession(self, params: NewSessionRequest) -> NewSessionResponse:
return NewSessionResponse(sessionId="sess-1")

async def prompt(self, params: PromptRequest) -> PromptResponse:
for block in params.prompt:
text = block.get("text", "") if isinstance(block, dict) else getattr(block, "text", "")
text = getattr(block, "text", "")
await self._conn.sessionUpdate(
SessionNotification(
sessionId=params.sessionId,
update=SessionUpdate2(
update=AgentMessageChunk(
sessionUpdate="agent_message_chunk",
content=ContentBlock1(type="text", text=text),
content=TextContentBlock(type="text", text=text),
),
)
)
Expand All @@ -80,17 +67,27 @@ if __name__ == "__main__":
asyncio.run(main())
```

Run this executable from your ACP-capable client (e.g. configure Zed to launch it). The library takes care of the stdio JSON-RPC transport.
Full example with streaming and lifecycle hooks lives in [examples/echo_agent.py](examples/echo_agent.py).

## Example: Mini SWE Agent bridge
## Examples

A minimal ACP bridge for mini-swe-agent is provided under [`examples/mini_swe_agent`](examples/mini_swe_agent/README.md). It demonstrates:

- Parsing a prompt from ACP content blocks
- Streaming agent output via `session/update`
- Mapping command execution to `tool_call` and `tool_call_update`
- `examples/mini_swe_agent`: bridges mini-swe-agent into ACP, including a duet launcher and Textual TUI client
- Additional transport helpers are documented in the [Mini SWE guide](docs/mini-swe-agent.md)

## Documentation

- Quickstart: [docs/quickstart.md](docs/quickstart.md)
- Mini SWE Agent example: [docs/mini-swe-agent.md](docs/mini-swe-agent.md)
- Project docs (MkDocs): https://psiace.github.io/agent-client-protocol-python/
- Local sources: `docs/`
- [Quickstart](docs/quickstart.md)
- [Mini SWE Agent bridge](docs/mini-swe-agent.md)

## Development workflow

```bash
make install # create uv virtualenv and install hooks
ACP_SCHEMA_VERSION=<ref> make gen-all # refresh generated schema bindings
make check # lint, types, dependency analysis
make test # run pytest + doctests
```

After local changes, consider updating docs/examples if the public API surface shifts.
79 changes: 21 additions & 58 deletions docs/index.md
Original file line number Diff line number Diff line change
@@ -1,67 +1,30 @@
# Agent Client Protocol (Python)
# Agent Client Protocol SDK (Python)

A Python implementation of the Agent Client Protocol (ACP). Build agents that communicate with ACP-capable clients (e.g. Zed) over stdio.
Welcome to the Python SDK for the Agent Client Protocol (ACP). The package ships ready-to-use transports, typed protocol models, and examples that stream messages to ACP-aware clients such as Zed.

## Install
## What you get

```bash
pip install agent-client-protocol
```
- Fully typed dataclasses generated from the upstream ACP schema (`acp.schema`)
- Async agent base class and stdio helpers to spin up an agent in a few lines
- Examples that demonstrate streaming updates and tool execution over ACP

## Minimal usage
## Getting started

```python
import asyncio
1. Install the package:
```bash
pip install agent-client-protocol
```
2. Launch the provided echo agent to verify your setup:
```bash
python examples/echo_agent.py
```
3. Point your ACP-capable client at the running process (for Zed, configure an Agent Server entry). The SDK takes care of JSON-RPC framing and lifecycle transitions.

from acp import (
Agent,
AgentSideConnection,
InitializeRequest,
InitializeResponse,
NewSessionRequest,
NewSessionResponse,
PromptRequest,
PromptResponse,
SessionNotification,
stdio_streams,
)
from acp.schema import ContentBlock1, SessionUpdate2
Prefer a guided tour? Head to the [Quickstart](quickstart.md) for step-by-step instructions, including how to run the agent from an editor or terminal.

## Documentation map

class EchoAgent(Agent):
def __init__(self, conn):
self._conn = conn
- [Quickstart](quickstart.md): install, run, and extend the echo agent
- [Mini SWE Agent guide](mini-swe-agent.md): bridge mini-swe-agent over ACP, including duet launcher and Textual client

async def initialize(self, params: InitializeRequest) -> InitializeResponse:
return InitializeResponse(protocolVersion=params.protocolVersion)

async def newSession(self, params: NewSessionRequest) -> NewSessionResponse:
return NewSessionResponse(sessionId="sess-1")

async def prompt(self, params: PromptRequest) -> PromptResponse:
for block in params.prompt:
text = block.get("text", "") if isinstance(block, dict) else getattr(block, "text", "")
await self._conn.sessionUpdate(
SessionNotification(
sessionId=params.sessionId,
update=SessionUpdate2(
sessionUpdate="agent_message_chunk",
content=ContentBlock1(type="text", text=text),
),
)
)
return PromptResponse(stopReason="end_turn")


async def main() -> None:
reader, writer = await stdio_streams()
AgentSideConnection(lambda conn: EchoAgent(conn), writer, reader)
await asyncio.Event().wait()


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

- Quickstart: [quickstart.md](quickstart.md)
- Mini SWE Agent example: [mini-swe-agent.md](mini-swe-agent.md)
Source code lives under `src/acp/`, while tests and additional examples are available in `tests/` and `examples/`. If you plan to contribute, see the repository README for the development workflow.
63 changes: 34 additions & 29 deletions docs/mini-swe-agent.md
Original file line number Diff line number Diff line change
@@ -1,49 +1,54 @@
# Mini SWE Agent bridge

> Just a show of the bridge in action. Not a best-effort or absolutely-correct implementation of the agent.
This example wraps mini-swe-agent behind ACP so editors such as Zed can interact with it over stdio. A duet launcher is included to run a local Textual client beside the bridge for quick experimentation.

This example wraps mini-swe-agent behind ACP so Zed can run it as an external agent over stdio. It also includes a local Textual UI client connected via a duet launcher
## Overview

## Behavior
- Accepts ACP prompts, concatenates text blocks, and forwards them to mini-swe-agent
- Streams language-model output via `session/update` → `agent_message_chunk`
- Emits `tool_call` / `tool_call_update` pairs for shell execution, including stdout and return codes
- Sends a final `agent_message_chunk` when mini-swe-agent prints `COMPLETE_TASK_AND_SUBMIT_FINAL_OUTPUT`

- Prompts: text blocks are concatenated into a single task string. (Resource embedding is not used in this example.)
- Streaming: only LM output is streamed via `session/update` → `agent_message_chunk`.
- Tool calls: when the agent executes a shell command, the bridge sends:
- `tool_call` with `kind=execute`, pending status, and a bash code block containing the command
- `tool_call_update` upon completion, including output and a `rawOutput` object with `output` and `returncode`
- Final result: on task submission (mini-swe-agent prints `COMPLETE_TASK_AND_SUBMIT_FINAL_OUTPUT` as the first line), a final `agent_message_chunk` with the submission content is sent.
## Requirements

## Configuration
- Python environment with `mini-swe-agent` installed (`pip install mini-swe-agent`)
- ACP-capable client (e.g. Zed) or the bundled Textual client
- Optional: `.env` file at the repo root for shared configuration when using the duet launcher

Environment variables control the model:
If `mini-swe-agent` is missing, the bridge falls back to the reference copy at `reference/mini-swe-agent/src`.

- `MINI_SWE_MODEL`: model ID (e.g. `openrouter/openai/gpt-4o-mini`)
- `OPENROUTER_API_KEY` for OpenRouter; or `OPENAI_API_KEY` / `ANTHROPIC_API_KEY` for native providers
- Optional `MINI_SWE_MODEL_KWARGS`: JSON, e.g. `{ "api_base": "https://openrouter.ai/api/v1" }` (auto-injected for OpenRouter if missing)
## Configure models and credentials

Agent behavior automatically maps the appropriate API key based on the chosen model and available environment variables.
Set environment variables before launching the bridge:

If `mini-swe-agent` is not installed in the venv, the bridge attempts to import a vendored reference copy under `reference/mini-swe-agent/src`.
- `MINI_SWE_MODEL`: model identifier such as `openrouter/openai/gpt-4o-mini`
- `OPENROUTER_API_KEY` for OpenRouter models, or `OPENAI_API_KEY` / `ANTHROPIC_API_KEY` for native providers
- Optional `MINI_SWE_MODEL_KWARGS`: JSON blob of extra keyword arguments (OpenRouter defaults are injected automatically when omitted)

## How to run
The bridge selects the correct API key based on the chosen model and available variables.

- In Zed (editor integration): configure an agent server to launch `examples/mini_swe_agent/agent.py` and set the environment variables there. Use Zed’s “Open ACP Logs” to inspect `tool_call`/`tool_call_update` and message chunks.
- In terminal (local TUI): run the duet launcher to start both the agent and the Textual client with the same environment and dedicated pipes:
## Run inside Zed

Add an Agent Server entry targeting `examples/mini_swe_agent/agent.py` and provide the environment variables there. Use Zed’s “Open ACP Logs” panel to observe streamed message chunks and tool call events in real time.

## Run locally with the duet launcher

To pair the bridge with the Textual TUI client, run:

```bash
python examples/mini_swe_agent/duet.py
```

The launcher loads `.env` from the repo root (using python-dotenv) so both processes share the same configuration.

### TUI usage
Both processes inherit settings from `.env` (thanks to `python-dotenv`) and communicate over dedicated pipes.

- Hotkeys: `y` → YOLO, `c` → Confirm, `u` → Human, `Enter` → Continue.
- In Human mode, you’ll be prompted for a bash command; it will be executed and streamed back as a tool call.
- Each executed command appears in the “TOOL CALLS” section with live status and output.
**TUI shortcuts**
- `y`: YOLO
- `c`: Confirm
- `u`: Human (prompts for a shell command and streams it back as a tool call)
- `Enter`: Continue

## Files
## Related files

- Agent entry: [`examples/mini_swe_agent/agent.py`](https://github.com/psiace/agent-client-protocol-python/blob/main/examples/mini_swe_agent/agent.py)
- Duet launcher: [`examples/mini_swe_agent/duet.py`](https://github.com/psiace/agent-client-protocol-python/blob/main/examples/mini_swe_agent/duet.py)
- Textual client: [`examples/mini_swe_agent/client.py`](https://github.com/psiace/agent-client-protocol-python/blob/main/examples/mini_swe_agent/client.py)
- Agent entrypoint: `examples/mini_swe_agent/agent.py`
- Duet launcher: `examples/mini_swe_agent/duet.py`
- Textual client: `examples/mini_swe_agent/client.py`
Loading