UNPKG

@mastra/core

Version:
225 lines (162 loc) • 9.34 kB
> Discover all available pages from the documentation index: https://mastra.ai/llms.txt # Channels **Added in:** `@mastra/core@1.22.0` Channels connect your agents to messaging platforms like Slack, Discord, and Telegram. When a user sends a message on a platform, the agent receives it, processes it through the normal agent pipeline, and streams the response back to the conversation. ## When to use channels Use channels when you want your agent to: - Respond to messages in Slack workspaces, Discord servers, or Telegram chats - Handle both direct messages and mentions in group conversations ## Quickstart Configure channels directly on your agent using adapters from the [Chat SDK](https://chat-sdk.dev/adapters): ```typescript import { Agent } from '@mastra/core/agent' import { createSlackAdapter } from '@chat-adapter/slack' export const supportAgent = new Agent({ id: 'support-agent', name: 'Support Agent', instructions: 'You are a helpful support assistant.', model: 'openai/gpt-5.5', channels: { adapters: { slack: createSlackAdapter(), }, }, }) ``` Register the agent in your Mastra instance with storage so channel state persists across restarts: ```typescript import { Mastra } from '@mastra/core' import { LibSQLStore } from '@mastra/libsql' import { supportAgent } from './agents/support-agent' export const mastra = new Mastra({ agents: { supportAgent }, storage: new LibSQLStore({ url: process.env.DATABASE_URL, }), }) ``` Each adapter reads credentials from environment variables by default. ## Platform setup Each platform requires credentials and event configuration. See the Chat SDK adapter docs for full setup: [Slack](https://chat-sdk.dev/adapters/slack), [Discord](https://chat-sdk.dev/adapters/discord), [Telegram](https://chat-sdk.dev/adapters/telegram). Mastra generates a webhook route for each platform at: ```text /api/agents/{agentId}/channels/{platform}/webhook ``` For example: `/api/agents/support-agent/channels/slack/webhook` Point your platform's webhook or interactions URL to this path. ### Local development Platform webhooks need a public URL to reach your local server. Use a tunnel to expose `localhost:4111`: ```bash # ngrok ngrok http 4111 # cloudflared npx cloudflared tunnel --url http://localhost:4111 ``` Copy the generated URL and use it as the base for your webhook paths (e.g. `https://abc123.ngrok.io/api/agents/support-agent/channels/slack/webhook`). ## Thread context When a user mentions the agent mid-conversation in a channel thread, the agent may not have prior context. By default, Mastra fetches the last 10 messages from the platform on the first mention. 1. On the **first mention** in a thread, the agent fetches recent messages from the platform. 2. These messages are prepended to the user's message as conversation context. 3. After responding, the agent subscribes to the thread and has full history via Mastra's memory. 4. Subsequent messages in that thread **don't** re-fetch from the platform. Set `threadContext: { maxMessages: 0 }` to disable this behavior. This only applies to non-DM threads. Mastra also adds a short system message telling the agent which channel and platform the request came from (DM vs public channel, platform name, bot display name). Set `threadContext: { addSystemMessage: false }` to skip it. ## Tool approval Tools with `requireApproval: true` render as interactive cards with Approve and Deny buttons: ```typescript import { createTool } from '@mastra/core/tools' import { z } from 'zod' const deleteFile = createTool({ id: 'delete-file', description: 'Delete a file from the system', inputSchema: z.object({ path: z.string().describe('Path to the file to delete'), }), requireApproval: true, execute: async ({ path }) => { await fs.unlink(path) return { deleted: path } }, }) ``` When the agent calls this tool, users see a card with the tool name, arguments, and Approve/Deny buttons. The tool only executes after approval. Set `toolDisplay: 'text'` on an adapter to render tool calls as plain text instead of interactive cards. In `'hidden'` mode the agent uses `autoResumeSuspendedTools` to let the LLM decide based on the conversation context, since hidden mode doesn't post approval buttons. ## Multi-user awareness In group conversations, Mastra automatically prefixes each message with the sender's name and platform ID so the agent can distinguish between speakers: ```text [Alice (@U123ABC)]: Can you help me with this? [Bob (@U456DEF)]: I have a question too. ``` ## Multimodal content Models like Gemini can natively process images, video, and audio. Combine `inlineMedia` and `inlineLinks` to let users share rich content with your agent across platforms: ```typescript import { Agent } from '@mastra/core/agent' import { createDiscordAdapter } from '@chat-adapter/discord' import { google } from '@ai-sdk/google' export const visionAgent = new Agent({ id: 'vision-agent', name: 'Vision Agent', instructions: 'You can see images, watch videos, and listen to audio.', model: google('gemini-3.1-flash-image-preview'), channels: { adapters: { discord: createDiscordAdapter(), }, inlineMedia: ['image/*', 'video/*', 'audio/*'], inlineLinks: [ // Gemini treats YouTube URLs as native video file parts { match: 'youtube.com', mimeType: 'video/*' }, { match: 'youtu.be', mimeType: 'video/*' }, 'imgur.com', // HEAD-check imgur links; inline as file part if mimeType matches inlineMedia ], }, }) ``` With this configuration: - A user uploads a screenshot and the agent describes what it sees - A user uploads an `.mp4` clip and the agent summarizes the video - A user pastes a YouTube link and the agent watches and discusses the video - A user pastes an imgur link and the agent sees the image directly By default, only images are sent inline (`inlineMedia: ['image/*']`). Unsupported types are described as text summaries so the agent knows about the file without crashing models that reject them. > **Note:** See [Channels reference](https://mastra.ai/reference/agents/channels) for all `inlineMedia` patterns and [inlineLinks reference](https://mastra.ai/reference/agents/channels) for domain matching, HEAD detection, and forced mime types. ## Serverless deployment On serverless platforms like Vercel, each request runs in a separate, short-lived instance. Channels need two things to work reliably in that environment: a way to keep the function alive while the agent responds, and a shared pub/sub so instances can coordinate. ### Keep the function alive with `waitUntil` A channel webhook returns a `200` response right away, then the agent runs in the background to post its reply. On most serverless platforms the function is frozen as soon as it responds, which kills the run before the agent answers. Pass a `waitUntil` function so the platform keeps the instance alive until the run finishes. On Vercel, pass `waitUntil` from `@vercel/functions`: ```typescript import { Agent } from '@mastra/core/agent' import { createSlackAdapter } from '@chat-adapter/slack' import { waitUntil } from '@vercel/functions' export const agent = new Agent({ id: 'agent', channels: { adapters: { slack: createSlackAdapter(), }, waitUntil, }, }) ``` Vercel and AWS Lambda require `waitUntil`, since they freeze the function as soon as the response is sent. Cloudflare Workers and Netlify Functions are detected automatically from the request context, so they don't need it. For runtimes where `waitUntil` lives on the request context but isn't detected automatically, use `resolveWaitUntil`. See the [Channels reference](https://mastra.ai/reference/agents/channels) for details. ### Coordinate instances with a shared pub/sub Channels route messages through the agent's signal pipeline, and each run acquires a lease on its thread so a single run owns the conversation at a time. The default in-memory pub/sub can't cross instance boundaries, so on serverless a follow-up message can land on a different instance than the one running the agent. Without a shared pub/sub, that instance can't reach the active run and starts its own, leaving the original run untouched and the thread processed twice. Configure a shared pub/sub backed by Redis Streams on the `Mastra` instance so leases and signals coordinate across instances: ```typescript import { Mastra } from '@mastra/core' import { RedisStreamsPubSub } from '@mastra/redis-streams' export const mastra = new Mastra({ agents: { agent }, pubsub: new RedisStreamsPubSub({ url: process.env.REDIS_URL, keyPrefix: 'mastra:my-app', }), }) ``` Vercel's one-click Redis integration and Upstash Redis both work well. For more on when a distributed pub/sub is needed, see the [PubSub guide](https://mastra.ai/docs/server/pubsub) and the [`RedisStreamsPubSub` reference](https://mastra.ai/reference/pubsub/redis-streams). ## Related - [Guide: Building a Slack assistant](https://mastra.ai/guides/guide/slack-assistant) - [Agent overview](https://mastra.ai/docs/agents/overview) - [Tool approval](https://mastra.ai/docs/agents/agent-approval) - [Channels reference](https://mastra.ai/reference/agents/channels) - [Chat SDK adapters](https://chat-sdk.dev/adapters)