メインコンテンツへ移動

Channels

追加バージョン: @mastra/core@1.22.0

Channels は Agent をメッセージングプラットフォームへ接続します。Agent コンストラクターの channels プロパティで設定します。渡すオブジェクトは ChannelConfig です。概念とプラットフォームの設定手順は、Channels の概要を参照してください。

使用例
使用例への直接リンク

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.6-sol',
channels: {
adapters: {
slack: createSlackAdapter(),
discord: createDiscordAdapter(),
},
},
})

パラメータ
パラメータへの直接リンク

channels プロパティは、次のフィールドを持つ ChannelConfig オブジェクトを受け取ります。

adapters:

Record<string, Adapter | ChannelAdapterConfig>
名前(例:slackdiscord)をキーとするプラットフォーム Adapter。デフォルト設定には Adapter を直接渡し、Adapter ごとのオプションをカスタマイズするには ChannelAdapterConfig オブジェクトを渡します。

handlers?:

ChannelHandlers
DM、メンション、購読中の Thread に対するデフォルトのメッセージ Handler を上書きします。

inlineMedia?:

string[] | ((mimeType: string) => boolean)
= ['image/png', 'image/jpeg', 'image/webp', 'application/pdf']
モデルへファイルパートとして送る添付ファイルの種類を制御します。一致しない種類はテキスト要約として説明されます。MIME タイプの glob 配列または述語関数を受け取ります。デフォルトは主要な Vision モデルが対応する形式です。

tools?:

boolean
= true
getTools() が Channel 固有の Tool(add_reactionremove_reaction)を返すかどうか。Function calling に対応しないモデルでは false にします。Channel Tool が Agent に自動追加されることはないため、tools: { ...channels.getTools() } で明示的に渡してください。

state?:

StateAdapter
= MastraStateAdapter (from Mastra storage)
購読と重複排除に使う State Adapter。デフォルトでは、Mastra インスタンスの Storage を利用する MastraStateAdapter です。Channels を使うには Storage の設定が必要です。

userName?:

string
= agent's `name`
プラットフォームのメッセージに表示する Bot 名。デフォルトは Agent の name で、名前が未設定の場合は 'Mastra' です。

threadContext?:

{ maxMessages?: number; addSystemMessage?: boolean }
= { maxMessages: 10, addSystemMessage: true }
Agent が現在の Thread のコンテキストを取得する方法。maxMessages は、最初のメンション時に取得するプラットフォーム上の直近メッセージ数を制御します(無効にするには 0。DM 以外の Thread にのみ適用)。addSystemMessage: false を指定すると、リクエスト元の Channel/プラットフォームを Agent に伝える組み込みシステムメッセージを省略します。

chatOptions?:

Omit<ChatConfig, 'adapters' | 'state' | 'userName'>
Chat SDK へ直接渡す追加オプション。dedupeTtlMsfallbackStreamingPlaceholderTextlockScopemessageHistory などの高度な設定に使用します。

resolveResourceId?:

(ctx: ResolveResourceIdContext) => string | Promise<string>
メッセージ送信者とは別に、Channel Thread のリソースレベル Memory を所有する resourceId を決定します。新しい Thread の作成時にのみ実行されます。再利用される Thread は保存済みの所有者を維持し、この Hook を呼び出しません。組み込みの動作を維持するには ctx.defaultResourceId${platform}:${message.author.userId})を返します。

resolveThreadId?:

(ctx: ResolveThreadIdContext) => string | Promise<string>
Channel Thread に使う Mastra 内部の Thread ID を決定します。解決済みの所有者をコンテキストに含め、resolveResourceId の後に実行されます。新しい Thread の作成時にのみ実行され、再利用される Thread は保存済み ID を維持して Hook を呼び出しません。返す ID は Memory Store 全体で一意である必要があります。衝突時は生成された ID が代わりに使われます。組み込みの動作を維持するには ctx.defaultThreadId(ランダムな UUID)を返します。

waitUntil?:

(promise: Promise<unknown>) => void
プラットフォームの waitUntil 関数。Webhook が 200 を返した後もバックグラウンドの Agent Run を継続させるため、Vercel では必須です。Vercel では @vercel/functionswaitUntil を渡します。Cloudflare Workers と Netlify Functions はリクエストコンテキストから自動検出されます。AWS Lambda はイベントループが自然に空になるまで待つため、waitUntil は不要です。

resolveWaitUntil?:

(c: Context) => ((promise: Promise<unknown>) => void) | undefined
waitUntil が Hono のリクエストコンテキストにあり、組み込み Helper では対応できない Runtime 用の Resolver。解決順序は、単独の waitUntilresolveWaitUntil(c) → デフォルト(Cloudflare Workers、Netlify)です。

Adapter ごとのオプション
Adapter ごとのオプションへの直接リンク

Adapter ごとのオプションを設定するには、Adapter を ChannelAdapterConfig オブジェクトでラップします。

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({
id: 'example',
name: 'Example',
instructions: '...',
model: 'openai/gpt-5.6-sol',
channels: {
adapters: {
discord: {
adapter: createDiscordAdapter(),
toolDisplay: 'text',
cors: {
origin: ['https://customer-saas.example'],
credentials: true,
},
gateway: false,
},
slack: createSlackAdapter(), // Plain adapter uses defaults
},
},
})

adapter:

Adapter
このプラットフォーム用の Chat SDK Adapter インスタンス。

gateway?:

boolean
= true
DM、@メンション、リアクションを受信する常駐 Gateway WebSocket Listener を起動します。Webhook ベースの対話だけが必要なサーバーレス環境では false にします。

cards?:

boolean
**非推奨** — 代わりに toolDisplay を使用してください。toolDisplay が未設定の場合、cards: truetoolDisplay: "cards"cards: falsetoolDisplay: "text" に対応します。IDE ではこのフィールドに取り消し線が表示されますが、Runtime の動作は維持されます。

cors?:

CorsOptions
この Adapter の Webhook Route に対する CORS 設定。オリジン間 Credential が必要なブラウザベースの Channel Adapter で使用します。

formatError?:

(error: Error) => PostableMessage
= "❌ Error: <error.message>"
チャットでのエラー表示方法を上書きします。生のエラーを公開せず、ユーザー向けのメッセージを返します。

formatToolCall?:

(args: { toolName: string; args: unknown; result: unknown; isError: boolean }) => PostableMessage | null
**非推奨** — 代わりに関数形式の toolDisplay を使用してください。設定すると、resulterror イベントでのみ発火する ToolDisplayFn として動作し、runningapproval イベントは表示されません。型レベルでは toolDisplay と相互排他的です。

streaming?:

boolean | { updateIntervalMs?: number }
= false (true for Slack)
Agent のテキスト差分をバッファリングして Step ごとに一度投稿する代わりに、生成中に Channel へストリーミングします。基盤となる Adapter が投稿と編集によるストリーミングに対応している必要があります。Slack のデフォルトは true、その他の Adapter は false です。

textFormat?:

'markdown' | 'plain'
= 'markdown'
Agent の最終返信テキストに使う方言。デフォルトの 'markdown' は返信を Markdown として投稿します。Markdown をネイティブ表示できる Adapter(Slack)は直接表示し、それ以外は各プラットフォーム形式へ変換します。'plain' は返信をリテラルなプレーンテキストとして投稿し、Slack mrkdwn などのプラットフォーム方言を出力するよう指示された Agent で、Markdown 対応前の動作を復元します。最終返信テキストにのみ適用され、Tool Card、エラーメッセージ、Tripwire 通知には影響しません。ネイティブストリーミングでは、この設定にかかわらず常に Markdown を使います。

toolDisplay?:

'cards' | 'text' | 'timeline' | 'grouped' | 'hidden' | ToolDisplayFn
= 'cards' ('grouped' for Slack)
Channel での Tool 呼び出しの表示方法。"cards" は Tool ごとの実行中/結果 Card をリッチな Block Kit として投稿します。"text" は同じライフサイクルをプレーンテキストで投稿します(Block Kit なし)。"timeline""grouped" は Tool の状態をインライン task_update チャンクとしてストリーミングします(streaming: true が必要。現在は Slack のみで、他の Adapter はプレースホルダーを表示する場合があります)。"hidden" は Tool を表示せずに実行します。Tool イベントを独自表示するには関数を渡します。個別の投稿/編集には { kind: "post", message }、ストリーミング Widget への送信には { kind: "stream", chunk } を返し、そのイベントを表示しない場合は undefined を返します。チャンクをアクティブなストリーミングセッションにのみ適用する場合は、Stream 結果に openIfEmpty: false を追加します。承認/拒否プロンプトは、モードにかかわらず常に別の Card として表示されます。

typingStatus?:

boolean | ((chunk: AgentChunkType, ctx: TypingStatusContext) => string | false | null | undefined | void)
= true
プラットフォームの入力中インジケーターを制御します。true は組み込みのデフォルトを使います(テキストでは is typing…、Tool 呼び出しでは is calling {tool}…、Tool 呼び出し承認では is waiting for approval…)。false は入力中表示を完全に抑制します。ライブストリーミング Widget(例:Slack の toolDisplay: "grouped")がすでに進捗を示す場合に便利です。チャンクごとにカスタムステータス文を設定するには関数を渡します。ステータスを設定するには文字列、変更しない場合は falsenullundefined を返します。処理しないチャンクをデフォルトへフォールバックするには、defaultTypingStatus@mastra/core/channels から export)と組み合わせます。

Tool 表示モード
Tool 表示モードへの直接リンク

toolDisplay は、チャットでの Tool 呼び出しの表示方法を制御します。デフォルトの 'cards' は Tool ごとに「Running…」Card を投稿し、結果で更新します。これは以前のバージョンと同じ動作です。'text' は同じライフサイクルを使いますが、リッチな Block Kit は使いません。Card を適切に表示できないプラットフォームで便利です。

'timeline''grouped' は、Agent のテキストとともに Tool の状態をインライン task_update チャンクとしてストリーミングします。これらのモードには streaming: true が必要で、チャンクの表示は Chat Adapter に依存します。Slack は両方をネイティブにサポートします。他の Adapter は対応するまでプレースホルダーを表示する場合があります。streaming が無効な場合、Channel は警告を一度記録して 'cards' へフォールバックします。

'hidden' は Tool を表示せずに実行します。処理中であることは入力中ステータスだけで示されます。

表示を完全にカスタマイズするには、toolDisplay に関数を渡します。関数は ToolDisplayEventrunningresulterrorapproval)と ToolDisplayContext{ mode, platform })を受け取ります。個別の投稿/編集には { kind: 'post', message } for a discrete post/edit, { kind: 'stream', chunk } を返し、 アクティブなストリーミング Widget へ送信します。そのイベントを表示しない場合は undefined を返します。

デフォルトでは、アクティブなセッションがない場合、Stream 結果がストリーミングセッションを開始します。チャンクを既存セッションにのみ適用する場合は openIfEmpty: false を設定します。アクティブなセッションがなければ、Mastra はチャンクをスキップします。静的 Channel はこのオプションを無視し、既存のプレーンテキストへのフォールバック動作を維持します。

toolDisplay: event => {
if (event.kind !== 'running') return undefined

return {
kind: 'stream',
chunk: {
type: 'task_update',
id: event.toolCallId,
title: event.displayName,
status: 'in_progress',
},
openIfEmpty: false,
}
}

承認/拒否プロンプト(requireApproval)は、モードにかかわらず常に別の Card として表示されます。インライン Task エントリには対話型ボタンを含められないためです。

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

const agent = new Agent({
id: 'streaming-agent',
name: 'Streaming Agent',
instructions: '...',
model: 'openai/gpt-5.6-sol',
channels: {
adapters: {
slack: {
adapter: createSlackAdapter(),
streaming: true, // already the Slack default
toolDisplay: 'timeline',
},
},
},
})

カスタム入力中ステータス
カスタム入力中ステータスへの直接リンク

ステータス文をカスタマイズするには、typingStatus に関数を渡します。関数は Stream チャンクごとに一度呼ばれます。ステータスを設定するには文字列を返し、現在のステータスを変更しない場合は falsenullundefined を返します。戻り値は重複排除されるため、プラットフォーム側で呼び出しが発生するのはステータスが変わったときだけです。

defaultTypingStatus@mastra/core/channels から export されており、処理しないチャンクを組み込みのデフォルトへフォールバックできます。

src/mastra/agents/custom-typing.ts
import { Agent } from '@mastra/core/agent'
import { defaultTypingStatus } from '@mastra/core/channels'
import { createDiscordAdapter } from '@chat-adapter/discord'

const agent = new Agent({
id: 'custom-typing-agent',
name: 'Custom Typing Agent',
instructions: '...',
model: 'openai/gpt-5.6-sol',
channels: {
adapters: {
discord: {
adapter: createDiscordAdapter(),
typingStatus: (chunk, ctx) => {
if (chunk.type === 'tool-call' && chunk.payload.toolName === 'searchDocs') {
return 'is searching docs…'
}
return defaultTypingStatus(chunk, ctx)
},
},
},
},
})

Handler
Handlerへの直接リンク

組み込み Event Handler を上書きします。各 Handler には次を指定できます。

  • 省略:デフォルトの Mastra Handler を使います(メッセージを Agent へ送り、レスポンスを投稿)
  • false:Handler を完全に無効化します
  • 関数 (thread, message, defaultHandler) => Promise<void>:デフォルト処理をラップまたは置換します
src/mastra/agents/custom-handlers.ts
import { Agent } from '@mastra/core/agent'
import { createSlackAdapter } from '@chat-adapter/slack'

const agent = new Agent({
id: 'custom-handler-agent',
name: 'Custom Handler Agent',
instructions: '...',
model: 'openai/gpt-5.6-sol',
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
Bot がダイレクトメッセージを受信したときに呼び出されます。

onMention?:

ChannelHandler | false
Channel または Thread で Bot が @メンションされたときに呼び出されます。

onSubscribedMessage?:

ChannelHandler | false
Agent が購読している Thread のメッセージに対して呼び出されます。

ChannelHandler 関数のシグネチャ:

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

type ChannelHandlerContext = {
mastra?: Mastra
requestContext: RequestContext
}

ctx.mastra は解決済みの mastra インスタンスです。そのため Handler は、外部 Accessor を渡されなくても Storage や他の登録済み Primitive へアクセスできます。

onDirectMessage: async (thread, message, defaultHandler, ctx) => {
const store = await ctx.mastra?.getStorage()?.getStore('memory')
await defaultHandler(thread, message)
}

ctx.requestContext は、このメッセージが開始する Run の RequestContext で、メッセージごとに新しく作成されます。defaultHandler を呼び出す前に値を書き込むと、後から Mastra が追加する Channel エントリとともに Run へ渡されます。

onDirectMessage: async (thread, message, defaultHandler, ctx) => {
ctx.requestContext.set('locale', 'en-GB')
await defaultHandler(thread, message)
}

この方法により、プラットフォームの送信者をどのユーザーへ対応付けるかなど、Run がリクエストコンテキストから読む値をメッセージごとに決定できます。

Resource ID の解決
Resource ID の解決への直接リンク

デフォルトでは、Channel Thread の Memory resourceId${platform}:${message.author.userId} です。送信者がプラットフォーム単位で Memory を所有します。シングルサインオン(SSO)のように共通 ID を使うアプリでは、これにより Memory が分割されます。同じユーザーでも、Feishu の DM では feishu:user_123、Web では user_123 になります。

送信者とは別に Memory の所有者を決めるには、resolveResourceId を渡します。新しい Thread の作成時にのみ実行されます。再利用される Thread は保存済みの resourceId を維持して Hook を呼び出さないため、既存の会話は Resolver の可用性に依存しません。組み込みの動作へフォールバックするには ctx.defaultResourceId を返します。

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

const agent = new Agent({
id: 'sso-agent',
name: 'SSO Agent',
instructions: '...',
model: 'openai/gpt-5.6-sol',
channels: {
adapters: {
slack: createSlackAdapter(),
},
resolveResourceId: async ({ thread, message }) => {
// DM: share resource-level memory with the web app by using the bare SSO id
if (thread.isDM) {
return await resolveSsoUserId(message)
}
// Group chat: the conversation owns the memory; the sender stays the actor
return thread.channelId
},
},
})

関数へ渡される ResolveResourceIdContext

platform:

string
プラットフォーム名(例:slackdiscord)。

thread:

Thread
メッセージを受信した Channel Thread。DM とグループ/Channel Thread の判別には thread.isDM を使います。

message:

Message
受信メッセージ。message.author.userId は Actor/送信者であり、必ずしも Memory の所有者ではありません。

defaultResourceId:

string
組み込みのデフォルト(${platform}:${message.author.userId})。現在の動作を維持するには、この値を返します。

Thread ID の解決
Thread ID の解決への直接リンク

デフォルトでは、新しい Channel Thread の Mastra 内部 Thread ID にランダムな UUID が使われます。ID を独自に決めるには resolveThreadId を渡します。たとえば、アプリ自身が作成する Thread の命名方法に合わせ、所属するセッションと同じ ID を Thread に設定できます。

この Hook は resolveResourceId の後に実行されるため、解決済みの所有者をコンテキストから利用できます。resolveResourceId と同様、新しい Thread の作成時にのみ実行されます。再利用される Thread は保存済み ID を維持し、Hook を呼び出しません。返す ID は Memory Store 全体で一意である必要があります。既存の Thread が同じ ID を使っている場合、Mastra は警告を記録し、既存 Thread を上書きしないよう生成した ID を代わりに使います。組み込みの動作を維持するには ctx.defaultThreadId を返します。

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

const agent = new Agent({
id: 'session-agent',
name: 'Session Agent',
instructions: '...',
model: 'openai/gpt-5.6-sol',
channels: {
adapters: {
slack: createSlackAdapter(),
},
// Owner: a session id resolved from the sender's linked account
resolveResourceId: async ctx => resolveSessionId(ctx),
// Thread id: align with the session id so app URLs that address
// threads by session id resolve channel-created threads too
resolveThreadId: ({ resourceId, defaultThreadId }) => {
return isSessionId(resourceId) ? resourceId : defaultThreadId
},
},
})

関数へ渡される ResolveThreadIdContext

platform:

string
プラットフォーム名(例:slackdiscord)。

thread:

Thread
メッセージを受信した Channel Thread。DM とグループ/Channel Thread の判別には thread.isDM を使います。

message:

Message
受信メッセージ。

resourceId:

string
新しい Thread が属する、解決済みの Memory resourceIdresolveResourceId の実行後)。

defaultThreadId:

string
組み込みのデフォルト(ランダムな UUID)。現在の動作を維持するには、この値を返します。

インラインメディア
インラインメディアへの直接リンク

モデルへファイルパートとして送る添付ファイルの種類(画像、動画、PDF など)を制御します。一致しない種類はテキスト要約として説明されるため、未対応の種類を拒否するモデルを停止させずに、Agent へファイルの存在を伝えられます。

デフォルト(['image/png', 'image/jpeg', 'image/webp', 'application/pdf'])は、主要な Vision モデルが対応する形式です。inlineMedia を上書きしてリストを拡張する(例:['image/*', 'audio/*'])か、述語関数で完全に置き換えます。

対応する glob パターン:

パターン一致対象
image/*すべての画像タイプ(image/pngimage/jpeg など)
video/*すべての動画タイプ
* または */*すべてのタイプ
application/pdf完全一致するタイプ

非公開 CDN を使うプラットフォーム(例:Slack)では、Chat SDK から認証済み Credential を使って添付ファイルを取得します。公開 CDN を使うプラットフォーム(例:Discord)では、URL をモデルへ直接渡します。

メッセージ本文内の URL をファイルパートへ昇格し、モデルが生の URL テキストではなくリンク先の内容を処理できるようにします。各エントリには文字列(ドメインパターン)、または MIME タイプを強制するオブジェクトを指定できます。

文字列エントリはドメインに一致し、HEAD リクエストで Content-Type を検出します。解決したタイプを inlineMedia と照合し、一致したタイプだけがファイルパートになります。

オブジェクトエントリはドメインに一致し、特定の MIME タイプを強制します。HEAD リクエストと inlineMedia の検査は省略されます。HEAD リクエストでは text/html が返るものの、モデルが URL を動画コンテンツとして扱う YouTube のようなサイトで便利です。

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