A Spring Boot compatible library that wraps Claude CLI as a Spring Bean, enabling programmatic control and automation of Claude AI's conversational interface within Spring applications.
- ✅ Spring Bean Integration: Easy usage through dependency injection
- ✅ Multiple Execution Modes: Support for synchronous, asynchronous, and streaming execution
- ✅ Session Management: Maintain conversation context with isolated multi-session support
- ✅ Tmux Integration: Advanced terminal control and background execution
- ✅ Security Policies: Command whitelisting/blacklisting and approval policies
- ✅ Full CLI Options Support: All Claude CLI flags available
- ✅ Spring Boot Auto-configuration: Instant setup with simple configuration
- ✅ Real-time Stream Processing: Process Claude responses in real-time
- Java 17 or higher
- Spring Boot 3.2.0 or higher
- Claude CLI installed on the system
- (Optional) tmux - for advanced session management features
<dependency>
<groupId>com.claudecli</groupId>
<artifactId>spring-claude-cli-adapter</artifactId>
<version>1.0.0-SNAPSHOT</version>
</dependency>
implementation 'com.claudecli:spring-claude-cli-adapter:1.0.0-SNAPSHOT'
@Service
@RequiredArgsConstructor
public class ClaudeService {
private final ClaudeCliWrapper claudeCli;
public void basicExample() {
// Simple query
ClaudeResponse response = claudeCli.execute("What is Spring Boot?");
System.out.println(response.getResponse());
}
}
public void advancedExample() {
ClaudeCliOptions options = ClaudeCliOptions.builder()
.model("claude-3-opus-20240229")
.maxTokens(2000)
.temperature(0.7)
.dangerouslySkipPermissions(true)
.workingDirectory("/my/project")
.build();
ClaudeResponse response = claudeCli.execute("Review this code", options);
}
public void streamExample() {
// Receive responses in real-time
claudeCli.executeStream("Write a long story",
line -> System.out.println("Claude: " + line));
}
public void sessionExample() {
// Create a session that maintains conversation context
ClaudeSession session = claudeCli.createSession("user-123");
ClaudeResponse resp1 = session.send("My name is John");
ClaudeResponse resp2 = session.send("What did I just tell you my name was?");
// Claude remembers the previous conversation
session.close();
}
public void asyncExample() {
CompletableFuture<ClaudeResponse> future =
claudeCli.executeAsync("Perform a complex task");
future.thenAccept(response -> {
System.out.println("Completed: " + response.getResponse());
});
}
claude:
cli:
# Path to Claude CLI executable
cli-path: claude
# Default model configuration
default-model: claude-3-opus-20240229
# API key (environment variable recommended)
api-key: ${CLAUDE_API_KEY}
# Auto-approve dangerous operations
dangerously-skip-permissions: false
# Working directory
working-directory: ${user.dir}
# Default parameters
default-max-tokens: 4096
default-temperature: 0.7
# Session configuration
session:
persist-history: true
persist-context: true
history-directory: /tmp/claude-history
context-directory: /tmp/claude-context
max-sessions-per-user: 10
# Security configuration
security:
enabled: true
require-approval-for-all-commands: false
log-all-commands: true
whitelisted-commands:
- ls
- pwd
- echo
- cat
- grep
- git status
- npm list
blacklisted-commands:
- rm -rf /
- sudo rm
- shutdown
whitelisted-paths:
- ${user.home}/Documents
- ${user.home}/Downloads
- /tmp
blacklisted-paths:
- /etc
- /sys
- /boot
# Tmux configuration
tmux:
enabled: true
default-session-prefix: claude-
auto-cleanup-on-shutdown: true
@Component
public class MySecurityPolicy implements CommandSecurityPolicy {
@Override
public boolean isCommandAllowed(String command) {
// Block dangerous commands
if (command.contains("rm -rf") || command.contains("format")) {
return false;
}
return true;
}
@Override
public ApprovalResult requiresApproval(CommandExecution cmd) {
// Sudo commands require user approval
if (cmd.getCommand().contains("sudo")) {
return ApprovalResult.REQUIRES_USER_APPROVAL;
}
// Auto-approve file writes
if (cmd.getCommand().startsWith("echo") && cmd.getCommand().contains(">")) {
return ApprovalResult.APPROVED;
}
return ApprovalResult.APPROVED;
}
@Override
public boolean isFileOperationAllowed(String filePath, FileOperation op) {
Path path = Paths.get(filePath).normalize().toAbsolutePath();
// Prevent writes to system directories
if (op == FileOperation.WRITE || op == FileOperation.DELETE) {
return !path.startsWith("/etc") && !path.startsWith("/sys");
}
return true;
}
}
@Service
@RequiredArgsConstructor
public class TmuxClaudeService {
private final ClaudeCliWrapper claudeCli;
private final TmuxSessionManager tmuxManager;
public void runInTmux() {
// Run Claude in a tmux session
ClaudeCliOptions options = ClaudeCliOptions.builder()
.executionMode(ExecutionMode.TMUX)
.tmuxOptions(TmuxOptions.builder()
.sessionName("claude-dev")
.windowName("ai-assistant")
.detached(true)
.logFile("/var/log/claude-session.log")
.build())
.build();
claudeCli.execute("Start the development server", options);
// Check session output later
String output = tmuxManager.capturePane("claude-dev");
System.out.println("Session output: " + output);
}
}
public void parallelExecution() {
List<String> prompts = Arrays.asList(
"Write Python code",
"Write Java code",
"Write JavaScript code"
);
List<CompletableFuture<ClaudeResponse>> futures = prompts.stream()
.map(prompt -> claudeCli.executeAsync(prompt))
.collect(Collectors.toList());
CompletableFuture.allOf(futures.toArray(new CompletableFuture[0]))
.thenRun(() -> {
futures.forEach(f -> {
try {
ClaudeResponse resp = f.get();
System.out.println(resp.getResponse());
} catch (Exception e) {
e.printStackTrace();
}
});
});
}
@RestController
@RequestMapping("/api/claude")
@RequiredArgsConstructor
public class ClaudeController {
private final ClaudeCliWrapper claudeCli;
@PostMapping("/chat")
public ClaudeResponse chat(@RequestBody ChatRequest request) {
return claudeCli.execute(request.getMessage());
}
@GetMapping(value = "/stream", produces = MediaType.TEXT_EVENT_STREAM_VALUE)
public Flux<String> stream(@RequestParam String prompt) {
return Flux.create(sink -> {
claudeCli.executeStream(prompt, sink::next);
sink.complete();
});
}
@PostMapping("/session/{userId}/message")
public ClaudeResponse sessionMessage(
@PathVariable String userId,
@RequestBody String message) {
ClaudeSession session = claudeCli.createSession(userId);
return session.send(message);
}
}
spring-claude-cli-adapter/
├── src/main/java/com/claudecli/adapter/
│ ├── core/ # Core interfaces and execution engine
│ ├── service/ # Service implementations
│ ├── model/ # Data models
│ ├── security/ # Security policies
│ ├── config/ # Spring Boot auto-configuration
│ └── util/ # Utilities
├── examples/ # Example applications
└── pom.xml
- Fork the repository
- Create your feature branch (
git checkout -b feature/amazing-feature
) - Commit your changes (
git commit -m 'Add some amazing feature'
) - Push to the branch (
git push origin feature/amazing-feature
) - Open a Pull Request
This project is distributed under the Apache License 2.0.
Q: How do I install Claude CLI? A: Please refer to the Claude CLI Installation Guide.
Q: Where can I get an API key? A: You can obtain an API key from the Anthropic Console.
Q: Can multiple users use this concurrently? A: Yes, the session management feature maintains independent conversation contexts for each user.
Q: Is this production-ready? A: Yes, with proper security policies configured and API usage monitoring in place.