# https://github.com/OpenHands/OpenHands-CLI Project Manual

Generated at: 2026-06-15 00:48:16 UTC

## Table of Contents

- [Project Overview & System Architecture](#page-1)
- [TUI (Textual UI) & Conversation Management](#page-2)
- [ACP Server & IDE Integration](#page-3)
- [Headless, Cloud, MCP & Operations](#page-4)

<a id='page-1'></a>

## Project Overview & System Architecture

### Related Pages

Related topics: [TUI (Textual UI) & Conversation Management](#page-2), [ACP Server & IDE Integration](#page-3), [Headless, Cloud, MCP & Operations](#page-4)

<details>
<summary>Related Source Files</summary>

The following source files were used to generate this page:

- [README.md](https://github.com/OpenHands/OpenHands-CLI/blob/main/README.md)
- [openhands_cli/tui/utils/critic/__init__.py](https://github.com/OpenHands/OpenHands-CLI/blob/main/openhands_cli/tui/utils/critic/__init__.py)
- [openhands_cli/tui/utils/critic/refinement.py](https://github.com/OpenHands/OpenHands-CLI/blob/main/openhands_cli/tui/utils/critic/refinement.py)
- [openhands_cli/tui/utils/critic/visualization.py](https://github.com/OpenHands/OpenHands-CLI/blob/main/openhands_cli/tui/utils/critic/visualization.py)
- [openhands_cli/tui/utils/critic/feedback.py](https://github.com/OpenHands/OpenHands-CLI/blob/main/openhands_cli/tui/utils/critic/feedback.py)
- [openhands_cli/acp_impl/README.md](https://github.com/OpenHands/OpenHands-CLI/blob/main/openhands_cli/acp_impl/README.md)
- [openhands_cli/acp_impl/agent/base_agent.py](https://github.com/OpenHands/OpenHands-CLI/blob/main/openhands_cli/acp_impl/agent/base_agent.py)
- [openhands_cli/acp_impl/utils/mcp.py](https://github.com/OpenHands/OpenHands-CLI/blob/main/openhands_cli/acp_impl/utils/mcp.py)
- [openhands_cli/acp_impl/utils/convert.py](https://github.com/OpenHands/OpenHands-CLI/blob/main/openhands_cli/acp_impl/utils/convert.py)
- [openhands_cli/acp_impl/utils/resources.py](https://github.com/OpenHands/OpenHands-CLI/blob/main/openhands_cli/acp_impl/utils/resources.py)
- [tui_e2e/README.md](https://github.com/OpenHands/OpenHands-CLI/blob/main/tui_e2e/README.md)
</details>

# Project Overview & System Architecture

## Purpose and Scope

OpenHands-CLI is the command-line distribution of the OpenHands agent. It packages the OpenHands agent so that it can be invoked from a terminal, an IDE, CI pipelines, or any context where a shell-friendly interface is preferred. According to [README.md](https://github.com/OpenHands/OpenHands-CLI/blob/main/README.md), the project is "feature-complete" in its V1 form and is maintained primarily for stability, with only major bug fixes and compatibility updates expected. Active feature development is tracked in the main [OpenHands](https://github.com/OpenHands/OpenHands) repository.

The CLI exposes a Textual-based Terminal User Interface (TUI), an Agent Client Protocol (ACP) endpoint for IDE integration, and a set of supporting subcommands for configuration, MCP management, and conversation lifecycle. It persists its settings under `~/.openhands/`, including `agent_settings.json`, `cli_config.json`, and `mcp.json`.

## System Architecture

The repository is organized as a layered system with the CLI shell acting as a thin orchestration layer over the `openhands-sdk`. The high-level component layout is:

```mermaid
graph TD
    User[User / Editor]
    Entry[openhands CLI entrypoint]
    TUI[Textual TUI - openhands_cli/tui]
    ACP[ACP server - openhands_cli/acp_impl]
    MCP[MCP config - cli mcp subcommand]
    SDK[openhands-sdk]
    Critic[Critic subsystem - tui/utils/critic]
    Storage[(~/.openhands/)]

    User --> Entry
    Entry --> TUI
    Entry --> ACP
    Entry --> MCP
    TUI --> Critic
    TUI --> SDK
    ACP --> SDK
    SDK --> Storage
```

### Entrypoint and Command Surface

The CLI ships a single `openhands` command that branches into multiple subcommands. The default subcommand launches the interactive TUI, while `openhands acp` starts the JSON-RPC ACP server used by editors such as Zed. The ACP entry point is documented in [openhands_cli/acp_impl/README.md](https://github.com/OpenHands/OpenHands-CLI/blob/main/openhands_cli/acp_impl/README.md), which provides example `uvx` and `uv run` invocations and references the [Agent Client Protocol specification](https://agentclientprotocol.com/protocol/overview).

### TUI Subsystem

The TUI is built on the [Textual](https://textual.textualize.io/) framework and is implemented under `openhands_cli/tui/`. It handles conversation rendering, slash command parsing, confirmation-mode toggling, and persistence of user preferences. A notable in-TUI subsystem is the **Critic** module, which surfaces a quality score for each agent response. Its public surface is re-exported from [openhands_cli/tui/utils/critic/__init__.py](https://github.com/OpenHands/OpenHands-CLI/blob/main/openhands_cli/tui/utils/critic/__init__.py):

- `create_critic_collapsible` — builds a collapsible widget showing a 5-star rating and categorized issues ([visualization.py](https://github.com/OpenHands/OpenHands-CLI/blob/main/openhands_cli/tui/utils/critic/visualization.py)).
- `send_critic_inference_event` — forwards telemetry to PostHog when a critic result is rendered ([feedback.py](https://github.com/OpenHands/OpenHands-CLI/blob/main/openhands_cli/tui/utils/critic/feedback.py)).
- `build_refinement_message`, `get_high_probability_issues`, and `should_trigger_refinement` — implement the iterative refinement pattern from the SDK ([refinement.py](https://github.com/OpenHands/OpenHands-CLI/blob/main/openhands_cli/tui/utils/critic/refinement.py)). When the predicted success likelihood is below the configured threshold, or a specific issue (such as insufficient testing) exceeds the issue threshold, the TUI sends an automatic follow-up prompt to the agent.

The community has reported polish issues with this subsystem; for example, issue [#641](https://github.com/OpenHands/OpenHands-CLI/issues/641) describes a stray "accuracy" feedback row that appears in the conversation pane after subsequent events.

### ACP Subsystem

The ACP implementation lets external editors drive the OpenHands agent through a standardized JSON-RPC protocol. The abstract base class is defined in [openhands_cli/acp_impl/agent/base_agent.py](https://github.com/OpenHands/OpenHands-CLI/blob/main/openhands_cli/acp_impl/agent/base_agent.py), which exposes capability negotiation (`AgentCapabilities`, `PromptCapabilities`, `McpCapabilities`), session lifecycle (`new_session`, `load_session`, `fork_session`, `list_sessions`), and slash command processing. Subclasses specialize it for local vs. cloud execution by overriding `agent_type`, `_setup_conversation`, and `_is_authenticated`.

ACP utilities translate between ACP-shaped data and the SDK's internal types:

- [acp_impl/utils/convert.py](https://github.com/OpenHands/OpenHands-CLI/blob/main/openhands_cli/acp_impl/utils/convert.py) maps ACP prompt content blocks (text, image, resource links, embedded resources) to the SDK's `TextContent` and `ImageContent` objects.
- [acp_impl/utils/resources.py](https://github.com/OpenHands/OpenHands-CLI/blob/main/openhands_cli/acp_impl/utils/resources.py) materializes embedded resources: text resources are inlined into the message, supported image MIME types are returned inline as data URIs, and unsupported binaries are saved under `~/.openhands/cache/acp/`.
- [acp_impl/utils/mcp.py](https://github.com/OpenHands/OpenHands-CLI/blob/main/openhands_cli/acp_impl/utils/mcp.py) converts ACP MCP server configurations (stdio, HTTP, SSE) into the dict-of-dicts shape expected by the SDK agent, flattening the serialized `EnvVariable` lists into environment dictionaries.

The protocol variant requested in issue [#66](https://github.com/OpenHands/OpenHands-CLI/issues/66) — full Agent Client Protocol support for Zed and other non-VSCode editors — is delivered through this subsystem.

### MCP, Critic Telemetry, and Configuration

The CLI exposes MCP server management through a dedicated subcommand. Community requests such as issue [#694](https://github.com/OpenHands/OpenHands-CLI/issues/694) propose extending it with `openhands mcp test <name>` for connectivity validation. Settings persistence uses three JSON files under `~/.openhands/`, as documented in [README.md](https://github.com/OpenHands/OpenHands-CLI/blob/main/README.md). By default, environment variables such as `LLM_API_KEY`, `LLM_MODEL`, and `LLM_BASE_URL` are ignored; passing `--override-with-envs` opts in for the current session. Issue [#673](https://github.com/OpenHands/OpenHands-CLI/issues/673) notes that the CLI still prompts for an API key even when running against local LLMs that do not require one.

Critic telemetry is sent to PostHog using the key and host defined in [feedback.py](https://github.com/OpenHands/OpenHands-CLI/blob/main/openhands_cli/tui/utils/critic/feedback.py). The module degrades gracefully if `posthog` is not installed, so the TUI still functions in offline or minimal installs.

### End-to-End Testing Infrastructure

The `tui_e2e/` directory hosts an end-to-end testing harness that drives the real CLI against a mock OpenAI-compatible LLM server. As described in [tui_e2e/README.md](https://github.com/OpenHands/OpenHands-CLI/blob/main/tui_e2e/README.md), tests load a recorded trajectory, replay its `MessageEvent` and `ActionEvent` entries through the mock server, and validate that the CLI's `ObservationEvent` outputs match expectations. This gives the project deterministic coverage of TUI behaviors without depending on a live model.

## See Also

- ACP integration details: [openhands_cli/acp_impl/README.md](https://github.com/OpenHands/OpenHands-CLI/blob/main/openhands_cli/acp_impl/README.md)
- Critic iterative refinement reference: [openhands_cli/tui/utils/critic/refinement.py](https://github.com/OpenHands/OpenHands-CLI/blob/main/openhands_cli/tui/utils/critic/refinement.py)
- Community discussion on LLM profile management: [#68](https://github.com/OpenHands/OpenHands-CLI/issues/68)
- Community discussion on conversation history listing: [#101](https://github.com/OpenHands/OpenHands-CLI/issues/101)
- Community discussion on planning mode: [#613](https://github.com/OpenHands/OpenHands-CLI/issues/613)

---

<a id='page-2'></a>

## TUI (Textual UI) & Conversation Management

### Related Pages

Related topics: [Project Overview & System Architecture](#page-1), [Headless, Cloud, MCP & Operations](#page-4)

<details>
<summary>Related Source Files</summary>

The following source files were used to generate this page:

- [README.md](https://github.com/OpenHands/OpenHands-CLI/blob/main/README.md)
- [openhands_cli/tui/utils/critic/__init__.py](https://github.com/OpenHands/OpenHands-CLI/blob/main/openhands_cli/tui/utils/critic/__init__.py)
- [openhands_cli/tui/utils/critic/visualization.py](https://github.com/OpenHands/OpenHands-CLI/blob/main/openhands_cli/tui/utils/critic/visualization.py)
- [openhands_cli/tui/utils/critic/feedback.py](https://github.com/OpenHands/OpenHands-CLI/blob/main/openhands_cli/tui/utils/critic/feedback.py)
- [openhands_cli/tui/utils/critic/refinement.py](https://github.com/OpenHands/OpenHands-CLI/blob/main/openhands_cli/tui/utils/critic/refinement.py)
- [openhands_cli/acp_impl/agent/base_agent.py](https://github.com/OpenHands/OpenHands-CLI/blob/main/openhands_cli/acp_impl/agent/base_agent.py)
- [openhands_cli/acp_impl/utils/convert.py](https://github.com/OpenHands/OpenHands-CLI/blob/main/openhands_cli/acp_impl/utils/convert.py)
- [openhands_cli/acp_impl/utils/mcp.py](https://github.com/OpenHands/OpenHands-CLI/blob/main/openhands_cli/acp_impl/utils/mcp.py)
- [tui_e2e/README.md](https://github.com/OpenHands/OpenHands-CLI/blob/main/tui_e2e/README.md)
</details>

# TUI (Textual UI) & Conversation Management

## Overview and Scope

The OpenHands CLI ships a Textual-based terminal UI that wraps an SDK conversation loop and provides rich, in-terminal feedback while an agent executes a task. The TUI is the primary interaction surface for `openhands` and is complemented by an ACP mode that reuses the same conversation primitives to talk to external editors (e.g. Zed) via JSON-RPC. Source: [README.md](https://github.com/OpenHands/OpenHands-CLI/blob/main/README.md).

Conversation management in this codebase covers:

- **Conversation loop and confirmation** — driving the SDK `BaseConversation` with confirmation gates before any agent action.
- **Critic visualization** — turning SDK `CriticResult` objects into collapsible, filtered, CLI-friendly panels.
- **User feedback capture** — letting the user rate the critic's predictions and reporting them to PostHog.
- **Iterative refinement** — feeding critic-detected issues back to the agent as a follow-up message until the score crosses a threshold or iterations are exhausted.
- **Resumption and event history** — rehydrating prior conversation events on resume (tracked as an enhancement in the community, see issue #204).

## Critic Visualization in the TUI

The TUI renders critic results using a CLI-specific subset of the SDK's star-rating format. `create_critic_collapsible` builds a `Collapsible` widget whose title shows the score as a five-star rating and whose body lists only the categories that are actionable in a terminal — **Potential Issues** and **Infrastructure**. Sections such as *Likely Follow-up* and *Other* are intentionally suppressed. Source: [openhands_cli/tui/utils/critic/visualization.py](https://github.com/OpenHands/OpenHands-CLI/blob/main/openhands_cli/tui/utils/critic/visualization.py).

```python
from openhands_cli.tui.utils.critic import create_critic_collapsible
widget = create_critic_collapsible(critic_result)
```

The collapsible is auto-expanded only when there is content to show, which keeps the transcript compact for clean runs. Source: [openhands_cli/tui/utils/critic/visualization.py](https://github.com/OpenHands/OpenHands-CLI/blob/main/openhands_cli/tui/utils/critic/visualization.py).

A known UX bug (community issue #641) is that the in-conversation "accuracy" feedback button row can appear above subsequent agent actions when the event ordering is not strictly enforced; the widget labels are `[1] Accurate`, `[2] Too high`, `[3] Too low`, `[4] N/A`, `[0] Dismiss`. Source: [openhands_cli/tui/utils/critic/feedback.py](https://github.com/OpenHands/OpenHands-CLI/blob/main/openhands_cli/tui/utils/critic/feedback.py).

## User Feedback Capture and PostHog Reporting

`send_critic_inference_event` is fired as soon as a critic result is rendered — before the user has judged it — so the inference itself is tracked separately from the feedback signal. The widget exposes `CriticFeedbackWidget` whose `BUTTON_LABELS` map human-readable labels to internal codes (`accurate`, `too_high`, `too_low`, `not_applicable`, `dismiss`). Source: [openhands_cli/tui/utils/critic/feedback.py](https://github.com/OpenHands/OpenHands-CLI/blob/main/openhands_cli/tui/utils/critic/feedback.py).

PostHog is treated as an optional dependency: the module imports `posthog` inside a guarded `try/except` and short-circuits if `POSTHOG_AVAILABLE` is `False`, so TUI sessions never crash on missing analytics. Source: [openhands_cli/tui/utils/critic/feedback.py](https://github.com/OpenHands/OpenHands-CLI/blob/main/openhands_cli/tui/utils/critic/feedback.py).

## Iterative Refinement Loop

Refinement is the bridge between passive critic display and active conversation steering. `should_trigger_refinement` decides whether the loop continues, `get_high_probability_issues` extracts agent-behavioural issues above the configured `issue_threshold`, and `build_refinement_message` produces a follow-up prompt that is sent back to the SDK conversation. Source: [openhands_cli/tui/utils/critic/refinement.py](https://github.com/OpenHands/OpenHands-CLI/blob/main/openhands_cli/tui/utils/critic/refinement.py).

Refinement is triggered in two ways:

1. The overall success score is below the critic threshold, **or**
2. Any specific behavioural issue has a probability above the `issue_threshold` (default `0.75`), even if the overall score is acceptable.

Source: [openhands_cli/tui/utils/critic/refinement.py](https://github.com/OpenHands/OpenHands-CLI/blob/main/openhands_cli/tui/utils/critic/refinement.py).

```mermaid
flowchart LR
    A[Agent finishes turn] --> B[Critic evaluates result]
    B --> C{Core critic<br/>package}
    C --> D[TUI: create_critic_collapsible<br/>+ send_critic_inference_event]
    D --> E[User feedback widget<br/>PostHog event]
    C --> F{should_trigger_refinement?}
    F -- yes --> G[build_refinement_message<br/>get_high_probability_issues]
    G --> H[Send follow-up to SDK conversation]
    H --> A
    F -- no --> I[Conversation idle]
```

## Conversation Plumbing: Runner and ACP Path

The same "drive a conversation, gate each action" pattern is shared between the TUI runner and the ACP server. `BaseOpenHandsACPAgent` is the abstract base used by both local and cloud ACP implementations and exposes a `run_conversation_with_confirmation` helper imported from `openhands_cli.acp_impl.runner`. Source: [openhands_cli/acp_impl/agent/base_agent.py](https://github.com/OpenHands/OpenHands-CLI/blob/main/openhands_cli/acp_impl/agent/base_agent.py).

Slash commands (`/confirm`, `/help`, unknown-command handling, confirmation-mode toggling) are parsed and applied through helpers in `openhands_cli.acp_impl.slash_commands` and re-imported by the ACP base agent; this is the same surface that the TUI's input bar uses. Source: [openhands_cli/acp_impl/agent/base_agent.py](https://github.com/OpenHands/OpenHands-CLI/blob/main/openhands_cli/acp_impl/agent/base_agent.py).

### MCP and Content Conversion

When a client (TUI or ACP) supplies MCP servers or content blocks, they are normalised into the SDK's `McpServer` and `TextContent`/`ImageContent` shapes:

- `convert_acp_mcp_servers_to_agent_format` reshapes ACP `McpServerStdio`, `HttpMcpServer`, and `SseMcpServer` into the agent's dict-keyed format and flattens `EnvVariable` arrays into plain dicts. Source: [openhands_cli/acp_impl/utils/mcp.py](https://github.com/OpenHands/OpenHands-CLI/blob/main/openhands_cli/acp_impl/utils/mcp.py).
- `convert_acp_prompt_to_message_content` accepts strings, single blocks, or lists mixing text and image blocks and produces the list of `TextContent | ImageContent` that the SDK expects. Source: [openhands_cli/acp_impl/utils/convert.py](https://github.com/OpenHands/OpenHands-CLI/blob/main/openhands_cli/acp_impl/utils/convert.py).
- Embedded resource blobs with unsupported image MIME types are converted via `_convert_image_to_supported_format`; otherwise they are written to disk and surfaced as `TextContent` references. Source: [openhands_cli/acp_impl/utils/resources.py](https://github.com/OpenHands/OpenHands-CLI/blob/main/openhands_cli/acp_impl/utils/resources.py).

## Conversation State, Configuration, and Persistence

CLI/TUI preferences live under `~/.openhands/`:

| File | Purpose | Source |
|------|---------|--------|
| `agent_settings.json` | Persisted agent settings, including condenser config | [README.md](https://github.com/OpenHands/OpenHands-CLI/blob/main/README.md) |
| `cli_config.json` | CLI/TUI preferences (e.g. critic enabled) | [README.md](https://github.com/OpenHands/OpenHands-CLI/blob/main/README.md) |
| `mcp.json` | MCP server configuration for the conversation | [README.md](https://github.com/OpenHands/OpenHands-CLI/blob/main/README.md) |

By default, environment variables such as `LLM_API_KEY`, `LLM_MODEL`, and `LLM_BASE_URL` are ignored at the TUI layer; passing `--override-with-envs` makes them apply (without being persisted). Source: [README.md](https://github.com/OpenHands/OpenHands-CLI/blob/main/README.md). Note that community issue #673 reports the CLI still prompts for an API key when using local LLMs (e.g. Ollama) that do not require one.

Resuming a conversation should rehydrate the pane with prior events; this is the subject of enhancement issue #204 and is a recurring UX gap reported by users.

## End-to-End Testing of the TUI

`tui_e2e/` runs real conversations against a mock OpenAI-compatible `/chat/completions` server that replays scripted trajectories. Each trajectory is a directory of event JSON files (`MessageEvent`, `ActionEvent`, `ObservationEvent`) keyed by a stable UUID, and the mock server returns the next scripted response on each request while the CLI executes tool calls for real. Source: [tui_e2e/README.md](https://github.com/OpenHands/OpenHands-CLI/blob/main/tui_e2e/README.md).

Adding a new TUI scenario is a three-step workflow: drop a trajectory under `tests/trajectories/<name>/`, write a `test_<feature>.py` that drives the CLI through `MockLLMServer`, and register the function in `runner.py` so it is picked up by the suite. Source: [tui_e2e/README.md](https://github.com/OpenHands/OpenHands-CLI/blob/main/tui_e2e/README.md).

## Common Failure Modes

- **Terminal state on exit.** Some commands leave residual escape sequences; users have to run `reset` or `stty sane` after exit (community issue #671).
- **Missing API key prompt suppression for local LLMs.** The TUI still asks for `LLM_API_KEY` when targeting providers like Ollama (community issue #673).
- **Stale critic button row.** The `[1] Accurate` button row can render above subsequent actions when event ordering is loose (community issue #641).
- **No event rehydration on resume.** When resuming a saved conversation, prior events are not always shown in the pane (community issue #204).
- **Posthog missing.** Analytics is optional; if `posthog` is not installed, both `send_critic_inference_event` and `CriticFeedbackWidget` silently no-op rather than failing.

## See Also

- [README.md](https://github.com/OpenHands/OpenHands-CLI/blob/main/README.md) — installation, configuration paths, and top-level usage.
- [openhands_cli/acp_impl/README.md](https://github.com/OpenHands/OpenHands-CLI/blob/main/openhands_cli/acp_impl/README.md) — Agent Client Protocol integration and editor wiring (e.g. Zed).
- [tui_e2e/README.md](https://github.com/OpenHands/OpenHands-CLI/blob/main/tui_e2e/README.md) — trajectory-based end-to-end testing for the TUI.

---

<a id='page-3'></a>

## ACP Server & IDE Integration

### Related Pages

Related topics: [Project Overview & System Architecture](#page-1), [Headless, Cloud, MCP & Operations](#page-4)

<details>
<summary>Related Source Files</summary>

The following source files were used to generate this page:

- [openhands_cli/acp_impl/README.md](https://github.com/OpenHands/OpenHands-CLI/blob/main/openhands_cli/acp_impl/README.md)
- [openhands_cli/acp_impl/utils/mcp.py](https://github.com/OpenHands/OpenHands-CLI/blob/main/openhands_cli/acp_impl/utils/mcp.py)
- [openhands_cli/acp_impl/utils/convert.py](https://github.com/OpenHands/OpenHands-CLI/blob/main/openhands_cli/acp_impl/utils/convert.py)
- [openhands_cli/acp_impl/utils/resources.py](https://github.com/OpenHands/OpenHands-CLI/blob/main/openhands_cli/acp_impl/utils/resources.py)
- [openhands_cli/acp_impl/utils/__init__.py](https://github.com/OpenHands/OpenHands-CLI/blob/main/openhands_cli/acp_impl/utils/__init__.py)
- [openhands_cli/acp_impl/agent/base_agent.py](https://github.com/OpenHands/OpenHands-CLI/blob/main/openhands_cli/acp_impl/agent/base_agent.py)
- [openhands_cli/acp_impl/agent/local_agent.py](https://github.com/OpenHands/OpenHands-CLI/blob/main/openhands_cli/acp_impl/agent/local_agent.py)
- [openhands_cli/acp_impl/agent/remote_agent.py](https://github.com/OpenHands/OpenHands-CLI/blob/main/openhands_cli/acp_impl/agent/remote_agent.py)
- [openhands_cli/acp_impl/agent/util.py](https://github.com/OpenHands/OpenHands-CLI/blob/main/openhands_cli/acp_impl/agent/util.py)
- [openhands_cli/acp_impl/agent/__init__.py](https://github.com/OpenHands/OpenHands-CLI/blob/main/openhands_cli/acp_impl/agent/__init__.py)
- [README.md](https://github.com/OpenHands/OpenHands-CLI/blob/main/README.md)
</details>

# ACP Server & IDE Integration

## Overview and Purpose

The **Agent Client Protocol (ACP)** integration allows OpenHands to act as a backend agent for code editors and IDEs that speak the standardized JSON-RPC 2.0 interface defined by the [Agent Client Protocol](https://agentclientprotocol.com/protocol/overview). Concretely, the CLI exposes an `openhands acp` entry point that IDEs such as Zed can spawn as a subprocess; the IDE then drives conversations, mode changes, and session lifecycle through ACP, while OpenHands handles prompt execution, MCP servers, confirmation, and event streaming.

This integration is the implementation behind community feature request [#66 — ACP support](https://github.com/OpenHands/OpenHands-CLI/issues/66), which explicitly requests the ability to launch OpenHands in the Zed editor via ACP. The README documents that OpenHands-CLI is a slim wrapper around the SDK and exposes MCP servers, confirmation modes, cloud conversations, and headless mode ([README.md](https://github.com/OpenHands/OpenHands-CLI/blob/main/README.md)); the ACP server is the same engine surfaced over a different transport.

## Architecture and Agent Implementations

The ACP implementation lives under `openhands_cli/acp_impl/` and is structured around a small inheritance hierarchy of agent classes plus a set of transport and content-conversion utilities.

```mermaid
flowchart LR
    IDE[ACP Client<br/>e.g. Zed] -- JSON-RPC 2.0 --> Server[openhands acp]
    subgraph CLI[acp_impl/]
        Server --> Launcher[agent/launcher.py<br/>run_acp_server]
        Launcher --> Base[BaseOpenHandsACPAgent]
        Base --> Local[LocalOpenHandsACPAgent]
        Base --> Remote[OpenHandsCloudACPAgent]
        Local --> SDKConv[SDK LocalConversation]
        Remote --> SDKConv2[SDK RemoteConversation<br/>+ OpenHandsCloudWorkspace]
    end
    Base --> Slash[slash_commands]
    Base --> Confirm[confirmation modes]
    Local --> MCP[MCP servers<br/>utils/mcp.py]
    Local --> Res[resources.py<br/>images / blobs / text]
```

- **`BaseOpenHandsACPAgent`** is an abstract class registered as an `acp.Agent`. It implements initialization, slash command processing, prompt handling with confirmation, and session management. It defines an `agent_type` property and a `_setup_conversation()` hook that subclasses must implement ([base_agent.py](https://github.com/OpenHands/OpenHands-CLI/blob/main/openhands_cli/acp_impl/agent/base_agent.py)).
- **`LocalOpenHandsACPAgent`** is the local workspace variant. It uses the SDK's `LocalConversation` with a `Workspace` rooted in the user's working directory, registers built-in subagents via `register_builtins_agents()`, loads MCP configuration from `MCP_CONFIG_FILE`, and persists conversations under the standard `get_conversations_dir()` location ([local_agent.py](https://github.com/OpenHands/OpenHands-CLI/blob/main/openhands_cli/acp_impl/agent/local_agent.py)).
- **`OpenHandsCloudACPAgent`** is the cloud variant. It uses `RemoteConversation` together with `OpenHandsCloudWorkspace`, requires an authenticated user (via `TokenStorage` and `is_token_valid`), and is reachable through a configurable `cloud_api_url` (default `https://app.all-hands.dev`) ([remote_agent.py](https://github.com/OpenHands/OpenHands-CLI/blob/main/openhands_cli/acp_impl/agent/remote_agent.py)).
- **`run_acp_server`** is the entry point exposed from the package root ([agent/__init__.py](https://github.com/OpenHands/OpenHands-CLI/blob/main/openhands_cli/acp_impl/agent/__init__.py)).

The base agent advertises its capabilities to the IDE through `AgentCapabilities`, `PromptCapabilities`, and `McpCapabilities`, and propagates confirmation-mode changes through `SessionModeState` constructed in `get_session_mode_state()` ([agent/util.py](https://github.com/OpenHands/OpenHands-CLI/blob/main/openhands_cli/acp_impl/agent/util.py)).

## Configuring an IDE (Zed)

ACP is launched through the CLI subcommand `openhands acp`. The ACP README documents two concrete ways to wire it into Zed's `agent_servers` configuration ([acp_impl/README.md](https://github.com/OpenHands/OpenHands-CLI/blob/main/openhands_cli/acp_impl/README.md)):

```json
"OpenHands-uvx": {
  "command": "uvx",
  "args": [
    "--from",
    "git+https://github.com/OpenHands/OpenHands-CLI.git@xw/acp-simplification",
    "openhands",
    "acp"
  ],
  "env": {}
}
```

The first config uses `uvx` to install and run a specific PR branch, while the second uses a local checkout via `uv run --project /YOUR_LOCAL_PATH/OpenHands-CLI openhands acp`. The same `acp` subcommand is what the launcher ultimately invokes, so any ACP-compliant client that accepts a child-process command should be able to integrate OpenHands the same way. For debugging, the README recommends opening ACP logs in Zed with `Cmd+Shift+P` before starting a conversation.

## Content, Resource, and MCP Conversion

ACP and the OpenHands SDK use different on-the-wire formats for prompts, resources, and MCP server definitions. The `acp_impl/utils/` package bridges these formats.

- **Prompt conversion.** `convert_acp_prompt_to_message_content()` turns a list of ACP content blocks into the SDK's `TextContent` / `ImageContent` objects. It handles `ACPTextContentBlock`, `ACPImageContentBlock`, and resource blocks, and delegates to `convert_resources_to_content()` for embedded resources ([utils/convert.py](https://github.com/OpenHands/OpenHands-CLI/blob/main/openhands_cli/acp_impl/utils/convert.py)).
- **Image handling.** `_convert_image_block()` returns an `ImageContent` for natively supported MIME types, attempts a Pillow-based conversion for unsupported image types, and otherwise falls back to a `TextContent` describing the file path ([utils/convert.py](https://github.com/OpenHands/OpenHands-CLI/blob/main/openhands_cli/acp_impl/utils/convert.py)). The list of supported types lives in `SUPPORTED_IMAGE_MIME_TYPES` in [utils/resources.py](https://github.com/OpenHands/OpenHands-CLI/blob/main/openhands_cli/acp_impl/utils/resources.py).
- **Blob and text resources.** `_materialize_embedded_resource()` expands an `ACPEmbeddedResourceContentBlock` into either an `ImageContent` (for supported or convertible images) or a temporary file on disk under `~/.openhands/cache/acp/` for binary blobs that cannot be inlined. Text resources are wrapped in a `BEGIN/END USER PROVIDED ADDITIONAL CONTEXT` envelope ([utils/resources.py](https://github.com/OpenHands/OpenHands-CLI/blob/main/openhands_cli/acp_impl/utils/resources.py)). The same module also injects a `RESOURCE_SKILL` so the agent knows how to interpret these user-provided blocks.
- **MCP server conversion.** ACP represents each MCP server as a Pydantic model with a `name` field and an `env` array, while the SDK agent expects a name-keyed dict with a `transport` field and a dict-formatted `env`. `convert_acp_mcp_servers_to_agent_format()` performs this translation and infers the transport from the server class (`McpServerStdio` → `stdio`, `HttpMcpServer` → `http`, `SseMcpServer` → `sse`) ([utils/mcp.py](https://github.com/OpenHands/OpenHands-CLI/blob/main/openhands_cli/acp_impl/utils/mcp.py)). The function is re-exported from the package `__init__` ([utils/__init__.py](https://github.com/OpenHands/OpenHands-CLI/blob/main/openhands_cli/acp_impl/utils/__init__.py)).

The local agent injects `RESOURCE_SKILL` into its conversation and reads MCP configuration from `MCP_CONFIG_FILE`; the cloud agent performs the same wiring but on top of `RemoteConversation` and `OpenHandsCloudWorkspace` ([local_agent.py](https://github.com/OpenHands/OpenHands-CLI/blob/main/openhands_cli/acp_impl/agent/local_agent.py), [remote_agent.py](https://github.com/OpenHands/OpenHands-CLI/blob/main/openhands_cli/acp_impl/agent/remote_agent.py)).

## See Also

- [Agent Client Protocol specification](https://agentclientprotocol.com/protocol/overview)
- OpenHands ACP user guide: <https://docs.openhands.dev/openhands/usage/run-openhands/acp#zed-ide>
- Community feature request: [#66 — ACP support](https://github.com/OpenHands/OpenHands-CLI/issues/66)
- Related: MCP server management and `openhands mcp` commands ([README.md](https://github.com/OpenHands/OpenHands-CLI/blob/main/README.md))

---

<a id='page-4'></a>

## Headless, Cloud, MCP & Operations

### Related Pages

Related topics: [Project Overview & System Architecture](#page-1), [TUI (Textual UI) & Conversation Management](#page-2), [ACP Server & IDE Integration](#page-3)

<details>
<summary>Related Source Files</summary>

The following source files were used to generate this page:

- [README.md](https://github.com/OpenHands/OpenHands-CLI/blob/main/README.md)
- [openhands_cli/acp_impl/README.md](https://github.com/OpenHands/OpenHands-CLI/blob/main/openhands_cli/acp_impl/README.md)
- [openhands_cli/acp_impl/utils/__init__.py](https://github.com/OpenHands/OpenHands-CLI/blob/main/openhands_cli/acp_impl/utils/__init__.py)
- [openhands_cli/acp_impl/utils/mcp.py](https://github.com/OpenHands/OpenHands-CLI/blob/main/openhands_cli/acp_impl/utils/mcp.py)
- [openhands_cli/acp_impl/utils/convert.py](https://github.com/OpenHands/OpenHands-CLI/blob/main/openhands_cli/acp_impl/utils/convert.py)
- [openhands_cli/acp_impl/utils/resources.py](https://github.com/OpenHands/OpenHands-CLI/blob/main/openhands_cli/acp_impl/utils/resources.py)
- [openhands_cli/acp_impl/agent/base_agent.py](https://github.com/OpenHands/OpenHands-CLI/blob/main/openhands_cli/acp_impl/agent/base_agent.py)
- [openhands_cli/tui/utils/critic/__init__.py](https://github.com/OpenHands/OpenHands-CLI/blob/main/openhands_cli/tui/utils/critic/__init__.py)
- [openhands_cli/tui/utils/critic/visualization.py](https://github.com/OpenHands/OpenHands-CLI/blob/main/openhands_cli/tui/utils/critic/visualization.py)
- [openhands_cli/tui/utils/critic/refinement.py](https://github.com/OpenHands/OpenHands-CLI/blob/main/openhands_cli/tui/utils/critic/refinement.py)
- [openhands_cli/tui/utils/critic/feedback.py](https://github.com/OpenHands/OpenHands-CLI/blob/main/openhands_cli/tui/utils/critic/feedback.py)
</details>

# Headless, Cloud, MCP & Operations

This page covers the operational surface area of OpenHands V1 CLI that extends beyond the interactive TUI: **headless execution**, **cloud task delegation**, **MCP server configuration**, and the **Agent Client Protocol (ACP)** integration that lets IDEs such as Zed drive the agent. It also documents the **critic-driven refinement loop**, which is the CLI's primary runtime quality gate.

> Project status note: The README states *"OpenHands V1 CLI is feature-complete and primarily maintained for stability. Expect only major bug fixes and compatibility updates; new features are unlikely."* Source: [README.md](https://github.com/OpenHands/OpenHands-CLI/blob/main/README.md)

## Configuration Surface

All persistent state lives under `~/.openhands/`:

| File | Purpose |
| --- | --- |
| `agent_settings.json` | Persisted agent settings (including condenser config) |
| `cli_config.json` | CLI/TUI preferences (e.g., critic enabled) |
| `mcp.json` | MCP server configuration |

By default, environment variables such as `LLM_API_KEY`, `LLM_MODEL`, and `LLM_BASE_URL` are ignored at startup; the flag `--override-with-envs` re-enables them (non-persistent). Source: [README.md](https://github.com/OpenHands/OpenHands-CLI/blob/main/README.md)

## MCP Server Management

The CLI exposes a top-level `mcp` namespace for managing [Model Context Protocol](https://modelcontextprotocol.io/) servers:

```bash
openhands mcp list                                # show configured servers
openhands mcp add tavily --transport stdio \
  npx -- -y mcp-remote "https://mcp.tavily.com/mcp/?tavilyApiKey=<key>"
openhands mcp enable <server-name>
openhands mcp disable <server-name>
```

A community feature request ([#694](https://github.com/OpenHands/OpenHands-CLI/issues/694)) is open for `openhands mcp test <name>` to validate a configured server before use.

When the agent is hosted inside an ACP-compliant IDE, the MCP definitions arrive in ACP's Pydantic format (`McpServerStdio` | `HttpMcpServer` | `SseMcpServer`) with `name` fields and serialized `EnvVariable` arrays. The CLI converts these to the agent's internal dict-keyed format using `convert_acp_mcp_servers_to_agent_format`, which flattens env vars via `_convert_env_to_dict` and adds the required `transport` discriminator. Source: [openhands_cli/acp_impl/utils/mcp.py](https://github.com/OpenHands/OpenHands-CLI/blob/main/openhands_cli/acp_impl/utils/mcp.py)

## Headless & Cloud Execution Modes

### Headless Mode

Designed for CI/CD pipelines and automation, headless mode skips the Textual UI:

```bash
openhands --headless -t "Write unit tests for parser.py"
```

### Confirmation Modes

The interactive entry point also accepts action-handling policies:

```bash
openhands                  # ask for confirmation on each action (default)
openhands --always-approve # auto-approve all actions (alias: --yolo)
openhands --llm-approve    # use an LLM-based security analyzer
```

### Cloud Conversations

For sandboxed, hosted execution, the CLI delegates to OpenHands Cloud:

```bash
openhands login            # fetch and cache cloud settings
openhands cloud -t "Fix the login bug"
```

Source: [README.md](https://github.com/OpenHands/OpenHands-CLI/blob/main/README.md)

## ACP (Agent Client Protocol) Integration

ACP enables editors like [Zed](https://zed.dev/blog/claude-code-via-acp) to drive the OpenHands agent over a JSON-RPC 2.0 interface. The CLI is launched as an ACP server via the `openhands acp` subcommand. Source: [openhands_cli/acp_impl/README.md](https://github.com/OpenHands/OpenHands-CLI/blob/main/openhands_cli/acp_impl/README.md)

```mermaid
flowchart LR
    Zed["Zed IDE (ACP client)"] -- JSON-RPC 2.0 --> Base["BaseOpenHandsACPAgent"]
    Base -- prompt --> Conv["run_conversation_with_confirmation"]
    Conv --> Agent["OpenHands SDK Agent"]
    Base -- convert --> Utils["acp_impl/utils"]
    Utils -- format --> Agent
    Agent -- events --> Base
    Base -- chunks --> Zed
```

Key responsibilities of the ACP layer:

- **Session lifecycle** — handled by `BaseOpenHandsACPAgent`, an abstract base that defines `agent_type`, `_setup_conversation()`, `_cleanup_session()`, and `_is_authenticated()`. Source: [openhands_cli/acp_impl/agent/base_agent.py](https://github.com/OpenHands/OpenHands-CLI/blob/main/openhands_cli/acp_impl/agent/base_agent.py)
- **Prompt conversion** — `convert_acp_prompt_to_message_content` accepts strings, lists of content blocks, or single `ContentBlock` objects and emits `TextContent` / `ImageContent` items. Source: [openhands_cli/acp_impl/utils/convert.py](https://github.com/OpenHands/OpenHands-CLI/blob/main/openhands_cli/acp_impl/utils/convert.py)
- **Resource materialization** — `convert_resources_to_content` handles inline `EmbeddedResourceContentBlock`s: supported image MIME types are returned as data-URI `ImageContent`, unsupported images are converted via Pillow, and other blobs are saved to `~/.openhands/cache/acp/` and referenced by URI. Source: [openhands_cli/acp_impl/utils/resources.py](https://github.com/OpenHands/OpenHands-CLI/blob/main/openhands_cli/acp_impl/utils/resources.py)
- **MCP bridging** — see previous section. Source: [openhands_cli/acp_impl/utils/__init__.py](https://github.com/OpenHands/OpenHands-CLI/blob/main/openhands_cli/acp_impl/utils/__init__.py)

A persistent community request ([#66](https://github.com/OpenHands/OpenHands-CLI/issues/66)) tracks upstream ACP support and additional editor integrations.

## Critic-Driven Refinement Operations

The CLI embeds an SDK-supplied critic that scores each completed task and, when the score is low or a high-probability issue is detected, automatically issues a follow-up prompt to the agent. This is the CLI's iterative-quality loop.

The refinement module:

- Extracts high-probability issues via `get_high_probability_issues(critic_result, issue_threshold)` from `agent_behavioral_issues` and other categorized metadata.
- Builds a follow-up message via `build_refinement_message(...)` that reports the predicted success likelihood (e.g., *"predicted success likelihood: 62.3%"*) and lists specific issues above the threshold.
- Gates refinement using `should_trigger_refinement(...)`.

Source: [openhands_cli/tui/utils/critic/refinement.py](https://github.com/OpenHands/OpenHands-CLI/blob/main/openhands_cli/tui/utils/critic/refinement.py)

Visualization side:

- `create_critic_collapsible` renders a 5-star rating and a CLI-filtered list of `Potential Issues` / `Infrastructure` categories (Likely Follow-up and Other sections are intentionally omitted from the CLI). Source: [openhands_cli/tui/utils/critic/visualization.py](https://github.com/OpenHands/OpenHands-CLI/blob/main/openhands_cli/tui/utils/critic/visualization.py)
- `send_critic_inference_event` and the `CriticFeedbackWidget` emit anonymized PostHog telemetry (`POSTHOG_API_KEY`, `POSTHOG_HOST = https://us.i.posthog.com`) when the user rates a prediction as *Accurate*, *Too high*, *Too low*, or *N/A*. Source: [openhands_cli/tui/utils/critic/feedback.py](https://github.com/OpenHands/OpenHands-CLI/blob/main/openhands_cli/tui/utils/critic/feedback.py)

The critic surface is re-exported through `openhands_cli.tui.utils.critic`. Source: [openhands_cli/tui/utils/critic/__init__.py](https://github.com/OpenHands/OpenHands-CLI/blob/main/openhands_cli/tui/utils/critic/__init__.py)

## Common Operational Pitfalls

- **Resumed conversations show no prior events** — a known UI gap; the conversation pane must be rehydrated from the trajectory on resume. Tracking issue: [#204](https://github.com/OpenHands/OpenHands-CLI/issues/204).
- **Terminal left in raw state on exit** — users occasionally need `stty sane` or `reset` after exit. Tracking issue: [#671](https://github.com/OpenHands/OpenHands-CLI/issues/671).
- **API key still requested for local LLMs** — local/Ollama models do not require an API key; the CLI prompt has historically not reflected that. Tracking issue: [#673](https://github.com/OpenHands/OpenHands-CLI/issues/673).
- **Critic button ordering** — the *Accurate* feedback button can appear below subsequent events. Tracking issue: [#641](https://github.com/OpenHands/OpenHands-CLI/issues/641).

## See Also

- [OpenHands-CLI Repository](https://github.com/OpenHands/OpenHands-CLI)
- [OpenHands V1 Documentation](https://docs.openhands.dev/openhands/usage/cli/installation)
- [Agent Client Protocol Specification](https://agentclientprotocol.com/protocol/overview)
- [OpenHands Software Agent SDK](https://github.com/OpenHands/software-agent-sdk)

---

<!-- evidence_pipeline_checked: true -->
<!-- evidence_injected: true -->

---

## Pitfall Log

Project: OpenHands/OpenHands-CLI

Summary: Found 13 structured pitfall item(s), including 1 high/blocking item(s). Top priority: Security or permission risk - Security or permission risk requires verification.

## 1. Security or permission risk - Security or permission risk requires verification

- Severity: high
- Evidence strength: source_linked
- Finding: Project evidence flags a security or permission risk. Review the linked source before relying on this workflow.
- User impact: May increase setup, validation, or first-run risk for the user.
- Evidence: community_evidence:github | https://github.com/OpenHands/OpenHands-CLI/issues/694

## 2. Identity risk - Identity risk requires verification

- Severity: medium
- Evidence strength: runtime_trace
- Finding: Project evidence flags a identity risk. Review the linked source before relying on this workflow.
- User impact: May increase setup, validation, or first-run risk for the user.
- Repro command: `uv tool install openhands`
- Evidence: identity.distribution | github_repo:1049343369 | https://github.com/OpenHands/OpenHands-CLI

## 3. Installation risk - Installation risk requires verification

- Severity: medium
- Evidence strength: source_linked
- Finding: Project evidence flags a installation risk. Review the linked source before relying on this workflow.
- User impact: May increase setup, validation, or first-run risk for the user.
- Evidence: community_evidence:github | https://github.com/OpenHands/OpenHands-CLI/issues/641

## 4. Configuration risk - Configuration risk requires verification

- Severity: medium
- Evidence strength: source_linked
- Finding: Project evidence flags a configuration risk. Review the linked source before relying on this workflow.
- User impact: May increase setup, validation, or first-run risk for the user.
- Evidence: community_evidence:github | https://github.com/OpenHands/OpenHands-CLI/issues/671

## 5. Capability evidence risk - Capability evidence risk requires verification

- Severity: medium
- Evidence strength: source_linked
- Finding: README/documentation is current enough for a first validation pass.
- User impact: May increase setup, validation, or first-run risk for the user.
- Evidence: capability.assumptions | github_repo:1049343369 | https://github.com/OpenHands/OpenHands-CLI

## 6. Maintenance risk - Maintenance risk requires verification

- Severity: medium
- Evidence strength: source_linked
- Finding: Project evidence flags a maintenance risk. Review the linked source before relying on this workflow.
- User impact: May increase setup, validation, or first-run risk for the user.
- Evidence: evidence.maintainer_signals | github_repo:1049343369 | https://github.com/OpenHands/OpenHands-CLI

## 7. Security or permission risk - Security or permission risk requires verification

- Severity: medium
- Evidence strength: source_linked
- Finding: no_demo
- User impact: May increase setup, validation, or first-run risk for the user.
- Evidence: downstream_validation.risk_items | github_repo:1049343369 | https://github.com/OpenHands/OpenHands-CLI

## 8. Security or permission risk - Security or permission risk requires verification

- Severity: medium
- Evidence strength: source_linked
- Finding: no_demo
- User impact: May increase setup, validation, or first-run risk for the user.
- Evidence: risks.scoring_risks | github_repo:1049343369 | https://github.com/OpenHands/OpenHands-CLI

## 9. Security or permission risk - Security or permission risk requires verification

- Severity: medium
- Evidence strength: source_linked
- Finding: Project evidence flags a security or permission risk. Review the linked source before relying on this workflow.
- User impact: May increase setup, validation, or first-run risk for the user.
- Evidence: community_evidence:github | https://github.com/OpenHands/OpenHands-CLI/issues/68

## 10. Security or permission risk - Security or permission risk requires verification

- Severity: medium
- Evidence strength: source_linked
- Finding: Project evidence flags a security or permission risk. Review the linked source before relying on this workflow.
- User impact: May increase setup, validation, or first-run risk for the user.
- Evidence: community_evidence:github | https://github.com/OpenHands/OpenHands-CLI/issues/409

## 11. Security or permission risk - Security or permission risk requires verification

- Severity: medium
- Evidence strength: source_linked
- Finding: Project evidence flags a security or permission risk. Review the linked source before relying on this workflow.
- User impact: May increase setup, validation, or first-run risk for the user.
- Evidence: community_evidence:github | https://github.com/OpenHands/OpenHands-CLI/issues/673

## 12. Maintenance risk - Maintenance risk requires verification

- Severity: low
- Evidence strength: source_linked
- Finding: issue_or_pr_quality=unknown。
- User impact: May increase setup, validation, or first-run risk for the user.
- Evidence: evidence.maintainer_signals | github_repo:1049343369 | https://github.com/OpenHands/OpenHands-CLI

## 13. Maintenance risk - Maintenance risk requires verification

- Severity: low
- Evidence strength: source_linked
- Finding: release_recency=unknown。
- User impact: May increase setup, validation, or first-run risk for the user.
- Evidence: evidence.maintainer_signals | github_repo:1049343369 | https://github.com/OpenHands/OpenHands-CLI

<!-- canonical_name: OpenHands/OpenHands-CLI; human_manual_source: deepwiki_human_wiki -->
