ACP Java Tutorial

June 9, 2026 · View on GitHub

Documentation: https://springaicommunity.mintlify.app/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.

  1. Module 12 — Echo Agent — the whole shape of an ACP agent in ~25 lines. No AI, no API key.
  2. 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.
  3. 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.
  4. Make it real: 14 sending updates · 15 files & permissions · 18 terminal · 31 elicitation.
  5. Ship it: 23 Spring Boot agent with @AcpAgent.

Prerequisites

  • Java 17+
  • Maven 3.8+ (or use the included ./mvnw wrapper)
  • For the chatbot (module 25): an ANTHROPIC_API_KEY — this key is actually used. Get one at https://console.anthropic.com/ and export ANTHROPIC_API_KEY=....
  • For the client modules (01–08, 21): an ACP-capable agent CLI on your PATH (e.g. gemini --experimental-acp, or claude-code-acp / codex-acp). ACP is model-agnostic — point them at any agentic CLI. The tutorial code never reads an API key; the CLI you launch handles its own model and authentication.

Getting started

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 (e.g. gemini)
./mvnw exec:java -pl module-01-first-contact

Module map

🤖 = 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.

Build an agent — the core path

ModuleTitleWhat you'll learn
12Echo AgentA minimal ACP agent (~25 lines) — the reveal
25🤖 AI Chatbot AgentThe same agent, but the prompt handler calls Claude and streams
14Sending UpdatesStream all update types to clients
15Agent RequestsRequest files / permissions from the client
18Terminal OperationsExecute commands via the terminal API
31ElicitationAsk the user for structured input (forms)
23Spring Boot AgentShip 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 — LangChain4j ChatModel. The ACP agent never changes — only the line that talks to the model does.

Run it in your IDE

ModuleTitleWhat you'll learn
29Run it in your IDEPlug your agent into JetBrains, Zed, or VS Code — same JAR, different config

Be a client — talk to an existing agent (no API key in the tutorial code)

ModuleTitleWhat you'll learn
01🤖 First ContactLaunch an agent CLI, get your first response
02🤖 Protocol BasicsThe initialize handshake and capability exchange
03🤖 SessionsSession creation and lifecycle
04🤖 PromptsPrompt requests and response handling
05🤖 Streaming UpdatesReceive real-time updates during prompts
06🤖 Update TypesAll SessionUpdate types in depth
07🤖 Agent RequestsRespond to file read/write requests
08🤖 PermissionsHandle permission requests from agents

Production & advanced

ModuleTitleWhat you'll learn
09Session ResumeLoad and resume existing sessions
10CancellationCancel in-progress operations
11Error HandlingHandle protocol errors
16In-Memory TestingTest client + agent without subprocesses
17Capability NegotiationAdvertise and check capabilities
19MCP ServersPass MCP server configs to agents
20Session ManagementList, resume, and close sessions
21🤖 Async ClientReactive client with Mono
22Async AgentBuild agents with AcpAgent.async()
24Spring Boot ClientAutoconfigured AcpSyncClient

Error handling in handlers

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.

Build commands

./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 tests

Integration testing

The 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