tA minimal Agent Client Protocol (ACP) JSON-RPC stdio server with AI integration for IntelliJ AI Assistant.
Good news! You can replace the custom JSON-RPC code with Koog's native ACP features, reducing code by 60% and getting better integration.
📖 Read the Quick Start Guide to migrate to Koog native features.
- QUICKSTART-KOOG.md - 3-step quick start guide
- KOOG-NATIVE-MIGRATION.md - Complete migration guide with all code examples
- KOOG-NATIVE-SUMMARY.md - Benefits and architecture comparison
| Current (Custom Java) | Koog Native (Kotlin) |
|---|---|
| 500 lines of code | 200 lines of code |
| Manual JSON-RPC | Automatic protocol handling |
| You maintain | JetBrains maintains |
| Basic ACP | Full Koog ecosystem |
Java can seamlessly call Kotlin code, so migration is straightforward!
The current implementation uses custom Java code with LangChain4j for AI.
- ✅ ACP-compliant JSON-RPC over stdio
- ✅ Handles
initialize,session/new, andsession/promptmethods - ✅ Emits
session/updatenotifications - ✅ LangChain4j integration for AI-powered responses
- ✅ Falls back to echo/template mode when AI is not configured
- ✅ Spring Boot autoconfiguration
For AI-powered responses, configure OpenAI:
export OPENAI_API_KEY=your-api-key-hereWithout an API key, the server operates in echo mode.
./gradlew :codeprompt:bootRunThe server reads JSON-RPC requests from stdin and writes responses to stdout.
Edit src/main/resources/application.properties:
# OpenAI Configuration (required for AI mode)
langchain4j.open-ai.chat-model.api-key=${OPENAI_API_KEY}
langchain4j.open-ai.chat-model.model-name=gpt-4o-mini
langchain4j.open-ai.chat-model.temperature=0.7
langchain4j.open-ai.chat-model.log-requests=true
langchain4j.open-ai.chat-model.log-responses=true
# Run LLM demo on startup (optional)
demo.llm.enabled=falseThe SimpleAssistant interface is automatically implemented by Spring Boot using LangChain4j:
@AiService
public interface SimpleAssistant {
@SystemMessage("You are a helpful coding assistant specialized in Java, Spring Boot, and enterprise applications.")
String chat(String userMessage);
}The JsonRpcHandler automatically uses the AI assistant when:
- The
SimpleAssistantbean is available (OPENAI_API_KEY is set) - A prompt is received via
session/prompt - No template is specified
Enable the startup demo to test AI integration:
# In application.properties
demo.llm.enabled=true
# Then run
./gradlew :codeprompt:bootRunAdd to ~/.jetbrains/acp.json:
{
"agent_servers": {
"Code Prompt Framework": {
"command": "/path/to/gradlew",
"args": [
"-p",
"/path/to/erp",
":codeprompt:bootRun",
"-q"
],
"env": [
{
"name": "OPENAI_API_KEY",
"value": "your-key-here"
}
]
}
}
}- Open the AI Chat tool window
- Click the agent selector dropdown
- Choose "Code Prompt Framework"
- Start chatting!
The project uses different LLM providers for local development and CI:
By default, tests use LM Studio running on localhost:1234:
# application.yaml (default configuration)
langchain4j:
open-ai:
chat-model:
base-url: http://127.0.0.1:1234/v1
model-name: ibm/granite-4-h-tinySetup LM Studio for local testing:
- Download and install LM Studio
- Download a compatible model (e.g.,
ibm/granite-4-h-tiny) - Start the local server on port 1234
- Run tests normally:
./gradlew test
CI uses GitHub Models via Azure AI:
# application-github.yaml
langchain4j:
open-ai:
chat-model:
base-url: https://models.github.ai/inference
model-name: gpt-5-miniGitHub Actions automatically:
- Activates the
githubprofile viaSPRING_PROFILES_ACTIVE=githubenvironment variable - Uses the
MODEL_TOKENsecret for authentication - No manual setup required
To test with GitHub models locally:
export MODEL_TOKEN=your-github-token
export SPRING_PROFILES_ACTIVE=github
./gradlew testThe framework uses Spring profiles to switch between configurations:
| Environment | Profile | Provider | Configuration |
|---|---|---|---|
| Local tests (default) | none | LM Studio | application.yaml |
| CI/GitHub Actions | github |
GitHub Models | application-github.yaml |
| Custom | testcontainers |
Ollama | application-testcontainers.yaml |
No code changes needed - just set the profile:
# Local with LM Studio (default)
./gradlew test
# Local with GitHub Models
export SPRING_PROFILES_ACTIVE=github
./gradlew test
# CI (automatically set via environment variable)
# SPRING_PROFILES_ACTIVE=github is set in ci.ymlFor CI to work, ensure the MODEL_TOKEN secret is set in your GitHub repository:
- Go to repository Settings → Secrets and variables → Actions
- Add a new secret named
MODEL_TOKEN - Set the value to your GitHub Models API token
{
"jsonrpc": "2.0",
"id": 1,
"method": "initialize"
}Response:
{
"jsonrpc": "2.0",
"id": 1,
"result": {
"serverInfo": {
"name": "code-prompt-framework",
"version": "0.1.0"
},
"capabilities": {
"session": {
"update": true
},
"prompt": {}
}
}
}{
"jsonrpc": "2.0",
"id": 2,
"method": "session/new"
}Response:
{
"jsonrpc": "2.0",
"method": "session/update",
"params": {
"sessionId": "...",
"text": "Session created"
}
}
{
"jsonrpc": "2.0",
"id": 2,
"result": {
"sessionId": "...",
"createdAt": "2026-02-14T..."
}
}{
"jsonrpc": "2.0",
"id": 3,
"method": "session/prompt",
"params": {
"sessionId": "abc123",
"prompt": "Explain Spring Boot dependency injection"
}
}Response (with AI):
{
"jsonrpc": "2.0",
"method": "session/update",
"params": {
"sessionId": "abc123",
"update": {
"sessionUpdate": "agent_message_chunk",
"content": {
"type": "text",
"text": "Spring Boot's dependency injection..."
}
}
}
}
{
"jsonrpc": "2.0",
"id": 3,
"result": {
"stopReason": "end_turn"
}
}{
"jsonrpc": "2.0",
"id": 4,
"method": "session/prompt",
"params": {
"template": "Hello {{name}}, welcome to {{place}}!",
"variables": {
"name": "Ada",
"place": "Wonderland"
}
}
}Response:
{
"jsonrpc": "2.0",
"method": "session/update",
"params": {
"sessionId": "...",
"update": {
"sessionUpdate": "agent_message_chunk",
"content": {
"type": "text",
"text": "Hello Ada, welcome to Wonderland!"
}
}
}
}
{
"jsonrpc": "2.0",
"id": 4,
"result": {
"stopReason": "end_turn"
}
}┌─────────────────────────────────────┐
│ IntelliJ AI Assistant (Client) │
└──────────────┬──────────────────────┘
│ JSON-RPC over stdio
▼
┌─────────────────────────────────────┐
│ StdioJsonRpcServer (Spring) │
│ ┌───────────────────────────────┐ │
│ │ JsonRpcHandler │ │
│ │ ┌─────────────────────────┐ │ │
│ │ │ SimpleAssistant (AI) │ │ │
│ │ │ ↓ │ │ │
│ │ │ LangChain4j │ │ │
│ │ │ ↓ │ │ │
│ │ │ OpenAI API │ │ │
│ │ └─────────────────────────┘ │ │
│ └───────────────────────────────┘ │
└─────────────────────────────────────┘
./gradlew :codeprompt:test./gradlew :codeprompt:buildUse the included demo script:
./codeprompt/demo-acp.shOr manually pipe commands:
echo '{"jsonrpc":"2.0","id":1,"method":"initialize"}' | ./gradlew :codeprompt:bootRun -q- Spring Boot 3.4.2 - Application framework
- LangChain4j 1.11.0 - AI/LLM integration
- LangChain4j Spring Boot Starter - Autoconfiguration
- LangChain4j OpenAI - OpenAI model integration
- Jackson - JSON processing
-
Verify
OPENAI_API_KEYis set:echo $OPENAI_API_KEY
-
Check logs for errors:
./gradlew :codeprompt:bootRun # Look for "Using LangChain4j assistant" messages -
Fallback behavior: If AI fails, server automatically falls back to echo mode
- Verify
acp.jsonpath and syntax - Restart IntelliJ IDE
- Check agent appears in AI Chat selector
Ensure Java 25 is available:
java -version # Should show Java 25+Part of the ERP project.
Initialize:
{
"jsonrpc": "2.0",
"id": 1,
"method": "initialize"
}Create session:
{
"jsonrpc": "2.0",
"id": 2,
"method": "session/new"
}Prompt (echo):
{
"jsonrpc": "2.0",
"id": 3,
"method": "session/prompt",
"params": {
"sessionId": "SESSION_ID",
"prompt": "Hello"
}
}Prompt (template):
{
"jsonrpc": "2.0",
"id": 4,
"method": "session/prompt",
"params": {
"template": "Hi {{name}}",
"variables": {
"name": "Ada"
}
}
}