Skip to content

Repository files navigation

ACP Java SDK

Documentation: https://springaicommunity.mintlify.app/acp-java-sdk | API Reference | Tutorial

Pure Java implementation of the Agent Client Protocol (ACP) specification for building both clients and agents.

Overview

The Agent Client Protocol (ACP) standardizes communication between code editors and coding agents. This SDK provides two modules:

  • Client — connect to and interact with ACP-compliant agents
  • Agent — build ACP-compliant agents in Java

Three API styles for building agents:

StyleBest forExample
Annotation-basedLeast boilerplate — @AcpAgent, @Prompt annotationsJump to example
SyncSimple blocking handlers with plain return valuesJump to example
AsyncReactive applications using Project Reactor MonoJump to example

Key Features:

  • Java 17+, type-safe, stdio and WebSocket transports
  • Capability negotiation and structured error handling
  • For a hands-on walkthrough, see the ACP Java Tutorial

Installation

<dependency>
<groupId>com.agentclientprotocol</groupId>
<artifactId>acp-core</artifactId>
<version>0.14.0</version>
</dependency>

For annotation-based agent development:

<dependency>
<groupId>com.agentclientprotocol</groupId>
<artifactId>acp-agent-support</artifactId>
<version>0.14.0</version>
</dependency>

For WebSocket server support (agents accepting WebSocket connections):

<dependency>
<groupId>com.agentclientprotocol</groupId>
<artifactId>acp-websocket-jetty</artifactId>
<version>0.14.0</version>
</dependency>

For snapshot builds (unreleased features), add the snapshot repository and use 0.15.0-SNAPSHOT:

<repositories>
<repository>
<id>central-snapshots</id>
<url>https://central.sonatype.com/repository/maven-snapshots/</url>
<snapshots><enabled>true</enabled></snapshots>
<releases><enabled>false</enabled></releases>
</repository>
</repositories>

Getting Started

1. Hello World Client

Connect to an ACP agent and send a prompt (tutorial):

importcom.agentclientprotocol.sdk.client.*;
importcom.agentclientprotocol.sdk.client.transport.*;
importcom.agentclientprotocol.sdk.spec.AcpSchema.*;
importjava.util.List;
// Launch Gemini CLI as an ACP agent subprocessvarparams = AgentParameters.builder("gemini").arg("--experimental-acp").build();
vartransport = newStdioAcpClientTransport(params);
// Create client — sessionUpdateConsumer prints the agent's streamed responseAcpSyncClientclient = AcpClient.sync(transport)
.sessionUpdateConsumer(notification -> {
if (notification.update() instanceofAgentMessageChunkmsg) {
System.out.print(((TextContent) msg.content()).text());
}
})
.build();
// Three-phase lifecycle: initialize → session → promptclient.initialize();
varsession = client.newSession(newNewSessionRequest("/workspace", List.of()));
varresponse = client.prompt(newPromptRequest(
session.sessionId(),
List.of(newTextContent("What is 2+2? Reply with just the number."))
));
// Output: 4// Stop reason: END_TURNSystem.out.println("\nStop reason: " + response.stopReason());
client.close();

2. Hello World Agent (Annotation-Based)

The simplest way to build an agent — use annotations (tutorial):

importcom.agentclientprotocol.sdk.annotation.*;
importcom.agentclientprotocol.sdk.agent.SyncPromptContext;
importcom.agentclientprotocol.sdk.agent.support.AcpAgentSupport;
importcom.agentclientprotocol.sdk.spec.AcpSchema.*;
@AcpAgentclassHelloAgent {
@InitializeInitializeResponseinit() {
returnInitializeResponse.ok();
}
@NewSessionNewSessionResponsenewSession() {
returnnewNewSessionResponse(UUID.randomUUID().toString(), null, null);
}
@PromptPromptResponseprompt(PromptRequestreq, SyncPromptContextctx) {
ctx.sendMessage("Hello from the agent!");
returnPromptResponse.endTurn();
}
}
// Bootstrap and runAcpAgentSupport.create(newHelloAgent())
.transport(newStdioAcpAgentTransport())
.run();

3. Hello World Agent (Sync)

The builder API with blocking handlers and plain return values (tutorial):

importcom.agentclientprotocol.sdk.agent.*;
importcom.agentclientprotocol.sdk.agent.transport.*;
importcom.agentclientprotocol.sdk.spec.AcpSchema.*;
importjava.util.UUID;
vartransport = newStdioAcpAgentTransport();
// Sync agent — plain return values, no MonoAcpSyncAgentagent = AcpAgent.sync(transport)
.initializeHandler(req -> InitializeResponse.ok())
.newSessionHandler(req ->
newNewSessionResponse(UUID.randomUUID().toString(), null, null))
.promptHandler((req, context) -> {
context.sendMessage("Hello from the agent!"); // blocking void methodreturnPromptResponse.endTurn();
})
.build();
agent.run(); // Blocks until client disconnects

4. Hello World Agent (Async)

For reactive applications, use the async API with Project Reactor (tutorial):

importcom.agentclientprotocol.sdk.agent.*;
importcom.agentclientprotocol.sdk.agent.transport.*;
importcom.agentclientprotocol.sdk.spec.AcpSchema.*;
importreactor.core.publisher.Mono;
importjava.util.UUID;
vartransport = newStdioAcpAgentTransport();
// Async agent — handlers return MonoAcpAsyncAgentagent = AcpAgent.async(transport)
.initializeHandler(req -> Mono.just(InitializeResponse.ok()))
.newSessionHandler(req -> Mono.just(
newNewSessionResponse(UUID.randomUUID().toString(), null, null)))
.promptHandler((req, context) ->
context.sendMessage("Hello from the agent!")
.then(Mono.just(PromptResponse.endTurn())))
.build();
agent.start().then(agent.awaitTermination()).block();

Progressive Examples

5. Streaming Updates

Send real-time updates to the client during prompt processing (tutorial: client-side, agent-side).

Annotation-based:

@PromptPromptResponseprompt(PromptRequestreq, SyncPromptContextctx) {
ctx.sendThought("Thinking...");
ctx.sendMessage("Here's my response.");
returnPromptResponse.endTurn();
}

Sync:

.promptHandler((req, context) -> {
context.sendThought("Thinking...");
context.sendMessage("Here's my response.");
returnPromptResponse.endTurn();
})

Async:

.promptHandler((req, context) ->
context.sendThought("Thinking...")
.then(context.sendMessage("Here's my response."))
.then(Mono.just(PromptResponse.endTurn())))

Client - receiving updates:

AcpSyncClientclient = AcpClient.sync(transport)
.sessionUpdateConsumer(notification -> {
varupdate = notification.update();
if (updateinstanceofAgentMessageChunkmsg) {
System.out.print(((TextContent) msg.content()).text());
}
})
.build();

6. Agent-to-Client Requests

Agents can request file operations from the client (tutorial). The context parameter provides access to all agent capabilities.

Agent (Sync) - reading files:

AcpSyncAgentagent = AcpAgent.sync(transport)
.promptHandler((req, context) -> {
// Convenience methods on SyncPromptContextStringcontent = context.readFile("pom.xml");
context.writeFile("output.txt", "Hello!");
returnPromptResponse.endTurn();
})
.build();
agent.run();

Client - registering file handlers:

AcpSyncClientclient = AcpClient.sync(transport)
.readTextFileHandler((ReadTextFileRequestreq) -> {
// Handlers receive typed requests directlyStringcontent = Files.readString(Path.of(req.path()));
returnnewReadTextFileResponse(content);
})
.writeTextFileHandler((WriteTextFileRequestreq) -> {
Files.writeString(Path.of(req.path()), req.content());
returnnewWriteTextFileResponse();
})
.build();

7. Capability Negotiation

Check what features the peer supports before using them (tutorial):

// Client: check agent capabilities after initializeclient.initialize(newInitializeRequest(1, clientCaps));
NegotiatedCapabilitiesagentCaps = client.getAgentCapabilities();
if (agentCaps.supportsLoadSession()) {
// Agent supports session persistence
}
if (agentCaps.supportsImageContent()) {
// Agent can handle image content in prompts
}
// Agent: check client capabilities before requesting operationsNegotiatedCapabilitiesclientCaps = agent.getClientCapabilities();
if (clientCaps.supportsReadTextFile()) {
agent.readTextFile(...);
} else {
// Client doesn't support file reading - handle gracefully
}
// Or use require methods (throws AcpCapabilityException if not supported)clientCaps.requireWriteTextFile();
agent.writeTextFile(...);

8. Error Handling

Handle protocol errors with structured exceptions (tutorial):

importcom.agentclientprotocol.sdk.error.*;
try {
client.prompt(request);
} catch (AcpProtocolExceptione) {
if (e.isConcurrentPrompt()) {
// Another prompt is already in progress
} elseif (e.isMethodNotFound()) {
// Agent doesn't support this method
}
System.err.println("Error " + e.getCode() + ": " + e.getMessage());
} catch (AcpCapabilityExceptione) {
// Tried to use a capability the peer doesn't supportSystem.err.println("Capability not supported: " + e.getCapability());
} catch (AcpConnectionExceptione) {
// Transport-level connection error
}

9. WebSocket Transport

Use WebSocket instead of stdio for network-based communication:

Client (JDK-native, no extra dependencies):

importcom.agentclientprotocol.sdk.client.transport.WebSocketAcpClientTransport;
importjava.net.URI;
vartransport = newWebSocketAcpClientTransport(
URI.create("ws://localhost:8080/acp"),
AcpJsonMapper.createDefault()
);
AcpSyncClientclient = AcpClient.sync(transport).build();

Agent (requires acp-websocket-jetty module):

importcom.agentclientprotocol.sdk.agent.transport.WebSocketAcpAgentTransport;
vartransport = newWebSocketAcpAgentTransport(
8080, // port"/acp", // pathAcpJsonMapper.createDefault()
);
AcpAsyncAgentagent = AcpAgent.async(transport)
// ... handlers ...
.build();
agent.start().block(); // Starts WebSocket server on port 8080

API Reference

Packages

PackageDescription
com.agentclientprotocol.sdk.specProtocol types (AcpSchema.*)
com.agentclientprotocol.sdk.clientClient SDK (AcpClient, AcpAsyncClient, AcpSyncClient)
com.agentclientprotocol.sdk.agentAgent SDK (AcpAgent, AcpAsyncAgent, AcpSyncAgent)
com.agentclientprotocol.sdk.agent.supportAnnotation-based agent runtime (AcpAgentSupport)
com.agentclientprotocol.sdk.annotationAgent annotations (@AcpAgent, @Prompt, etc.)
com.agentclientprotocol.sdk.capabilitiesCapability negotiation (NegotiatedCapabilities)
com.agentclientprotocol.sdk.errorExceptions (AcpProtocolException, AcpCapabilityException)

Maven Artifacts

ArtifactDescription
acp-coreClient and Agent SDKs, stdio and WebSocket client transports
acp-annotations@AcpAgent, @Prompt, and other annotations
acp-agent-supportAnnotation-based agent runtime
acp-testIn-memory transport and mock utilities for testing
acp-websocket-jettyJetty-based WebSocket server transport for agents

Transports

TransportClientAgentModule
StdioStdioAcpClientTransportStdioAcpAgentTransportacp-core
WebSocketWebSocketAcpClientTransportWebSocketAcpAgentTransportacp-core / acp-websocket-jetty

Building

./mvnw compile # Compile
./mvnw test# Run tests
./mvnw verify # Run unit tests + integration tests
./mvnw install # Install to local Maven repository

Integration Tests

Integration tests connect to real ACP agents and require additional setup:

# Gemini CLI integration tests (requires API key and gemini CLI)export GEMINI_API_KEY=your_key_here
./mvnw verify -pl acp-core

Test Categories:

TypeCommandCountRequirements
Unit tests./mvnw test370+None
Clean shutdown IT./mvnw verify4None
Gemini CLI IT./mvnw verify5GEMINI_API_KEY, gemini CLI in PATH

Testing Your Code

Use the mock utilities for testing:

importcom.agentclientprotocol.sdk.test.*;
// Create in-memory transport pair for testingInMemoryTransportPairpair = InMemoryTransportPair.create();
// Use pair.clientTransport() for client, pair.agentTransport() for agentMockAcpClientmockClient = MockAcpClient.builder(pair.clientTransport())
.fileContent("/test.txt", "test content")
.build();

Tutorial

For a hands-on, progressive introduction to the SDK, see the ACP Java Tutorial -- 30 modules covering client basics, agent development, streaming, testing, and IDE integration.

ACP Ecosystem

This SDK is part of the Agent Client Protocol ecosystem.

Other ACP SDKs:Kotlin | Python | TypeScript | Rust

Editor ACP docs:Zed | JetBrains | VS Code

ACP directories:Agents | Clients | Protocol spec

Versioning

This SDK tracks the ACP protocol, which is evolving. We don't promise strict semver — when the protocol changes, the SDK changes. What we do commit to:

  • Every breaking change is documented in release notes. Nothing breaks silently.
  • Deprecate before remove where feasible.
  • @UnstableAcpApi marks protocol elements from schema.unstable.json — the most likely to change. But even stable APIs may change when the protocol requires it.

If you need a stable target, pin to an exact version.

Releases

0.14.0 (Current — Maven Central)

Protocol currency: catching up to ACP spec v0.13.6 (June 2026). Supersedes the never-published 0.13.0.

New stable methods: logout, session/delete; the session config-option API (session/set_config_option, session/set_mode) is promoted to stable.

New unstable methods (marked @UnstableAcpApi): provider configuration — providers/list, providers/set, providers/disable.

Also: additionalDirectories workspace roots on session requests; per-chunk messageId on streamed chunks with sendMessage/sendThought overloads. The session-model API (session/set_model) is deprecated for removal — use session/set_config_option with a "model" category instead. Jackson aligned to 2.21.2; WebSocket max message size raised to 4 MB.

0.12.0 (Maven Central)

New stable methods: session/list, session/close, session/resume (tutorial)

New unstable methods (marked @UnstableAcpApi): elicitation/create, elicitation/complete (tutorial), session/fork, session/set_config_option

New @UnstableAcpApi annotation for protocol stability signaling (CLASS retention, aligns with JDK @PreviewFeature)

0.11.0

  • Client and Agent SDKs with async/sync APIs
  • Stdio and WebSocket transports
  • Capability negotiation, structured error handling
  • Full protocol compliance (all SessionUpdate types, MCP configs, _meta extensibility)

About

No description, website, or topics provided.

Resources

Stars

62 stars

Watchers

0 watching

Forks

Releases

Packages

Used by

Contributors

Languages