Installation
Maven (0.80.0 β stable)
Core SDK (client + sync/async agent APIs). From 0.18.0,acp-core contains no JSON implementation; add one JSON module next to it:
tools.jackson.databind, for example alongside Spring Boot 4), use acp-json-jackson3 in place of acp-json-jackson2. Without either, AcpJsonMapper.createDefault() fails with No AcpJsonMapperSupplier found on the classpath. AcpJsonMapper.createDefault() picks the supplier with the highest AcpJsonMapperSupplier.priority(): with both modules present Jackson 3 wins, and an applicationβs own supplier wins over both. The system property acp.json.mapper.supplier names a supplier class explicitly.
Annotation-based agent support (includes acp-core and acp-json-jackson2 transitively):
acp-websocket-jetty (single-client WebSocketAcpAgentTransport) was deprecated for removal in 0.18.0 and is removed in 0.80.0; use acp-streamable-http-jetty. See the 0.80.0 migration guide.
Gradle
Snapshot (0.81.0-SNAPSHOT)
For unreleased features, add the snapshot repository and use the snapshot version:0.81.0-SNAPSHOT in place of 0.80.0 in your dependencies.
Three Agent API Styles
Quick Comparison
All three produce identical protocol behavior and support the same capabilities.
When to Use Each
- Annotation-based β default choice. Least boilerplate, auto-converts return types, supports interceptors and custom argument resolvers.
- Sync β when you want explicit control over every handler without annotations. Blocking void methods for sending updates.
- Async β when your agent needs non-blocking I/O. Uses Project Reactor
Monofor composable async chains.
Client API
AcpClient β Factory
AcpSyncClient β Blocking Client
Builder Configuration
Example β Complete client lifecycle
This launches the Grok CLI as an ACP agent subprocess and sends it a prompt.AgentParameters builds the command line; StdioAcpClientTransport spawns the process and handles JSON-RPC framing over stdin/stdout.
Agent API β Annotation-Based
Theacp-agent-support module provides a declarative programming model using annotations.
Annotations
Class-Level
Handler Methods
Removed in 0.80.0: the session-model API.session/set_modeland the related types (@SetSessionModel,SetSessionModelRequestResolver,SetSessionModelRequest/Response,SessionModelState,ModelInfo, and themodelsfield on session responses) were deprecated for removal in 0.14.0 and are gone in 0.80.0. An agent built on 0.80.0 no longer answerssession/set_model; a peer that sends it gets βmethod not foundβ. Expose model selection throughsession/set_config_optioninstead: advertise aselectconfig option whosecategoryis"model", and switch models withsetSessionConfigOption(...). This is the same mechanism used for session modes (category: "mode") and reasoning level (category: "thought_level"). See the 0.80.0 migration guide.
Parameter Annotations
Connection-Level Parameters (0.80.0)
Any handler method, not only@Prompt, may also declare a parameter of type AcpSyncAgent/
AcpAsyncAgent (the connectionβs own agent facade, to call back into the client) or
NegotiatedCapabilities (what the connected client advertised). Resolution is by parameter type, so
order and presence are both optional:
Flexible Method Signatures
Handler methods support flexible parameter resolution:Return Value Handling
SyncPromptContext
Available in @Prompt handlers. Provides blocking methods for agent-client interaction:
ctx.async() (0.80.0) returns the same turnβs PromptContext (the async, Reactor-based view),
for the rare case where a sync handler needs to compose one of the async-only APIs. If you implement
SyncPromptContext yourself, for a test double, async() is an abstract method you must provide
too; itβs not a default method.
AcpAgentSupport β Bootstrap
For a single stdio agent, .run() blocks until the client disconnects:
buildFactory() (0.80.0) hands back an AcpAgentFactory instead of running one agent directly;
the server creates a fresh agent per connection from it, sharing the same handler bean (which must be
thread-safe) across all of them:
buildFactory() calls, if you need more than one
listener sharing the same configuration. See Transports for the
full connection lifecycle.
Interceptors
Cross-cutting concerns like logging, metrics, or error handling:Example β Complete annotation-based agent
Agent API β Sync (Builder)
Blocking handlers with plain return values. No annotations.Builder Methods
Example
Prompt Handler Context
Thecontext parameter in promptHandler provides:
Agent API β Async (Builder)
Reactive handlers returningMono. Uses Project Reactor.
Example
sendMessage(), sendSessionUpdate(), etc. return Mono<Void>, composable with .then() and .flatMap().
Convenience Methods vs Full API
The SDK provides convenience methods that cover the most common operations. Use these by default β they produce cleaner code and handle the protocol details for you.When convenience methods are enough (~80% of cases)
When to use the full API (~20% of cases)
Drop to the full API when you need control that convenience methods donβt expose:Protocol Types
All protocol types are defined inAcpSchema as Java records.
Request/Response Types
Session Types (0.12.0)
Config Option Types (0.12.0)
session/set_config_option is stable. The select variant is the stable shape; boolean is an SDK extension.
Provider Types (0.14.0, unstable)
Model/backend routing configuration. The agent advertises support with aproviders capability; the
client manages providers via listProviders / setProvider / disableProvider.
apiType / supported use well-known LlmProtocol ids (anthropic, openai, azure, vertex, bedrock) or a custom string, modeled as String in the SDK.
Elicitation Types (0.12.0, unstable)
Content Types
Session Update Types
Content chunks (
UserMessageChunk, AgentMessageChunk, AgentThoughtChunk) carry an optional messageId: chunks sharing the same id belong to one logical message, and a change in id starts a new message (0.14.0).
Stop Reasons
Convenience Methods
Capabilities
Client Capabilities
Advertised on the client builder, beforeinitialize() is called. As of 0.80.0, initialize(InitializeRequest) is removed; capabilities are set only this way. See the 0.80.0 migration guide.
A client built with the matching handlers (readTextFileHandler, writeTextFileHandler, the five
terminal handlers) advertises these capabilities automatically as of 0.80.0; setting
clientCapabilities(..) explicitly, as below, always wins over what the handlers would derive. See
Client builders derive capabilities from handlers.
.fs(new FileSystemCapability(true, true)),
.terminal(true)) still work, for a capability shape the one-flag setters donβt cover.
initialize(int protocolVersion, Map<String, Object> meta) is still available for the rare case of
pinning a specific protocol version or attaching _meta to the handshake; plain initialize() is
what nearly every client should call.
NegotiatedCapabilities
Check capabilities before using them:
require methods that throw AcpCapabilityException if unsupported:
Session Capabilities (0.12.0)
Agents advertise session management support viaSessionCapabilities. The
NegotiatedCapabilities accessors are: supportsListSessions(), supportsCloseSession(),
supportsResumeSession(), supportsDeleteSession() (0.14.0),
supportsAdditionalDirectories() (0.14.0), and supportsForkSession() (unstable) β each with a
matching require*() that throws AcpCapabilityException.
Elicitation Capabilities (stable since 0.80.0; added 0.12.0 as unstable)
Clients advertise elicitation support during initialization. The capabilityβs modes are typed (ElicitationFormCapabilities, ElicitationUrlCapabilities); the no-argument ElicitationCapabilities() constructor is removed in 0.80.0 in favor of formOnly(), urlOnly(), and formAndUrl().
A client built with a createElicitationHandler and no explicit clientCapabilities(..) advertises
form-mode elicitation automatically (0.80.0):
@UnstableAcpApi
APIs marked @UnstableAcpApi correspond to protocol elements in schema.unstable.json. They are public and functional but may change in any minor release. When the protocol element stabilizes, the annotation is removed (compatible change). See Stable vs Unstable for the full policy.
IntelliJ users can configure the Unstable API Usage inspection (Settings > Inspections > JVM languages) to flag usages.
Transports
Stdio Transport
The default transport. The client launches the agent as a subprocess and communicates via JSON-RPC over stdin/stdout. This is the same mechanism Zed, JetBrains, and VS Code use to talk to agents. Client side βAgentParameters specifies the command to launch. Any executable that speaks ACP over stdin/stdout works (Grok CLI, your own agent JAR, etc.):
Streamable HTTP and WebSocket Transport
For remote agents, per the ACP Streamable HTTP and WebSocket transport RFD. One endpoint serves HTTP POST with Server-Sent Event streams and a WebSocket upgrade on the same path. Plainhttp:// is supported: against a server that speaks h2c, every request runs on HTTP/2.
Agent (requires acp-streamable-http-jetty): StreamableHttpAcpAgentTransport is a listener, not a per-session transport. It creates one fresh agent per remote connection through an AcpAgentFactory:
AcpAgentFactory.async(...) for an AcpAgent.async(...) agent. Other constructors take a custom path and StreamableHttpAcpAgentTransportOptions (POST body cap, SSE mailbox and backpressure limits, keep-alive, HTTP/2 stream limit).
Agent in your own Servlet 6 container: StreamableHttpAcpServlet(jsonMapper, agentFactory) serves the HTTP/SSE profile at a path you choose; register it with async support. It does not serve the WebSocket upgrade. See Remote agents for a Spring Boot registration.
Client, Streamable HTTP (in acp-core):
WebSocketAcpAgentTransport (acp-websocket-jetty) was deprecated for removal in 0.18.0 and is removed in 0.80.0. It served a single WebSocket client. WebSocketAcpClientTransport clients connect to StreamableHttpAcpAgentTransport unchanged.In-Memory Transport
For testing. No subprocess or network I/O.Errors
Exception Hierarchy
Error Codes
isConcurrentPrompt() is removed in 0.80.0: a prompt sent while one is already running on the session is now a generic INVALID_REQUEST (-32600), not its own code. isAuthenticationRequired() is new. See the 0.80.0 migration guide.Agent-Side Error Handling
ThrowAcpProtocolException from handlers to send structured errors to clients:
Test Utilities
Theacp-test module provides utilities for testing without subprocesses.
InMemoryTransportPair
Packages
Maven Artifacts
acp-websocket-jetty (single-client WebSocket agent transport) is removed in 0.80.0; it is not a dependable artifact as of this release. See the 0.80.0 migration guide.
See Also
- ACP Java SDK GitHub β Source code
- ACP Java Tutorial β 32 hands-on modules
- Agent Client Protocol β Official specification