채널
추가된 항목: @mastra/core@1.22.0
채널은 Agent를 메시징 플랫폼에 연결합니다. Agent 생성자의 channels 속성을 통해 구성하세요. 전달하는 객체는 ChannelConfig입니다. 개념 및 플랫폼 설정 지침은 채널 개요를 참조하세요.
사용예사용예에 대한 직접 링크
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를 직접 전달하고, 어댑터별 옵션을 사용자 지정하려면 ChannelAdapterConfig 객체를 전달하세요.handlers?:
inlineMedia?:
inlineLinks?:
tools?:
getTools()가 채널별 Tool(add_reaction, remove_reaction)을 반환할지 여부입니다. 함수 호출을 지원하지 않는 Model에서는 false로 설정하세요. 채널 Tool은 Agent에 자동으로 추가되지 않습니다. tools: { ...channels.getTools() }를 통해 명시적으로 전달하세요.state?:
MastraStateAdapter입니다. 채널을 사용하려면 스토리지를 구성해야 합니다.userName?:
name이며, 이름이 설정되지 않은 경우에는 'Mastra'입니다.threadContext?:
maxMessages는 첫 멘션에서 가져올 최근 플랫폼 메시지 수를 제어합니다(0으로 설정하면 비활성화되며 DM이 아닌 스레드에만 적용됨). addSystemMessage: false는 요청이 어느 채널/플랫폼에서 왔는지 Agent에 알려 주는 기본 제공 시스템 메시지를 생략합니다.chatOptions?:
dedupeTtlMs, fallbackStreamingPlaceholderText, lockScope, messageHistory 같은 고급 구성에 사용하세요.resolveResourceId?:
resourceId를 결정합니다. 새 스레드를 생성할 때만 실행되며, 재사용되는 스레드는 저장된 소유자를 유지하고 훅을 호출하지 않습니다. 기본 제공 동작을 유지하려면 ctx.defaultResourceId(${platform}:${message.author.userId})를 반환하세요.resolveThreadId?:
resolveResourceId 이후에 실행되며, 새 스레드를 생성할 때만 실행됩니다. 재사용되는 스레드는 저장된 ID를 유지하고 훅을 호출하지 않습니다. 반환된 ID는 Memory 저장소 전체에서 고유해야 하며, 충돌하면 대신 생성된 ID가 사용됩니다. 기본 제공 동작을 유지하려면 ctx.defaultThreadId(임의의 UUID)를 반환하세요.waitUntil?:
waitUntil 함수입니다. Vercel에서는 웹훅이 200을 반환한 후에도 백그라운드 Agent 실행을 유지하려면 필요합니다. Vercel에서는 @vercel/functions의 waitUntil을 전달하세요. Cloudflare Workers와 Netlify Functions는 요청 컨텍스트에서 자동으로 감지됩니다. AWS Lambda는 이벤트 루프가 자연스럽게 모두 처리될 때까지 기다리므로 waitUntil이 필요하지 않습니다.resolveWaitUntil?:
waitUntil이 Hono 요청 컨텍스트에 있지만 기본 제공 헬퍼가 지원하지 않는 런타임을 위한 확인 함수입니다. 확인 순서: 독립형 waitUntil → resolveWaitUntil(c) → 기본값(Cloudflare Workers, Netlify).어댑터별 옵션어댑터별 옵션에 대한 직접 링크
어댑터별 옵션을 설정하려면 어댑터를 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는 이 필드에 취소선을 표시하지만 런타임 동작은 유지됩니다.cors?:
formatError?:
formatToolCall?:
toolDisplay(함수 형식)를 사용하세요. 설정하면 result/error 이벤트에서만 실행되는 ToolDisplayFn으로 작동하며, running 및 approval 이벤트는 렌더링 없이 통과합니다. 형식 수준에서 toolDisplay와 상호 배타적입니다.streaming?:
true이고 다른 어댑터의 기본값은 false입니다.textFormat?:
'markdown'(기본값)은 답변을 Markdown으로 게시합니다. 네이티브 Markdown 렌더링을 지원하는 어댑터(Slack)는 직접 렌더링하고, 그 외 어댑터는 해당 플랫폼 형식으로 변환합니다. 'plain'은 답변을 리터럴 일반 텍스트로 게시하여 Slack mrkdwn 같은 플랫폼 문법을 출력하도록 Prompt가 제공된 Agent에 대해 Markdown 도입 이전의 동작을 복원합니다. 최종 답변 텍스트에만 적용되며 Tool 카드, 오류 메시지 및 트립와이어 알림에는 영향을 주지 않습니다. 네이티브 스트리밍은 이 설정과 관계없이 항상 Markdown입니다.toolDisplay?:
"cards"는 Tool별 실행/결과 카드를 리치 Block Kit으로 게시합니다. "text"는 동일한 수명 주기를 일반 텍스트로 게시합니다(Block Kit 미사용). "timeline"과 "grouped"는 Tool 상태를 인라인 task_update 청크로 스트리밍합니다(streaming: true가 필요하며 현재는 Slack만 지원합니다. 다른 어댑터에서는 자리표시자를 렌더링할 수 있습니다). "hidden"은 Tool을 표시하지 않고 실행합니다. Tool 이벤트를 직접 렌더링하려면 함수를 전달하세요. 개별 게시/수정에는 { kind: "post", message }, 스트리밍 위젯에 푸시하려면 { kind: "stream", chunk }, 해당 이벤트의 렌더링을 건너뛰려면 undefined를 반환합니다. 청크를 활성 스트리밍 세션에만 적용해야 한다면 스트림 결과에 openIfEmpty: false를 추가합니다. 승인/거부 Prompt는 모드와 관계없이 항상 별도의 카드로 렌더링됩니다.typingStatus?:
true는 기본 제공 기본값을 사용합니다(텍스트에서는 is typing…, Tool 호출에서는 is calling {tool}…, Tool 호출 승인에서는 is waiting for approval…). false는 입력 중 표시를 완전히 숨깁니다. Slack의 toolDisplay: "grouped"처럼 실시간 스트리밍 위젯이 이미 진행 상황을 전달할 때 유용합니다. 청크별 상태 문구를 맞춤 설정하려면 함수를 전달하세요. 상태를 설정하려면 문자열을 반환하고, 변경하지 않으려면 false/null/undefined를 반환합니다. 처리하지 않는 청크에 기본값을 적용하려면 defaultTypingStatus(@mastra/core/channels에서 내보냄)와 조합하세요.Tool 표시 모드Tool 표시 모드에 대한 직접 링크
toolDisplay는 채팅에서 Tool 호출이 렌더링되는 방식을 제어합니다. 기본값인 'cards'는
Tool마다 "Running…" 카드를 게시한 후 결과로 수정하며, 이전 버전의
동작과 일치합니다. 'text'는 수명 주기는 같지만 리치
Block Kit을 사용하지 않으므로 카드를 제대로 렌더링하지 못하는 플랫폼에 유용합니다.
'timeline'과 'grouped'는 Agent의 텍스트와 함께 Tool 상태를 인라인 task_update 청크로
스트리밍합니다. 이 모드에는 streaming: true가 필요하며 청크 렌더링은
채팅 어댑터에 의존합니다. Slack은 두 모드를 모두 기본 지원하지만, 다른
어댑터는 지원 기능을 추가할 때까지 자리표시자를 렌더링할 수 있습니다. streaming이
비활성화되어 있으면 채널은 경고를 한 번 기록하고 'cards'로 대체합니다.
'hidden'Tool을 자동으로 실행합니다. 입력 상태만 작업 중임을 나타냅니다.
진행.
함수를 전달하세요.toolDisplay 를 사용해 완전히 사용자 지정 렌더링할 수 있습니다. 이 함수는
다음을 받습니다: ToolDisplayEvent (running / result / error / approval)
and a ToolDisplayContext ({ mode, platform }); return { kind: 'post', message } for a discrete post/edit, { kind: 'stream', chunk }밀어 넣다
활성 스트리밍 위젯 또는undefined해당 이벤트 렌더링을 건너뜁니다.
기본적으로 스트림 결과는 활성 세션이 없을 때 스트리밍 세션을 엽니다. 청크를 기존 세션에만 적용해야 한다면
openIfEmpty: false를 설정하세요. 활성 세션이 없으면 Mastra는
청크를 건너뜁니다. 정적 채널은 이 옵션을 무시하고
기존의 일반 텍스트 대체 동작을 유지합니다.
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,
}
}
인라인 작업 항목에는 대화형 버튼을 포함할 수 없으므로 승인/거부 Prompt(requireApproval)는
모드와 관계없이 항상 별도의 카드로 렌더링됩니다.
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에 함수를 전달하세요. 이 함수는
스트림 청크마다 한 번 호출됩니다. 상태를 설정하려면 문자열을 반환하고, 현재 상태를
변경하지 않으려면 false / null / undefined를 반환합니다. 반환 값은
중복 제거되므로 상태가 변경될 때만 플랫폼에 호출이 전달됩니다.
처리하지 않는 청크에 기본 제공 기본값을 적용할 수 있도록
defaultTypingStatus는 @mastra/core/channels에서 내보냅니다.
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)
},
},
},
},
})
핸들러핸들러에 대한 직접 링크
내장된 이벤트 핸들러를 재정의합니다. 각 핸들러는 다음과 같습니다.
- 생략: 기본 Mastra 핸들러를 사용합니다(Agent를 통해 메시지를 라우팅하고 응답을 게시합니다).
false: 핸들러를 완전히 비활성화합니다.- 함수
(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 function signature:
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 인스턴스이므로 핸들러는 외부 접근자를 전달받지 않고도 스토리지나 등록된 다른 기본 구성 요소에 접근할 수 있습니다.
onDirectMessage: async (thread, message, defaultHandler, ctx) => {
const store = await ctx.mastra?.getStorage()?.getStore('memory')
await defaultHandler(thread, message)
}
ctx.requestContext는 이 메시지가 시작하려는 실행을 위한 RequestContext이며, 메시지마다 새로 생성됩니다. defaultHandler를 호출하기 전에 여기에 값을 쓰면 이후 Mastra가 추가하는 채널 항목과 함께 그 값이 실행에 전달됩니다.
onDirectMessage: async (thread, message, defaultHandler, ctx) => {
ctx.requestContext.set('locale', 'en-GB')
await defaultHandler(thread, message)
}
실행이 요청 컨텍스트에서 읽는 모든 내용은 플랫폼 발신자가 매핑되는 사용자와 같은 방식으로 메시지별로 결정될 수 있습니다.
리소스 ID 확인리소스 ID 확인에 대한 직접 링크
기본적으로 채널 스레드의 Memory resourceId는 ${platform}:${message.author.userId}입니다. 발신자가 플랫폼별 범위에서 Memory를 소유합니다. SSO(통합 인증)처럼 공유 ID를 사용하는 앱에서는 이로 인해 Memory가 분리됩니다. 같은 사용자가 Feishu DM에서는 feishu:user_123을, 웹에서는 user_123을 갖게 됩니다.
Memory 소유자를 발신자와 별도로 결정하려면 resolveResourceId를 전달하세요. 이 함수는 새 스레드가 생성될 때만 실행됩니다. 재사용되는 스레드는 저장된 resourceId를 유지하고 후크를 호출하지 않으므로, 기존 대화는 리졸버의 가용성에 의존하지 않습니다. 기본 제공 동작으로 대체하려면 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는 행위자/발신자이며, 반드시 Memory 소유자인 것은 아닙니다.defaultResourceId:
${platform}:${message.author.userId})입니다. 현재 동작을 유지하려면 이 값을 반환하세요.스레드 ID 확인스레드 ID 확인에 대한 직접 링크
기본적으로 새 채널 스레드는 내부 Mastra 스레드 ID로 임의의 UUID를 받습니다. ID를 직접 선택하려면 resolveThreadId를 전달하세요. 예를 들어 앱이 직접 생성하는 스레드의 명명 방식에 맞춰 스레드에 소속 세션과 같은 ID를 지정할 수 있습니다.
후크는 resolveResourceId 이후에 실행되므로 확인된 소유자를 컨텍스트에서 사용할 수 있습니다. resolveResourceId와 마찬가지로 새 스레드가 생성될 때만 실행됩니다. 재사용되는 스레드는 저장된 ID를 유지하며 후크를 호출하지 않습니다. 반환되는 ID는 Memory 저장소 전체에서 고유해야 합니다. 이미 기존 스레드에 속한 ID라면 기존 스레드를 덮어쓰지 않도록 Mastra가 경고를 기록하고 생성된 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:
인라인 미디어인라인 미디어에 대한 직접 링크
Model에 파일 부분으로 전송되는 첨부 파일 유형(이미지, 비디오, PDF 등)을 제어합니다. 일치하지 않는 유형은 텍스트 요약으로 설명되므로 Agent는 지원되지 않는 유형을 거부하는 Model을 충돌시키지 않고 파일에 대해 알 수 있습니다.
기본값(['image/png', 'image/jpeg', 'image/webp', 'application/pdf'])은 주요 비전 Model이 지원하는 형식과 일치합니다. 목록을 확장하려면(예: ['image/*', 'audio/*']) inlineMedia를 재정의하고, 목록 전체를 대체하려면 조건자 함수를 사용하세요.
지원되는 글로브 패턴:
| 패턴 | 일치 대상 |
|---|---|
image/* | 모든 이미지 유형(image/png, image/jpeg 등) |
video/* | 모든 동영상 유형 |
* or */* | 모든 유형 |
application/pdf | 정확히 일치하는 유형 |
| 비공개 CDN을 사용하는 플랫폼(예: Slack)의 경우 Chat SDK가 인증된 자격 증명을 사용하여 첨부 파일을 가져옵니다. 공개 CDN을 사용하는 플랫폼(예: Discord)의 경우 URL이 Model에 직접 전달됩니다. |
인라인 링크인라인 링크에 대한 직접 링크
Model이 원시 URL 텍스트를 보는 대신 연결된 콘텐츠를 처리할 수 있도록 메시지 텍스트에서 발견된 URL을 파일 부분으로 승격합니다. 각 항목은 문자열(도메인 패턴)이거나 강제 MIME 유형이 있는 객체일 수 있습니다.
문자열 항목은 도메인을 일치시키고 HEAD 요청을 수행해 Content-Type을 감지합니다. 확인된 유형을 inlineMedia와 대조하며, 일치하는 유형만 파일 파트가 됩니다.
객체 항목은 도메인을 일치시키고 특정 MIME 유형을 강제로 적용하여 HEAD 요청과 inlineMedia 검사를 건너뜁니다. HEAD 요청은 text/html을 반환하지만 Model은 URL을 동영상 콘텐츠로 처리하는 YouTube 같은 사이트에 유용합니다.
type InlineLinkEntry =
| string // Domain pattern (HEAD determines mime type)
| { match: string; mimeType: string } // Domain + forced mime type (skips HEAD)
관련된관련된에 대한 직접 링크
- 채널 개요: 개념, 빠른 시작 및 플랫폼 설정
- Agent 클래스: 생성자 매개변수 및 메소드
- Chat SDK 어댑터: 어댑터 구성 및 플랫폼 설정