Agent Client Protocol
Agent Client Protocol (ACP) is a standardized protocol that enables client applications to communicate with AI agents through a consistent, bidirectional interface.
ACP provides a structured way for agents to interact with clients, supporting real-time event streaming, tool call notifications, and session lifecycle management.
The Koog framework provides integration with ACP, enabling you to build ACP-compliant agents that can communicate with standardized client applications.
To learn more about the protocol, see the Agent Client Protocol documentation.
Integration with Koog
The Koog framework integrates with ACP using the ACP Kotlin SDK with additional API extensions in the agents-features-acp module.
This integration lets Koog agents perform the following:
- Communicate with ACP-compliant client applications
- Send real-time updates about agent execution (tool calls, thoughts, completions)
- Handle standard ACP events and notifications automatically
- Convert between Koog message formats and ACP content blocks
Key components
Here are the main components of the ACP integration in Koog:
| Component | Description |
|---|---|
AcpAgent |
The main feature that enables communication between Koog agents and ACP clients. |
MessageConverters |
Utilities for converting messages between Koog and ACP formats. |
AcpConfig |
Configuration class for the AcpAgent feature. |
Getting started
ACP dependencies are not included by default in the koog-agents meta-dependency.
You must explicitly add the ACP module to your project.
Dependencies
To use ACP in your project, add the following dependency:
1. Implement ACP agent support
Koog ACP integration is based on Kotlin ACP SDK.
The SDK provides an AgentSupport and AgentSession interface that you need to implement to implement in order to connect your agent to ACP clients.
The AgentSupport manages the agent sessions creation and loading. The interface implementation is almost the same for all agents, we'll provide an example implementation further.
The AgentSession manages the agent instantiation, invocation and controls runtime. Inside the prompt method you will define and run the Koog agent.
To use ACP with Koog, you need to implement the AgentSupport and AgentSession interfaces from the ACP SDK:
// Implement AgentSession to manage the lifecycle of a Koog 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 = myStrategy()
) {
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))
}
}
private fun myStrategy() = strategy<Unit, Unit>("") {
// Define your strategy here
}
override suspend fun cancel() {
agentJob?.cancel()
}
}
2. Configure the AcpAgent feature
The AcpAgent feature can be configured through AcpConfig:
val agent = AIAgent(
promptExecutor = promptExecutor,
agentConfig = agentConfig,
strategy = myStrategy()
) {
install(AcpAgent) {
// Required: The unique session identifier for the ACP connection
this.sessionId = sessionIdValue
// Required: The protocol instance used for sending requests and notifications
this.protocol = protocol
// Required: A coroutine-based producer scope for sending events
this.eventsProducer = this@channelFlow
// Optional: Whether to register default notification handlers (default: true)
this.setDefaultNotifications = true
}
}
Key configuration options:
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)
3. Handle incoming prompts
Convert ACP content blocks to Koog messages using the provided extension functions:
// Convert ACP content blocks to Koog message
val koogMessage = acpContent.toKoogMessage(clock)
// Append to existing prompt
fun Prompt.appendPrompt(content: List<ContentBlock>): Prompt {
return withMessages { messages ->
messages + listOf(content.toKoogMessage(clock))
}
}
Default notification handlers
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
Sending custom events
You can send custom events to the ACP client using the sendEvent method:
// Access the ACP feature and send custom events
withAcpAgent {
sendEvent(
Event.SessionUpdateEvent(
SessionUpdate.PlanUpdate(plan.entries)
)
)
}
Moreover, you can use protocol inside withAcpAgent and send custom notifications or requests:
// Access the ACP feature and send custom events
withAcpAgent {
protocol.sendRequest<AuthenticateRequest, AuthenticateResponse>(
AcpMethod.AgentMethods.Authenticate,
AuthenticateRequest(methodId = AuthMethodId("Google"))
)
}
Message conversion
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()
Important notes
Use channelFlow for event streaming
Use channelFlow to allow sending events from different coroutines:
override suspend fun prompt(
content: List<ContentBlock>,
_meta: JsonElement?
): Flow<Event> = channelFlow {
// Install AcpAgent with this@channelFlow as eventsProducer
}
Synchronize agent execution
Use a mutex to synchronize access to the agent instance, as the protocol should not trigger new execution until the previous one is finished:
Manual notification handling
If you need custom notification handling, set setDefaultNotifications = false and process all agent events according to the specification:
Platform support
The ACP feature is currently available only on the JVM platform, as it depends on the ACP Kotlin SDK which is JVM-specific.
Usage examples
Complete working examples can be found in the Koog repository.
Running the example
-
Run the ACP example application:
-
Enter a request for the ACP agent:
-
Observe the event traces in the console showing the agent's execution, tool calls, and completion status.