Documentation: https://lab.pollack.ai/docs/acp-java-sdk/tutorial | API Reference
A progressive, hands-on tutorial for the Agent Client Protocol (ACP) Java SDK.
ACP has two sides: the agent (what you build and ship) and the client (the IDE/editor that talks to it). The fastest way to get ACP is to build an agent and watch it grow up.
An ACP agent is just a few handlers:
initialize,newSession,prompt. The value is what you put in the prompt handler — echo, a real LLM call, or a curated domain workflow. The skeleton never changes; only that one method does.
- Module 12 — Echo Agent — the whole shape of an ACP agent in ~25 lines. No AI, no API key.
- Module 25 — AI Chatbot Agent — the same agent, but the prompt handler now calls Claude and streams a real answer. This is the first place in the tutorial where you can point at the line of Java that invokes the AI.
- Module 29 — Run it in your IDE — plug your agent into IntelliJ (or Zed / VS Code) and chat with it inside the editor. The same JAR works in all three — only the config differs.
- Make it real: 14 sending updates · 15 files & permissions · 18 terminal · 31 elicitation.
- Ship it: 23 Spring Boot agent with
@AcpAgent.
- Java 17+
- Maven 3.8+ (or use the included
./mvnwwrapper) - For the chatbot (module 25): an
ANTHROPIC_API_KEY— this key is actually used. Get one at https://console.anthropic.com/ andexport ANTHROPIC_API_KEY=.... - For the client modules (01–08, 21): an ACP-capable agent CLI on your
PATH. The modules launch the Grok CLI, which speaks ACP natively asgrok agent stdio: install it withcurl -fsSL https://x.ai/cli/install.sh | bashand sign in once withgrok login. ACP is model-agnostic — point the modules at any agentic CLI (e.g.claude-code-acp/codex-acp) by changing the launch command. The tutorial code never reads an API key; the CLI you launch handles its own model and authentication.
git clone https://github.com/markpollack/acp-java-tutorial.git
cd acp-java-tutorial
./mvnw compile# 1) Your first agent — echoes, runs entirely locally, no key
./mvnw package -pl module-12-echo-agent -q
./mvnw exec:java -pl module-12-echo-agent
# 2) The same agent with a brain — calls Claude (needs ANTHROPIC_API_KEY)
export ANTHROPIC_API_KEY=sk-ant-...
./mvnw package -pl module-25-ai-chatbot-agent -q
./mvnw exec:java -pl module-25-ai-chatbot-agent
# 3) Be the client — connect to an existing agent CLI (Grok)
./mvnw exec:java -pl module-01-first-contact🤖 = talks to a real AI. Unmarked modules teach the protocol with an echo agent — in ACP the AI is just one line in the prompt handler.
| Module | Title | What you'll learn |
|---|---|---|
| 12 | Echo Agent | A minimal ACP agent (~25 lines) — the reveal |
| 25 | 🤖 AI Chatbot Agent | The same agent, but the prompt handler calls Claude and streams |
| 14 | Sending Updates | Stream all update types to clients |
| 15 | Agent Requests | Request files / permissions from the client |
| 18 | Terminal Operations | Execute commands via the terminal API |
| 31 | Elicitation | Ask the user for structured input (forms) or to visit a URL (elicitation/complete) |
| 23 | Spring Boot Agent | Ship an agent with @AcpAgent (Java 21+) |
Portability — same chatbot, any provider: the module-25 agent rebuilt on the two top Java AI frameworks, so the model is a swap-a-dependency choice: 🤖 Module 26 — Spring AI
ChatClient· 🤖 Module 27 — LangChain4jChatModel. The ACP agent never changes — only the line that talks to the model does.
| Module | Title | What you'll learn |
|---|---|---|
| 29 | Run it in your IDE | Plug your agent into JetBrains, Zed, or VS Code — same JAR, different config |
| Module | Title | What you'll learn |
|---|---|---|
| 01 | 🤖 First Contact | Launch an agent CLI, get your first response |
| 02 | 🤖 Protocol Basics | The initialize handshake and capability exchange |
| 03 | 🤖 Sessions | Session creation and lifecycle |
| 04 | 🤖 Prompts | Prompt requests and response handling |
| 05 | 🤖 Streaming Updates | Receive real-time updates during prompts |
| 06 | 🤖 Update Types | All SessionUpdate types in depth |
| 07 | 🤖 Agent Requests | Respond to file read/write requests |
| 08 | 🤖 Permissions | Handle permission requests from agents |
| Module | Title | What you'll learn |
|---|---|---|
| 09 | Session Resume | Load and resume existing sessions |
| 10 | Cancellation | Cancel in-progress operations |
| 11 | Error Handling | Handle protocol errors |
| 16 | In-Memory Testing | Test client + agent without subprocesses |
| 17 | Capability Negotiation | Advertise and check capabilities |
| 19 | MCP Servers | Pass MCP server configs to agents |
| 20 | Session Management | List, resume, and close sessions |
| 21 | 🤖 Async Client | Reactive client with Mono |
| 22 | Async Agent | Build agents with AcpAgent.async() |
| 24 | Spring Boot Client | Autoconfigured AcpSyncClient |
Every module here runs a Java agent it builds itself, plus a client: no agent CLI, no API key.
| Module | Title | What you'll learn |
|---|---|---|
| 33 | Session Config Options | Model picker, capability-gated boolean, set_config_option, config_option_update, modes |
| 34 | Extension Methods | _-prefixed requests and notifications both ways, typed and raw, @ExtRequest/@ExtNotification |
| 35 | Cancellation and Timeouts | session/cancel, $/cancel_request and cancelWhen, the SDK's cancelGracePeriod and maxPromptDuration |
| 36 | Terminal Auth and Logout | AuthMethodAgent and AuthMethodTerminal, the logout capability, @Authenticate and @Logout |
| 37 | Streamable HTTP and WebSocket | A remote agent on the Jetty listener, one agent per connection, HTTP and WebSocket clients, h2c |
| 38 | Spring Boot over HTTP | spring.acp.agent.transport.type=http, an HTTP client app, AcpClientCustomizer and the fs capability properties (Java 21+) |
| 39 | Forward Compatibility and _meta | Unknown* variants kept, open values with equals, _meta on messages |
| 40 | Micronaut | acp-micronaut: a @Singleton @AcpAgent bean over stdio and HTTP/WebSocket, Micronaut clients from acp.client.*, derived initialize, an @Around interceptor, a Publisher return |
| 41 | Quarkus | acp-quarkus: @AcpAgent as a CDI bean on the Quarkus HTTP server (HTTP and WebSocket), Uni and cancellable Multi returns, a CDI interceptor, derived initialize (JVM mode) |
The Spring Boot starter is part of the SDK from 0.80.0: com.agentclientprotocol:acp-spring-boot-starter
replaces org.springaicommunity:acp-spring-boot-starter (modules 23, 24, 26 and 38; the spring.acp.*
properties are unchanged). Annotated agents need no @Initialize method any more: agentInfo and the
capabilities are derived from @AcpAgent and the handlers each agent declares.
When implementing file or permission handlers, throw exceptions for errors. The SDK automatically converts exceptions to proper JSON-RPC error responses.
// CORRECT: Throw exceptions - SDK converts to JSON-RPC errors
.readTextFileHandler(req -> {
if (!Files.exists(Path.of(req.path()))) {
throw new RuntimeException("File not found: " + req.path());
}
return new ReadTextFileResponse(Files.readString(Path.of(req.path())));
})Why throw exceptions? Errors belong in the JSON-RPC error field, not result;
agents get proper error codes; and it's consistent with the Kotlin and Python SDKs.
./mvnw compile # build everything
./mvnw compile -pl module-12-echo-agent # build one module
./mvnw package -pl module-25-ai-chatbot-agent -q # package an agent (before running)
./mvnw test # run testsThe tutorial includes an automated suite (deterministic output checks + an AI judge):
cd integration-testing
./scripts/run-integration-tests.sh --local # local tests, no keys
jbang RunIntegrationTest.java module-25-ai-chatbot-agent # needs ANTHROPIC_API_KEY
./scripts/run-integration-tests.sh # everything- ACP Java SDK — the SDK this tutorial teaches
- ACP Java SDK Documentation — full docs + API reference
- Agent Client Protocol — the official specification
- Other ACP SDKs: Kotlin | Python | TypeScript | Rust