Building Adapters

This guide walks through building a complete custom channel adapter from scratch.


What Is a ChannelAdapter?

A ChannelAdapter is the bridge between OpenACP's core and a specific messaging platform. It has two responsibilities:

  • Inbound: receive messages from the platform and call core.handleIncomingMessage().

  • Outbound: implement methods that core calls to deliver agent output back to the platform.

OpenACP provides an abstract base class ChannelAdapter<TCore> with default no-op implementations for optional methods. You extend it and implement the required abstracts.


Message Flow

Platform user sends message

  YourAdapter (platform SDK listener)

  core.handleIncomingMessage(IncomingMessage)

  OpenACPCore → Session → AgentInstance (ACP subprocess)

  AgentEvents emitted

  core calls adapter.sendMessage() / sendPermissionRequest() / sendNotification()

  YourAdapter delivers to platform

The adapter is always the outermost layer. Core never talks to the platform directly.


Step 1 — Extend ChannelAdapter

The generic parameter TCore types this.core. Use OpenACPCore for full type safety.


Step 2 — Implement start() and stop()

start() initializes your platform SDK, registers listeners, and begins receiving messages. stop() tears everything down cleanly.


Step 3 — Handle Inbound Messages

When the platform delivers a user message, convert it to IncomingMessage and call core.handleIncomingMessage():

handleIncomingMessage looks up or creates a session for the (channelId, threadId, userId) combination and enqueues the prompt.


Step 4 — Implement sendMessage()

Core calls sendMessage() for every agent output event (text chunks, tool calls, usage summaries, errors, etc.).

OutgoingMessage.type can be: text, thought, tool_call, tool_update, plan, usage, session_end, error, attachment, system_message. You decide which types to surface in your UI.


Step 5 — Implement sendPermissionRequest()

When an agent needs user approval before taking an action, core calls sendPermissionRequest(). You must render the options and eventually call core.resolvePermission().


Step 6 — Implement sendNotification()

Notifications are summary alerts (session completed, error, budget warning). They are typically sent to a dedicated notification channel or thread.


Step 7 — Implement Session Thread Lifecycle

Core manages sessions and expects the adapter to maintain corresponding UI threads (channels, topics, threads):

deleteSessionThread and archiveSessionTopic are optional — the base class provides no-op defaults.


Step 8 — Register With Core

Before calling core.start(), register your adapter:


Step 9 — Export as AdapterFactory

So your adapter can be loaded as a plugin:


Complete Minimal Adapter

Last updated

Was this helpful?