-
Notifications
You must be signed in to change notification settings - Fork 12
docs(sdk): add ACP Agent guide #351
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
Merged
Changes from all commits
Commits
Show all changes
6 commits
Select commit
Hold shift + click to select a range
c0a393c
docs(sdk): add ACP Agent guide
simonrosenberg 718ed57
docs(sdk): apply review suggestions for ACP Agent guide
openhands-agent 8b53e4b
Merge branch 'main' into feat/acp-agent
simonrosenberg 5b6d365
Merge branch 'main' into feat/acp-agent
simonrosenberg bb768a9
fix: address review feedback for ACP Agent guide
openhands-agent 4f2ee5b
refactor: condense How It Works section to key architectural points
openhands-agent File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,156 @@ | ||
| --- | ||
| title: ACP Agent | ||
| description: Delegate to an ACP-compatible server (Claude Code, Gemini CLI, etc.) instead of calling an LLM directly. | ||
| --- | ||
|
|
||
| > A ready-to-run example is available [here](#ready-to-run-example)! | ||
|
|
||
| `ACPAgent` lets you use any [Agent Client Protocol](https://agentclientprotocol.com/protocol/overview) server as the backend for an OpenHands conversation. Instead of calling an LLM directly, the agent spawns an ACP server subprocess and communicates with it over JSON-RPC. The server manages its own LLM, tools, and execution — your code just sends messages and collects responses. | ||
|
|
||
| ## Basic Usage | ||
simonrosenberg marked this conversation as resolved.
Show resolved
Hide resolved
|
||
|
|
||
| ```python icon="python" highlight={5,7-9} | ||
| from openhands.sdk.agent import ACPAgent | ||
| from openhands.sdk.conversation import Conversation | ||
|
|
||
| # Point at any ACP-compatible server | ||
| agent = ACPAgent(acp_command=["npx", "-y", "claude-code-acp"]) | ||
|
|
||
| conversation = Conversation(agent=agent, workspace="./my-project") | ||
| conversation.send_message("Explain the architecture of this project.") | ||
| conversation.run() | ||
|
|
||
| agent.close() | ||
| ``` | ||
|
|
||
| The `acp_command` is the shell command used to spawn the server process. The SDK communicates with it over stdin/stdout JSON-RPC. | ||
|
|
||
| <Note> | ||
| **Key difference from standard agents:** With `ACPAgent`, you don't need an `LLM_API_KEY` in your code. The ACP server handles its own LLM authentication and API calls. This is *delegation* — your code sends messages to the ACP server, which manages all LLM interactions internally. | ||
| </Note> | ||
|
|
||
| ### What ACPAgent Does Not Support | ||
|
|
||
| Because the ACP server manages its own tools and context, these `AgentBase` features are not available on `ACPAgent`: | ||
|
|
||
| - `tools` / `include_default_tools` — the server has its own tools | ||
| - `mcp_config` — configure MCP on the server side | ||
| - `condenser` — the server manages its own context window | ||
| - `critic` — the server manages its own evaluation | ||
| - `agent_context` — configure the server directly | ||
|
|
||
| Passing any of these raises `NotImplementedError` at initialization. | ||
|
|
||
| ## How It Works | ||
|
|
||
| - **Subprocess delegation**: `ACPAgent` spawns the ACP server and communicates via JSON-RPC over stdin/stdout | ||
| - **Server-managed execution**: The ACP server handles its own LLM calls, tools, and context — your code just sends messages | ||
| - **Auto-approval**: Permission requests from the server are automatically granted, so ensure you trust the ACP server you're running | ||
| - **Metrics collection**: Token usage and costs from the server are captured into the agent's `LLM.metrics` | ||
|
|
||
| ## Configuration | ||
|
|
||
| ### Server Command and Arguments | ||
|
|
||
| ```python icon="python" | ||
| agent = ACPAgent( | ||
| acp_command=["npx", "-y", "claude-code-acp"], | ||
| acp_args=["--profile", "my-profile"], # extra CLI args | ||
| acp_env={"CLAUDE_API_KEY": "sk-..."}, # extra env vars | ||
| ) | ||
| ``` | ||
|
|
||
| | Parameter | Description | | ||
| |-----------|-------------| | ||
| | `acp_command` | Command to start the ACP server (required) | | ||
| | `acp_args` | Additional arguments appended to the command | | ||
| | `acp_env` | Additional environment variables for the server process | | ||
|
|
||
| ## Metrics | ||
|
|
||
| Token usage and cost data are automatically captured from the ACP server's responses. You can inspect them through the standard `LLM.metrics` interface: | ||
|
|
||
| ```python icon="python" | ||
| metrics = agent.llm.metrics | ||
| print(f"Total cost: ${metrics.accumulated_cost:.6f}") | ||
|
|
||
| for usage in metrics.token_usages: | ||
| print(f" prompt={usage.prompt_tokens} completion={usage.completion_tokens}") | ||
| ``` | ||
|
|
||
| Usage data comes from two ACP protocol sources: | ||
| - **`PromptResponse.usage`** — per-turn token counts (input, output, cached, reasoning tokens) | ||
| - **`UsageUpdate` notifications** — cumulative session cost and context window size | ||
|
|
||
| ## Cleanup | ||
|
|
||
| Always call `agent.close()` when you are done to terminate the ACP server subprocess. A `try/finally` block is recommended: | ||
|
|
||
| ```python icon="python" | ||
| agent = ACPAgent(acp_command=["npx", "-y", "claude-code-acp"]) | ||
| try: | ||
| conversation = Conversation(agent=agent, workspace=".") | ||
| conversation.send_message("Hello!") | ||
| conversation.run() | ||
| finally: | ||
| agent.close() | ||
| ``` | ||
|
|
||
| ## Ready-to-run Example | ||
|
|
||
| <Note> | ||
| This example is available on GitHub: [examples/01_standalone_sdk/40_acp_agent_example.py](https://github.com/OpenHands/software-agent-sdk/blob/main/examples/01_standalone_sdk/40_acp_agent_example.py) | ||
| </Note> | ||
|
|
||
| ```python icon="python" expandable examples/01_standalone_sdk/40_acp_agent_example.py | ||
| """Example: Using ACPAgent with Claude Code ACP server. | ||
|
|
||
| This example shows how to use an ACP-compatible server (claude-code-acp) | ||
| as the agent backend instead of direct LLM calls. | ||
|
|
||
| Prerequisites: | ||
| - Node.js / npx available | ||
| - Claude Code CLI authenticated (or CLAUDE_API_KEY set) | ||
|
|
||
| Usage: | ||
| uv run python examples/01_standalone_sdk/40_acp_agent_example.py | ||
| """ | ||
|
|
||
| import os | ||
|
|
||
| from openhands.sdk.agent import ACPAgent | ||
| from openhands.sdk.conversation import Conversation | ||
|
|
||
|
|
||
| agent = ACPAgent(acp_command=["npx", "-y", "claude-code-acp"]) | ||
|
|
||
| try: | ||
| cwd = os.getcwd() | ||
| conversation = Conversation(agent=agent, workspace=cwd) | ||
|
|
||
| conversation.send_message( | ||
| "List the Python source files under openhands-sdk/openhands/sdk/agent/, " | ||
| "then read the __init__.py and summarize what agent classes are exported." | ||
| ) | ||
| conversation.run() | ||
| finally: | ||
| # Clean up the ACP server subprocess | ||
| agent.close() | ||
|
|
||
| print("Done!") | ||
| ``` | ||
|
|
||
simonrosenberg marked this conversation as resolved.
Show resolved
Hide resolved
simonrosenberg marked this conversation as resolved.
Show resolved
Hide resolved
simonrosenberg marked this conversation as resolved.
Show resolved
Hide resolved
|
||
| This example does not use an LLM API key directly — the ACP server (Claude Code) handles authentication on its own. | ||
|
|
||
| ```bash Running the Example | ||
| # Ensure Claude Code CLI is authenticated first | ||
| # (or set CLAUDE_API_KEY in your environment) | ||
| cd software-agent-sdk | ||
| uv run python examples/01_standalone_sdk/40_acp_agent_example.py | ||
| ``` | ||
|
|
||
| ## Next Steps | ||
|
|
||
| - **[Creating Custom Agents](/sdk/guides/agent-custom)** — Build specialized agents with custom tool sets and system prompts | ||
| - **[Agent Delegation](/sdk/guides/agent-delegation)** — Compose multiple agents for complex workflows | ||
| - **[LLM Metrics](/sdk/guides/metrics)** — Track token usage and costs across models | ||
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.