ChannelPort interface from @comis/core, which defines messaging operations (send, receive, edit, delete, react). This guide walks you through building a complete adapter, using the EchoChannelAdapter as a reference implementation.
ChannelPort Interface
TheChannelPort interface defines the contract that every channel adapter must implement. It covers the full messaging lifecycle: connecting, receiving messages, sending responses, and disconnecting.
You do not need to implement every method fully. Methods like
editMessage, reactToMessage, fetchMessages, and platformAction can return err(new Error("Not supported")) if your platform does not support them. The system uses ChannelCapability metadata to know which features are available.File Structure
The canonical file structure for a channel adapter follows this layout:ChannelPort) and the plugin wrapper (implements ChannelPluginPort).
Tutorial: Building a Minimal Adapter
Implement ChannelPort
Create the adapter class that implements the
ChannelPort interface. The EchoChannelAdapter at packages/channels/src/echo/echo-adapter.ts (251 lines) is the simplest real example to reference.Normalize incoming messages
When your platform receives a message, convert it to a
Create a
NormalizedMessage before passing it to the handler. This is the standard message format that all of Comis understands.Key fields of NormalizedMessage:| Field | Type | Description |
|---|---|---|
id | string | Platform message ID |
text | string | Message text content |
channelId | string | Channel or chat ID |
senderId | string | User who sent the message |
senderName | string | Display name of the sender |
timestamp | number | When the message was sent (epoch ms) |
attachments | MessageAttachment[] | Array of file/media attachments |
isGroup | boolean | Whether this is a group chat |
replyTo | object | Message being replied to (optional) |
message-mapper.ts file to keep the normalization logic separate from the adapter:Define ChannelCapability metadata
The Set each feature flag honestly. The system trusts your declared capabilities to decide how to deliver messages. For example, if
ChannelCapability schema describes what your adapter supports. The system uses this metadata for feature negotiation — for example, deciding whether to attempt streaming or threading on your channel.streaming.supported is false, Comis will send complete messages instead of streaming edits.Create the ChannelPluginPort wrapper
Wrap your adapter in a
ChannelPluginPort for registration with the plugin system. The plugin provides metadata (ID, name, capabilities) and lifecycle hooks (activate, deactivate).Reference packages/channels/src/echo/echo-plugin.ts for the simplest real example:Register and test
The plugin is registered through the
PluginRegistry during bootstrap. The channel registry validates your declared capabilities against the ChannelCapabilitySchema at registration time.For testing, use the co-located test pattern. Create myplatform-adapter.test.ts alongside the adapter and test:- Lifecycle:
start()returnsok,stop()returnsok - Message sending:
sendMessage()returns a message ID - Message receiving: Call
onMessage()handler with a normalized message and verify it processes correctly - Unsupported operations: Methods that return
err()do so with descriptive messages
Reference: EchoChannelAdapter
TheEchoChannelAdapter is the canonical reference implementation. It is an in-memory adapter with no external dependencies, used extensively in integration tests. Located at packages/channels/src/echo/echo-adapter.ts (251 lines).
The Echo adapter implements every ChannelPort method by storing data in memory maps. It also provides test helper methods like injectMessage() (simulates incoming messages) and getSentMessages() (retrieves sent messages for assertions).
Study this adapter to see every ChannelPort method implemented correctly with the Result pattern.
Reference: Real Adapters
For more complex adapter implementations, reference these production adapters:- Telegram — The most complete adapter with 8 files. Webhook and polling modes, rich reactions, voice messages, platform actions (pin, kick, poll, ban). Located at
packages/channels/src/telegram/. - Discord — Rich feature support including buttons, embeds, reactions, threads, and guild management. Located at
packages/channels/src/discord/. - Slack — Block Kit integration with both Socket Mode and HTTP mode. Located at
packages/channels/src/slack/.
Related
Architecture
Port interfaces and hexagonal pattern
Plugins
General plugin system (hooks, tools, routes)
Event Bus
Channel events your adapter should emit
Channels Overview
User-facing channel documentation
