Channels
追加バージョン: @mastra/core@1.22.0
Channels は Agent をメッセージングプラットフォームへ接続します。Agent コンストラクターの channels プロパティで設定します。渡すオブジェクトは ChannelConfig です。概念とプラットフォームの設定手順は、Channels の概要を参照してください。
使用例使用例への直接リンク
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:
slack、discord)をキーとするプラットフォーム Adapter。デフォルト設定には Adapter を直接渡し、Adapter ごとのオプションをカスタマイズするには ChannelAdapterConfig オブジェクトを渡します。handlers?:
inlineMedia?:
inlineLinks?:
tools?:
getTools() が Channel 固有の Tool(add_reaction、remove_reaction)を返すかどうか。Function calling に対応しないモデルでは false にします。Channel Tool が Agent に自動追加されることはないため、tools: { ...channels.getTools() } で明示的に渡してください。state?:
MastraStateAdapter です。Channels を使うには Storage の設定が必要です。userName?:
name で、名前が未設定の場合は 'Mastra' です。threadContext?:
maxMessages は、最初のメンション時に取得するプラットフォーム上の直近メッセージ数を制御します(無効にするには 0。DM 以外の Thread にのみ適用)。addSystemMessage: false を指定すると、リクエスト元の Channel/プラットフォームを Agent に伝える組み込みシステムメッセージを省略します。chatOptions?:
dedupeTtlMs、fallbackStreamingPlaceholderText、lockScope、messageHistory などの高度な設定に使用します。resolveResourceId?:
resourceId を決定します。新しい Thread の作成時にのみ実行されます。再利用される Thread は保存済みの所有者を維持し、この Hook を呼び出しません。組み込みの動作を維持するには ctx.defaultResourceId(${platform}:${message.author.userId})を返します。resolveThreadId?:
resolveResourceId の後に実行されます。新しい Thread の作成時にのみ実行され、再利用される Thread は保存済み ID を維持して Hook を呼び出しません。返す ID は Memory Store 全体で一意である必要があります。衝突時は生成された ID が代わりに使われます。組み込みの動作を維持するには ctx.defaultThreadId(ランダムな UUID)を返します。waitUntil?:
waitUntil 関数。Webhook が 200 を返した後もバックグラウンドの Agent Run を継続させるため、Vercel では必須です。Vercel では @vercel/functions の waitUntil を渡します。Cloudflare Workers と Netlify Functions はリクエストコンテキストから自動検出されます。AWS Lambda はイベントループが自然に空になるまで待つため、waitUntil は不要です。resolveWaitUntil?:
waitUntil が Hono のリクエストコンテキストにあり、組み込み Helper では対応できない Runtime 用の Resolver。解決順序は、単独の waitUntil → resolveWaitUntil(c) → デフォルト(Cloudflare Workers、Netlify)です。Adapter ごとのオプションAdapter ごとのオプションへの直接リンク
Adapter ごとのオプションを設定するには、Adapter を ChannelAdapterConfig オブジェクトでラップします。
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:
gateway?:
false にします。cards?:
toolDisplay を使用してください。toolDisplay が未設定の場合、cards: true は toolDisplay: "cards"、cards: false は toolDisplay: "text" に対応します。IDE ではこのフィールドに取り消し線が表示されますが、Runtime の動作は維持されます。cors?:
formatError?:
formatToolCall?:
toolDisplay を使用してください。設定すると、result/error イベントでのみ発火する ToolDisplayFn として動作し、running と approval イベントは表示されません。型レベルでは toolDisplay と相互排他的です。streaming?:
true、その他の Adapter は false です。textFormat?:
'markdown' は返信を Markdown として投稿します。Markdown をネイティブ表示できる Adapter(Slack)は直接表示し、それ以外は各プラットフォーム形式へ変換します。'plain' は返信をリテラルなプレーンテキストとして投稿し、Slack mrkdwn などのプラットフォーム方言を出力するよう指示された Agent で、Markdown 対応前の動作を復元します。最終返信テキストにのみ適用され、Tool Card、エラーメッセージ、Tripwire 通知には影響しません。ネイティブストリーミングでは、この設定にかかわらず常に Markdown を使います。toolDisplay?:
"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?:
true は組み込みのデフォルトを使います(テキストでは is typing…、Tool 呼び出しでは is calling {tool}…、Tool 呼び出し承認では is waiting for approval…)。false は入力中表示を完全に抑制します。ライブストリーミング Widget(例:Slack の toolDisplay: "grouped")がすでに進捗を示す場合に便利です。チャンクごとにカスタムステータス文を設定するには関数を渡します。ステータスを設定するには文字列、変更しない場合は false/null/undefined を返します。処理しないチャンクをデフォルトへフォールバックするには、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 に関数を渡します。関数は ToolDisplayEvent(running/result/error/approval)と 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 エントリには対話型ボタンを含められないためです。
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 チャンクごとに一度呼ばれます。ステータスを設定するには文字列を返し、現在のステータスを変更しない場合は false/null/undefined を返します。戻り値は重複排除されるため、プラットフォーム側で呼び出しが発生するのはステータスが変わったときだけです。
defaultTypingStatus は @mastra/core/channels から export されており、処理しないチャンクを組み込みのデフォルトへフォールバックできます。
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)
},
},
},
},
})
HandlerHandlerへの直接リンク
組み込み Event Handler を上書きします。各 Handler には次を指定できます。
- 省略:デフォルトの Mastra Handler を使います(メッセージを Agent へ送り、レスポンスを投稿)
false:Handler を完全に無効化します- 関数
(thread, message, defaultHandler) => Promise<void>:デフォルト処理をラップまたは置換します
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?:
onMention?:
onSubscribedMessage?:
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 を返します。
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:
slack、discord)。thread:
thread.isDM を使います。message:
message.author.userId は Actor/送信者であり、必ずしも Memory の所有者ではありません。defaultResourceId:
${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 を返します。
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:
slack、discord)。thread:
thread.isDM を使います。message:
resourceId:
resourceId(resolveResourceId の実行後)。defaultThreadId:
インラインメディアインラインメディアへの直接リンク
モデルへファイルパートとして送る添付ファイルの種類(画像、動画、PDF など)を制御します。一致しない種類はテキスト要約として説明されるため、未対応の種類を拒否するモデルを停止させずに、Agent へファイルの存在を伝えられます。
デフォルト(['image/png', 'image/jpeg', 'image/webp', 'application/pdf'])は、主要な Vision モデルが対応する形式です。inlineMedia を上書きしてリストを拡張する(例:['image/*', 'audio/*'])か、述語関数で完全に置き換えます。
対応する glob パターン:
| パターン | 一致対象 |
|---|---|
image/* | すべての画像タイプ(image/png、image/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)
関連項目関連項目への直接リンク
- Channels の概要:概念、クイックスタート、プラットフォーム設定
- Agent クラス:コンストラクターのパラメータとメソッド
- Chat SDK Adapter:Adapter の設定とプラットフォーム設定