A module provides integration with Agent Client Protocol (ACP). The main components of the ACP integration in Koog are:
- AcpAgent: A feature that enables communication between Koog agents and ACP-compliant client applications
- MessageConverters: Utilities for converting messages between Koog and ACP formats
The Agent Client Protocol (ACP) is a standardized protocol that enables AI agents to communicate with client applications through a consistent interface. ACP provides a bidirectional communication channel where agents can:
- Receive prompts from clients
- Send events and updates back to clients in real-time
- Report tool call status and progress
- Share reasoning and thoughts with clients
- Manage session lifecycle and state
To read more about ACP visit https://agentclientprotocol.com
Koog integrates with ACP using the ACP Kotlin SDK
with the additional API extensions presented in the agents-features-acp module.
To use ACP with Koog, you need to:
- Implement the
AgentSupportandAgentSessioninterface from the ACP SDK - In
AgentSession.promptmethod initialize Koog agent withAcpAgentfeature installed - Configure the feature with session ID, protocol instance, and events producer
- Handle incoming prompts and convert them to Koog messages
Here's a basic example of setting up an ACP-enabled agent:
class KoogAgentSession(
override val sessionId: SessionId,
private val promptExecutor: PromptExecutor,
private val protocol: Protocol,
private val clock: Clock,
) : AgentSession {
private var agentJob: Deferred<Unit>? = null
private val agentMutex = Mutex()
override suspend fun prompt(
content: List<ContentBlock>,
_meta: JsonElement?
): Flow<Event> = channelFlow {
val agentConfig = AIAgentConfig(
prompt = prompt("acp") {
system("You are a helpful assistant.")
}.appendPrompt(content),
model = OpenAIModels.Chat.GPT4o,
maxAgentIterations = 1000
)
agentMutex.withLock {
val agent = AIAgent(
promptExecutor = promptExecutor,
agentConfig = agentConfig,
strategy = yourStrategy(),
toolRegistry = toolRegistry,
) {
install(AcpAgent) {
this.sessionId = this@KoogAgentSession.sessionId.value
this.protocol = this@KoogAgentSession.protocol
this.eventsProducer = this@channelFlow
this.setDefaultNotifications = true
}
}
agentJob = async { agent.run(Unit) }
agentJob?.await()
}
}
private fun Prompt.appendPrompt(content: List<ContentBlock>): Prompt {
return withMessages { messages ->
messages + listOf(content.toKoogMessage(clock))
}
}
}Important notes:
- Use
channelFlowto allow sending events from different coroutines - Set
this.setDefaultNotifications = trueto automatically handle standard ACP notifications using agent pipeline interception. In case manual notification handling, please setthis.setDefaultNotifications = falseand process all the agents events according to the specification via protocolAcpAgentfeature. - To convert ACP content blocks to Koog messages use
toKoogMessageextension function and append received user message to the prompt. - Run the agent in a separate coroutine to allow canceling in
AgentSession.cancelmethod - Use
mutexto synchronize access to the agent instance, as by current protocol prompt should not trigger new execution until previous is finished
The AcpAgent feature can be configured through AcpConfig:
sessionId: The unique session identifier for the ACP connectionprotocol: The protocol instance used for sending requests and notifications to ACP clientseventsProducer: A coroutine-based producer scope for sending eventssetDefaultNotifications: Whether to register default notification handlers (default:true)
When setDefaultNotifications is enabled, the AcpAgent feature automatically handles:
- Agent Completion: Sends
PromptResponseEventwithStopReason.END_TURNwhen the agent completes successfully - Agent Execution Failures: Sends
PromptResponseEventwith appropriate stop reasons:StopReason.MAX_TURN_REQUESTSfor max iterations exceededStopReason.REFUSALfor other execution failures
- LLM Responses: Converts and sends LLM responses as ACP events (text, tool calls, reasoning)
- Tool Call Lifecycle: Reports tool call status changes:
ToolCallStatus.IN_PROGRESSwhen a tool call startsToolCallStatus.COMPLETEDwhen a tool call succeedsToolCallStatus.FAILEDwhen a tool call fails
You can send custom events to the ACP client using the sendEvent method:
val agent = AIAgent(...) {
install(AcpAgent) { ... }
}
// Later in your code, access the ACP feature
withAcpAgent {
sendEvent(
Event.SessionUpdateEvent(
SessionUpdate.PlanUpdate(planEntries)
)
)
}The module provides utilities for converting between Koog and ACP message formats:
ACP to Koog:
// Convert ACP content blocks to Koog message
val koogMessage = acpContentBlocks.toKoogMessage(clock)
// Convert single ACP content block to Koog content part
val contentPart = acpContentBlock.toKoogContentPart()Koog to ACP:
// Convert Koog response message to ACP events
val acpEvents = koogResponseMessage.toAcpEvents()
// Convert Koog content part to ACP content block
val acpContentBlock = koogContentPart.toAcpContentBlock()The ACP integration supports the following content types:
- Text: Plain text content
- Image: Image data with MIME type
- Audio: Audio data with MIME type
- File: File attachments (embedded or linked)
- Base64-encoded binary data
- Plain text data
- URL references
- Resource: Embedded resources with URI and content
- Resource Link: Links to external resources
The ACP feature is currently available only on the JVM platform, as it depends on the ACP Kotlin SDK which is JVM-specific.
Complete working examples can be found in examples/simple-examples/src/main/kotlin/ai/koog/agents/example/acp/.
How to run the example:
- Run the AcpApp.kt file
./gradlew :examples:simple-examples:run - Enter the request for ACP Agent
Move file `my-file.md` to folder `my-folder` and append title `## My File` to the file content- Check the events traces in the console