Skip to main content

Channels

Added in: @mastra/core@1.22.0

Channels connect agents to messaging platforms. Configure them via the channels property on the Agent constructor. See the Channels guide for concepts and platform setup instructions.

Usage example
Direct link to Usage example

src/mastra/agents/support-agent.ts
import { Agent } from '@mastra/core/agent'
import { createSlackAdapter } from '@chat-adapter/slack'
import { createDiscordAdapter } from '@chat-adapter/discord'

export const supportAgent = new Agent({
id: 'support-agent',
name: 'Support Agent',
instructions: 'You are a helpful support assistant.',
model: 'openai/gpt-5.4',
channels: {
adapters: {
slack: createSlackAdapter(),
discord: createDiscordAdapter(),
},
},
})

Parameters
Direct link to Parameters

adapters:

Record<string, Adapter | ChannelAdapterConfig>
Platform adapters keyed by name (e.g. `slack`, `discord`). Pass an `Adapter` directly for defaults, or a `ChannelAdapterConfig` object to customize per-adapter options.

handlers?:

ChannelHandlers
Override default message handlers for DMs, mentions, and subscribed threads.

inlineMedia?:

string[] | ((mimeType: string) => boolean)
= ['image/*']
Controls which attachment types are sent as file parts to the model. Types that do not match are described as text summaries. Accepts an array of mime type globs or a predicate function.

tools?:

boolean
= true
Include channel-specific tools (`add_reaction`, `remove_reaction`). Set to `false` for models that do not support function calling.

state?:

StateAdapter
= MastraStateAdapter (from Mastra storage)
State adapter for subscriptions and deduplication. Defaults to `MastraStateAdapter` backed by the Mastra instance storage. Channels require storage to be configured.

userName?:

string
= agent's `name`
Bot display name shown in platform messages. Defaults to the agent's `name`, or `'Mastra'` if no name is set.

threadContext?:

{ maxMessages?: number }
= { maxMessages: 10 }
Fetch recent messages from the platform when the agent is first mentioned in a thread. Set `maxMessages: 0` to disable. Only applies to non-DM threads.

chatOptions?:

Omit<ChatConfig, 'adapters' | 'state' | 'userName'>
Additional options passed directly to the [Chat SDK](https://chat-sdk.dev/docs/usage). Use for advanced configuration such as `dedupeTtlMs`, `fallbackStreamingPlaceholderText`, `lockScope`, and `messageHistory`.

Per-adapter options
Direct link to Per-adapter options

Wrap an adapter in a ChannelAdapterConfig object to set per-adapter options:

src/mastra/agents/example.ts
import { Agent } from '@mastra/core/agent'
import { createDiscordAdapter } from '@chat-adapter/discord'
import { createSlackAdapter } from '@chat-adapter/slack'

const agent = new Agent({
name: 'Example',
instructions: '...',
model: 'openai/gpt-5.4',
channels: {
adapters: {
discord: {
adapter: createDiscordAdapter(),
cards: false,
gateway: false,
},
slack: createSlackAdapter(), // Plain adapter uses defaults
},
},
})

adapter:

Adapter
The Chat SDK adapter instance for this platform.

gateway?:

boolean
= true
Start a persistent Gateway WebSocket listener for receiving DMs, @mentions, and reactions. Set to `false` for serverless deployments that only need webhook-based interactions.

cards?:

boolean
= true
Render tool calls as interactive rich cards with buttons. Set to `false` to use plain text formatting instead. When disabled and a tool requires approval, the agent uses `autoResumeSuspendedTools` to let the LLM decide based on conversation context.

formatToolCall?:

(info: { toolName, args, result, isError? }) => PostableMessage | null
Override how tool calls are rendered in the chat. Called once per tool invocation after the result is available. Return `null` to suppress the message entirely.

formatError?:

(error: Error) => PostableMessage
= "❌ Error: <error.message>"
Override how errors are rendered in the chat. Return a user-friendly message instead of exposing the raw error.

Handlers
Direct link to Handlers

Override built-in event handlers. Each handler can be:

  • Omitted: uses the default Mastra handler (routes to agent.stream and posts the response)
  • false: disables the handler entirely
  • A function (thread, message, defaultHandler) => Promise<void>: wraps or replaces the default
src/mastra/agents/custom-handlers.ts
import { Agent } from '@mastra/core/agent'
import { createSlackAdapter } from '@chat-adapter/slack'

const agent = new Agent({
name: 'Custom Handler Agent',
instructions: '...',
model: 'openai/gpt-5.4',
channels: {
adapters: {
slack: createSlackAdapter(),
},
handlers: {
onMention: async (thread, message, defaultHandler) => {
console.log('Received mention:', message.text)
await defaultHandler(thread, message)
},
onDirectMessage: false,
},
},
})

onDirectMessage?:

ChannelHandler | false
Called when the bot receives a direct message.

onMention?:

ChannelHandler | false
Called when the bot is @mentioned in a channel or thread.

onSubscribedMessage?:

ChannelHandler | false
Called for messages in threads the agent has subscribed to.

The ChannelHandler function signature:

type ChannelHandler = (
thread: Thread,
message: Message,
defaultHandler: (thread: Thread, message: Message) => Promise<void>,
) => Promise<void>

Inline media
Direct link to Inline media

Controls which attachment types (images, video, PDFs, etc.) are sent as file parts to the model. Types that do not match are described as text summaries so the agent knows about the file without crashing models that reject unsupported types.

Supported glob patterns:

PatternMatches
image/*All image types (image/png, image/jpeg, etc.)
video/*All video types
* or */*All types
application/pdfExact type match

For platforms with private CDNs (e.g. Slack), attachments are fetched with authenticated credentials from the Chat SDK. For platforms with public CDNs (e.g. Discord), the URL is passed directly to the model.

Promotes URLs found in message text to file parts so the model can process linked content instead of seeing raw URL text. Each entry can be a string (domain pattern) or an object with a forced mime type.

String entries match a domain and perform a HEAD request to detect the Content-Type. The resolved type is checked against inlineMedia and only matching types become file parts.

Object entries match a domain and force a specific mime type, skipping the HEAD request and bypassing the inlineMedia check. This is useful for sites like YouTube where a HEAD request returns text/html, but the model treats the URL as video content.

type InlineLinkEntry =
| string // Domain pattern (HEAD determines mime type)
| { match: string; mimeType: string } // Domain + forced mime type (skips HEAD)