Documentation Index
Fetch the complete documentation index at: https://docs.agentfront.dev/llms.txt
Use this file to discover all available pages before exploring further.
Overview
ChannelContext extends ExecutionContextBase and provides the foundation for all channel implementations. It handles event transformation via onEvent() and optional reply handling via onReply().
Class Hierarchy
ExecutionContextBase<ChannelNotification>
└── ChannelContext (abstract)
├── Your @Channel class
└── FunctionChannelContext (internal, for channel() builder)
Abstract Methods
onEvent
Transform an incoming event payload into a channel notification.
abstract onEvent(payload: unknown): Promise<ChannelNotification>;
| Parameter | Type | Description |
|---|
payload | unknown | Raw event payload from the source. Shape depends on source type. |
Returns: Promise<ChannelNotification> — the notification to push to Claude Code sessions.
Payload Shapes by Source
| Source | Payload Shape |
|---|
webhook | WebhookPayload { body, headers, method, query } |
app-event | Whatever was passed to eventBus.emit(event, payload) |
agent-completion | AgentCompletionEvent { agentId, agentName, status, durationMs, output, error } |
job-completion | JobCompletionEvent { jobName, jobId, status, durationMs, output, error, sessionId } |
service | Whatever pushIncoming(payload) receives from your connection listener |
file-watcher | Whatever pushIncoming(payload) receives from your file watcher |
manual | Whatever was passed to handleEvent(payload) |
Optional Methods
onReply
Handle a reply from Claude Code. Only called when the channel has twoWay: true.
async onReply(reply: string, meta?: Record<string, string>): Promise<void>
| Parameter | Type | Description |
|---|
reply | string | The reply text from Claude |
meta | Record<string, string> | Metadata from the channel-reply tool call (e.g., chat_id) |
Default implementation logs a warning. Override to forward replies to external systems.
Lifecycle Hooks (Service Connectors)
For channels with source: { type: 'service' } or source: { type: 'file-watcher' }, these lifecycle hooks manage persistent connections.
onConnect
Establish a persistent connection to an external service. Called during scope initialization after the notification service is wired.
async onConnect(): Promise<void>
Use this to set up WebSocket connections, API clients, polling loops, or file watchers. Call pushIncoming() inside event listeners to feed incoming events into the notification pipeline.
async onConnect(): Promise<void> {
this.client = new WhatsAppClient(process.env['WA_TOKEN']);
this.client.on('message', (msg) => {
this.pushIncoming({ sender: msg.from, text: msg.body, chatId: msg.chatId });
});
await this.client.connect();
}
onDisconnect
Tear down the persistent connection. Called during scope shutdown.
async onDisconnect(): Promise<void>
async onDisconnect(): Promise<void> {
await this.client?.disconnect();
this.watcher?.close();
}
pushIncoming
Push an incoming event from a service connection into the notification pipeline. Triggers onEvent() → notification push to subscribed sessions.
protected pushIncoming(payload: unknown): void
| Parameter | Type | Description |
|---|
payload | unknown | Raw event payload to process through onEvent() |
Only call pushIncoming() inside onConnect() event listeners. Calling it before onConnect() completes will log a warning since the handler is not yet wired.
Inherited Properties
From ExecutionContextBase:
| Property | Type | Description |
|---|
logger | FrontMcpLogger | Scoped logger for this channel |
metadata | ChannelMetadata | The channel’s decorator metadata |
channelName | string | The channel name (from metadata) |
Inherited Methods
From ExecutionContextBase:
| Method | Description |
|---|
get<T>(token) | Resolve a dependency from the DI container |
tryGet<T>(token) | Resolve or return undefined if not found |
scope | Access the parent scope |
fail(error) | Throw an MCP error |
Examples
Webhook Channel
@Channel({
name: 'monitoring',
source: { type: 'webhook', path: '/hooks/monitor' },
twoWay: true,
})
class MonitoringChannel extends ChannelContext {
async onEvent(payload: unknown): Promise<ChannelNotification> {
const { body } = payload as { body: { alert: string; severity: string } };
return { content: `Alert: ${body.alert}`, meta: { severity: body.severity } };
}
async onReply(reply: string, meta?: Record<string, string>): Promise<void> {
const messenger = this.get(MessengerServiceToken);
await messenger.send(meta?.chat_id, reply);
}
}
Service Connector (Full Lifecycle)
@Channel({
name: 'whatsapp',
source: { type: 'service', service: 'whatsapp-business' },
tools: [SendWhatsAppTool], // Claude calls this to send outbound messages
twoWay: true,
})
class WhatsAppChannel extends ChannelContext {
private client: WhatsAppClient;
async onConnect(): Promise<void> {
// Called during scope initialization
this.client = new WhatsAppClient(process.env['WA_TOKEN']);
this.client.on('message', (msg) => {
// Feed incoming messages into the notification pipeline
this.pushIncoming({ from: msg.sender, text: msg.body, chatId: msg.chatId });
});
await this.client.connect();
}
async onDisconnect(): Promise<void> {
// Called during scope teardown
await this.client.disconnect();
}
async onEvent(payload: unknown): Promise<ChannelNotification> {
// Transform incoming service event → notification for Claude
const msg = payload as { from: string; text: string; chatId: string };
return {
content: `${msg.from}: ${msg.text}`,
meta: { chat_id: msg.chatId, sender: msg.from },
};
}
async onReply(reply: string, meta?: Record<string, string>): Promise<void> {
// Forward Claude's reply back through the service
await this.client.sendMessage(meta?.chat_id, reply);
}
}