Java SDK for interacting with Claude Code CLI. This is a pure Java implementation that mirrors the design of the official Python and TypeScript Claude Agent SDKs.
| Feature | Description |
|---|---|
| Simple One-Shot API | Query.text() for quick answers in one line |
| Reactive API | ReactiveQuery with Mono/Flux for Spring WebFlux |
| Session API | Multi-turn conversations with context preservation |
| Hook System | Register callbacks for tool use events |
| MCP Integration | Support for Model Context Protocol servers |
| Resilience | Built-in retry and circuit breaker patterns |
- Java 17+
- Claude Code CLI installed and authenticated
- Maven 3.8+
Note: This project is currently available as a SNAPSHOT. Releases to Maven Central are coming soon.
Add the snapshot repository and dependency to your pom.xml:
<repositories>
<repository>
<id>central-snapshots</id>
<url>https://central.sonatype.com/repository/maven-snapshots/</url>
<snapshots>
<enabled>true</enabled>
</snapshots>
</repository>
</repositories>
<dependencies>
<dependency>
<groupId>org.springaicommunity</groupId>
<artifactId>claude-code-sdk</artifactId>
<version>1.0.0-SNAPSHOT</version>
</dependency>
</dependencies>Add the snapshot repository and dependency to your build.gradle:
repositories {
mavenCentral()
maven {
url 'https://central.sonatype.com/repository/maven-snapshots/'
}
}
dependencies {
implementation 'org.springaicommunity:claude-code-sdk:1.0.0-SNAPSHOT'
}git clone https://github.com/spring-ai-community/claude-agent-sdk-java.git
cd claude-agent-sdk-java
./mvnw installimport org.springaicommunity.claude.agent.sdk.Query;
String answer = Query.text("What is 2+2?");
System.out.println(answer); // "4"import org.springaicommunity.claude.agent.sdk.Query;
import org.springaicommunity.claude.agent.sdk.QueryOptions;
String answer = Query.text("Explain quantum computing",
QueryOptions.builder()
.model("claude-sonnet-4-5-20250929")
.systemPrompt("Be concise")
.build());import org.springaicommunity.claude.agent.sdk.Query;
import org.springaicommunity.claude.agent.sdk.types.QueryResult;
QueryResult result = Query.execute("Write a haiku about Java");
result.text().ifPresent(System.out::println);
System.out.println("Cost: $" + result.metadata().cost().calculateTotal());| API Style | Class | Returns | Best For |
|---|---|---|---|
| Blocking | Query |
String, QueryResult |
Simple scripts, CLI tools |
| Reactive | ReactiveQuery |
Mono<String>, Flux<Message> |
Spring WebFlux, async apps |
| Session | ClaudeSession |
Iterator<ParsedMessage> |
Multi-turn, hooks, MCP |
import org.springaicommunity.claude.agent.sdk.ReactiveQuery;
// Non-blocking with backpressure
ReactiveQuery.text("What is 2+2?")
.subscribe(System.out::println);
// Stream messages as they arrive
ReactiveQuery.query("Explain recursion")
.filter(msg -> msg instanceof AssistantMessage)
.subscribe(msg -> System.out.println(msg));try (ClaudeSession session = DefaultClaudeSession.builder()
.workingDirectory(Path.of("."))
.build()) {
session.connect("What is 2+2?");
Iterator<ParsedMessage> messages = session.receiveResponse();
while (messages.hasNext()) {
ParsedMessage msg = messages.next();
if (msg.isRegularMessage()) {
Message message = msg.asMessage();
if (message instanceof AssistantMessage assistant) {
assistant.getTextContent().ifPresent(System.out::println);
}
}
}
}try (ClaudeSession session = DefaultClaudeSession.builder()
.workingDirectory(Path.of("."))
.build()) {
// First turn
session.connect("My name is Alice.");
consumeResponse(session.receiveResponse());
// Second turn - context is preserved
session.query("What's my name?");
consumeResponse(session.receiveResponse());
}CLIOptions options = CLIOptions.builder()
.model("claude-sonnet-4-20250514") // Model selection
.permissionMode(PermissionMode.DEFAULT) // Permission handling
.systemPrompt("You are a helpful assistant") // System prompt
.appendSystemPrompt("Be concise") // Additional instructions
.maxTurns(10) // Limit conversation turns
.timeout(Duration.ofMinutes(5)) // Operation timeout
.allowedTools(List.of("Read", "Grep")) // Restrict tool access
.disallowedTools(List.of("Bash")) // Block specific tools
.build();Register callbacks for tool execution events:
DefaultClaudeSession session = DefaultClaudeSession.builder()
.workingDirectory(Path.of("."))
.build();
// Block dangerous operations
session.registerHook(HookEvent.PRE_TOOL_USE, "Bash", input -> {
if (input.toolInput().toString().contains("rm -rf")) {
return HookOutput.block("Dangerous command blocked");
}
return HookOutput.allow();
});
// Log all tool completions
session.registerHook(HookEvent.POST_TOOL_USE, null, input -> {
System.out.println("Tool completed: " + input.toolName());
return HookOutput.allow();
});Programmatic control over tool execution:
session.setToolPermissionCallback((toolName, input, context) -> {
if (toolName.equals("Write") && input.toString().contains("/etc/")) {
return PermissionResult.deny("Cannot write to system directories");
}
return PermissionResult.allow();
});claude-agent-sdk-java/
├── claude-code-sdk/ # Core SDK module
│ └── src/
│ ├── main/java/org/springaicommunity/claude/agent/sdk/
│ │ ├── session/ # ClaudeSession, DefaultClaudeSession
│ │ ├── transport/ # BidirectionalTransport, ReactiveTransport
│ │ ├── streaming/ # MessageReceiver, MessageStreamIterator
│ │ ├── hooks/ # HookRegistry, HookCallback
│ │ ├── permission/ # ToolPermissionCallback
│ │ ├── mcp/ # MCP server configuration
│ │ ├── types/ # Message types, content blocks
│ │ └── parsing/ # JSON parsing, control messages
│ └── test/
└── examples/
└── hello-world/ # Simple usage example
Apache License 2.0
Contributions are welcome! Please open an issue or submit a pull request.