Skip to main content
Commands, package names, and image names on this page come from the open-source project that Mibyan Desktop is built on, and can differ from the Mibyan Desktop installer. For the supported Mibyan install and update path, see Install and update.
Mibyan integrates with Discord as a bot, letting you chat with your AI assistant through direct messages or server channels. The bot receives your messages, processes them through the Mibyan pipeline (including tool use, memory, and reasoning), and responds in real time. It supports text, voice messages, file attachments, and slash commands. Before setup, here’s the part most people want to know: how Mibyan behaves once it’s in your server.

How Mibyan Behaves

If you want a normal bot-help channel where people can talk to Mibyan without tagging it every time, add that channel to DISCORD_FREE_RESPONSE_CHANNELS.

Discord Gateway Model

Mibyan on Discord is not a webhook that replies statelessly. It runs through the full messaging gateway, which means each incoming message goes through:
  1. authorization (DISCORD_ALLOWED_USERS)
  2. mention / free-response checks
  3. session lookup
  4. session transcript loading
  5. normal Mibyan agent execution, including tools, memory, and slash commands
  6. response delivery back to Discord
That matters because behavior in a busy server depends on both Discord routing and Mibyan session policy.

Session Model in Discord

By default:
  • each DM gets its own session
  • each server thread gets its own session namespace
  • each user in a shared channel gets their own session inside that channel
So if Alice and Bob both talk to Mibyan in #research, Mibyan treats those as separate conversations by default even though they are using the same visible Discord channel. This is controlled by config.yaml:
Set it to false only if you explicitly want one shared conversation for the entire room:
Shared sessions can be useful for a collaborative room, but they also mean:
  • users share context growth and token costs
  • one person’s long tool-heavy task can bloat everyone else’s context
  • one person’s in-flight run can interrupt another person’s follow-up in the same room

Interrupts and Concurrency

Mibyan tracks running agents by session key. With the default group_sessions_per_user: true:
  • Alice interrupting her own in-flight request only affects Alice’s session in that channel
  • Bob can keep talking in the same channel without inheriting Alice’s history or interrupting Alice’s run
With group_sessions_per_user: false:
  • the whole room shares one running-agent slot for that channel/thread
  • follow-up messages from different people can interrupt or queue behind each other
This guide walks you through the full setup process — from creating your bot on Discord’s Developer Portal to sending your first message.

Gateway WebSocket health

Discord REST and the Gateway WebSocket are separate transports. A successful REST response (including fetch_user() returning HTTP 200) does not prove that the bot can still receive Gateway events. Mibyan therefore combines the ready state, client/socket closure state, socket openness, heartbeat ACK age, finite heartbeat latency, and — since the dispatch-side dimension — how long it has been since the last parsed Gateway event. After the configured number of consecutive unhealthy samples, the adapter emits one retryable fatal event. A closed transport (socket_closed / client_closed) is a confirmed death and forces the reconnect on the first unhealthy sample; the threshold applies to soft signals only (stale heartbeat ACK, latency, event silence) — see #118487. The existing gateway reconnect watcher creates a fresh adapter; the Discord adapter does not start a second unbounded reconnect loop. Configure the non-secret thresholds in config.yaml:
The old liveness_interval_seconds and liveness_failure_threshold names remain compatibility aliases only; they no longer mean REST probing. Any knob at 0 disables the whole WebSocket liveness probe. Values that fail to parse as a positive number (e.g. 15s, nan, true, -1) also disable it, and log a warning each time the adapter starts — check gateway.log if the probe seems inactive. websocket_event_max_silence_seconds is the exception: it guards a single dimension (event dispatch), so 0 opts out of that check only — ready/ACK/latency keep guarding. A socket can stay ESTABLISHED and keep ACKing heartbeats while delivering zero Gateway events; heartbeat ACKs are frames without an event type, so no transport-side check can see that state. The default (4 hours) matches the outage window operators have observed in the field; a quiet guild can legitimately go hours without a single Gateway event, so keep this bound generous unless you know your traffic.

Step 1: Create a Discord Application

  1. Go to the Discord Developer Portal and sign in with your Discord account.
  2. Click New Application in the top-right corner.
  3. Enter a name for your application (e.g., “Mibyan”) and accept the Developer Terms of Service.
  4. Click Create.
You’ll land on the General Information page. Note the Application ID — you’ll need it later to build the invite URL.

Step 2: Create the Bot

  1. In the left sidebar, click Bot.
  2. Discord automatically creates a bot user for your application. You’ll see the bot’s username, which you can customize.
  3. Under Authorization Flow:
    • Set Public Bot to ON — required to use the Discord-provided invite link (recommended). This allows the Installation tab to generate a default authorization URL.
    • Leave Require OAuth2 Code Grant set to OFF.
You can set a custom avatar and banner for your bot on this page. This is what users will see in Discord.
Private Bot AlternativeIf you prefer to keep your bot private (Public Bot = OFF), you must use the Manual URL method in Step 5 instead of the Installation tab. The Discord-provided link requires Public Bot to be enabled.

Step 3: Enable Privileged Gateway Intents

This is the most critical step in the entire setup. Without the correct intents enabled, your bot will connect to Discord but will not be able to read message content. On the Bot page, scroll down to Privileged Gateway Intents. You’ll see three toggles: Enable both Server Members Intent and Message Content Intent by toggling them ON.
  • Without Message Content Intent, your bot receives message events but the message text is empty — the bot literally cannot see what you typed.
  • Without Server Members Intent, the bot cannot resolve usernames for the allowed users list and may fail to identify who is messaging it.
This is the #1 reason Discord bots don’t workIf your bot is online but never responds to messages, the Message Content Intent is almost certainly disabled. Go back to the Developer Portal, select your application → Bot → Privileged Gateway Intents, and make sure Message Content Intent is toggled ON. Click Save Changes.
Regarding server count:
  • If your bot is in fewer than 100 servers, you can simply toggle intents on and off freely.
  • If your bot is in 100 or more servers, Discord requires you to submit a verification application to use privileged intents. For personal use, this is not a concern.
Click Save Changes at the bottom of the page.

Step 4: Get the Bot Token

The bot token is the credential Mibyan uses to log in as your bot. Still on the Bot page:
  1. Under the Token section, click Reset Token.
  2. If you have two-factor authentication enabled on your Discord account, enter your 2FA code.
  3. Discord will display your new token. Copy it immediately.
Token shown only onceThe token is only displayed once. If you lose it, you’ll need to reset it and generate a new one. Never share your token publicly or commit it to Git — anyone with this token has full control of your bot.
Store the token somewhere safe (a password manager, for example). You’ll need it in Step 8.

Step 5: Generate the Invite URL

You need an OAuth2 URL to invite the bot to your server. There are two ways to do this:
Requires Public BotThis method requires Public Bot to be set to ON in Step 2. If you set Public Bot to OFF, use the Manual URL method below instead.
  1. In the left sidebar, click Installation.
  2. Under Installation Contexts, enable Guild Install.
  3. For Install Link, select Discord Provided Link.
  4. Under Default Install Settings for Guild Install:
    • Scopes: select bot and applications.commands
    • Permissions: select the permissions listed below.

Option B: Manual URL

You can construct the invite URL directly using this format:
Replace YOUR_APP_ID with the Application ID from Step 1.

Required Permissions

These are the minimum permissions your bot needs:
  • View Channels — see the channels it has access to
  • Send Messages — respond to your messages
  • Embed Links — format rich responses
  • Attach Files — send images, audio, and file outputs
  • Read Message History — maintain conversation context
  • Send Messages in Threads — respond in thread conversations
  • Add Reactions — react to messages for acknowledgment

Permission Integers

Step 6: Invite to Your Server

  1. Open the invite URL in your browser (from the Installation tab or the manual URL you constructed).
  2. In the Add to Server dropdown, select your server.
  3. Click Continue, then Authorize.
  4. Complete the CAPTCHA if prompted.
You need the Manage Server permission on the Discord server to invite a bot. If you don’t see your server in the dropdown, ask a server admin to use the invite link instead.
After authorizing, the bot will appear in your server’s member list (it will show as offline until you start the Mibyan gateway).

Step 7: Find Your Discord User ID

Mibyan uses your Discord User ID to control who can interact with the bot. To find it:
  1. Open Discord (desktop or web app).
  2. Go to Settings → Advanced → toggle Developer Mode to ON.
  3. Close settings.
  4. Right-click your own username (in a message, the member list, or your profile) → Copy User ID.
Your User ID is a long number like 284102345871466496.
Developer Mode also lets you copy Channel IDs and Server IDs the same way — right-click the channel or server name and select Copy ID. You’ll need a Channel ID if you want to set a home channel manually.

Step 8: Configure Mibyan

Run the guided setup command:
Select Discord when prompted, then paste your bot token and user ID when asked.

Option B: Manual Configuration

Add the following to your ~/.mibyan/.env file:
Then start the gateway:
The bot should come online in Discord within a few seconds. Send it a message — either a DM or in a channel it can see — to test.
You can run mibyan gateway in the background or as a systemd service for persistent operation. See the deployment docs for details.

Configuration Reference

Discord behavior is controlled through two files: ~/.mibyan/.env for credentials and env-level toggles, and ~/.mibyan/config.yaml for structured settings. Environment variables always take precedence over config.yaml values when both are set.

Environment Variables (.env)

Bot-to-bot handoffs: tag once, collect the burst

Bot input remains opt-in (DISCORD_ALLOW_BOTS=none by default). When enabled with mentions or all, starting a bot handoff requires a literal <@BOT_ID> / <@!BOT_ID> in the message content by default. Discord’s automatic reply ping alone does not start a handoff. Human-authored messages retain their existing mention behavior. After an admitted bot mention, Mibyan briefly accepts unmentioned follow-ups from that same bot in that same channel or thread. Text follow-ups enter the existing text batcher, so a rapidly split response can reach the agent together without repeating the tag on every part. No special sender protocol or part markers are required. For example, bot A sends <@BOT_B_ID> Here is the review …, followed immediately by two untagged text chunks in the same thread. Bot B admits those chunks during the continuation window and batches them with the tagged text. After the window expires, an untagged message or reply ping cannot start another handoff under the default policy. Explicit reciprocal tags remain supported; this prevents accidental reply-metadata loops, not intentionally continued conversations.

Timing and limits

The admission window lasts max(mibyan_DISCORD_TEXT_BATCH_DELAY_SECONDS, mibyan_DISCORD_TEXT_BATCH_SPLIT_DELAY_SECONDS) after the admitted mention (2 seconds with the defaults), and each admitted continuation re-arms it, so a long handoff paced at Discord’s send rate arrives whole. Separately, each queued text chunk restarts the batch’s quiet timer; a tagged bot batch uses the split quiet period even when its first chunk is short. These settings control receiver batching, not sender pacing. Disabling text batching (mibyan_DISCORD_TEXT_BATCH_DELAY_SECONDS=0) also disables continuation admission. This is a short-burst heuristic, not guaranteed multipart delivery. A delayed chunk outside the window needs its own mention under the default policy. Unrelated messages from the same bot and channel inside the window can also be admitted. The exception is not restricted to text: attachments and commands may pass admission, but only text enters this batcher; other message types follow their normal handling. Existing channel restrictions and DISCORD_ALLOW_BOTS=none still apply. History/backfill behavior is unchanged. The gateway’s bot loop guard remains as a backstop: after 20 bot-authored messages in one channel inside 5 minutes, further bot messages there are dropped for 10 minutes (tunable under gateway.bot_loop_guard in config.yaml; human messages are never counted).

Compatibility with trusted relays

The inline-mention requirement now defaults to true (previously false), including when DISCORD_ALLOW_BOTS=all. If an existing relay intentionally relies on reply pings or unmentioned bot messages, retain the old admission behavior explicitly:
The existing DISCORD_BOTS_REQUIRE_INLINE_MENTION=false environment override is also supported when the YAML option is unset. With this opt-out, mentions accepts Discord’s resolved mentions, including reply pings, and all removes the bot-specific mention requirement; other channel mention rules still apply. An admitted reply ping can also open the continuation window in this compatibility mode. Use it only for trusted relays because it restores the accidental reply-loop risk.

Config File (config.yaml)

The discord section in ~/.mibyan/config.yaml mirrors the env vars above. Config.yaml settings are applied as defaults — if the equivalent env var is already set, the env var wins.

discord.require_mention

Type: boolean — Default: true When enabled, the bot only responds in server channels when directly @mentioned. DMs always get a response regardless of this setting.

discord.thread_require_mention

Type: boolean — Default: false By default, once the bot has participated in a thread (auto-created on @mention or replied in once), it keeps responding to every subsequent message in that thread without needing to be @mentioned again. That’s the right default for one-on-one conversations. In multi-bot threads where users address one bot per turn, this default becomes a footgun — every other bot in the thread also fires on every message, burning credits and spamming the channel. Set thread_require_mention: true to disable the in-thread shortcut and gate threads the same way channels are gated. Explicit @mentions still work as before.

discord.free_response_channels

Type: string or list — Default: "" Channel IDs where the bot responds to all messages without needing an @mention. Accepts either a comma-separated string or a YAML list:
If a thread’s parent channel is in this list, the thread also becomes mention-free. Free-response channels also skip auto-threading by default — the bot replies inline rather than spinning off a new thread per message. This keeps the channel usable as a lightweight chat surface. To opt in to threading for free-response channels, set discord.free_response_auto_thread: true (or DISCORD_FREE_RESPONSE_AUTO_THREAD=true). In that mode each new top-level message in a free channel still gets its own thread, but the channel remains @mention-free. Requires discord.auto_thread: true.

discord.free_response_auto_thread

Type: boolean — Default: false When true, channels listed in discord.free_response_channels also auto-create a thread for each top-level message, instead of answering inline. The channel stays mention-free; it only changes where the conversation lives.
Requires discord.auto_thread: true (with it off, nothing threads anywhere). discord.no_thread_channels still wins, voice-linked text channels always reply inline, and reply-type messages are never auto-threaded. DISCORD_FREE_RESPONSE_AUTO_THREAD wins over the config.yaml key when both are set — the YAML value only seeds the env var when it isn’t already set, like every other discord.* bridge.

discord.auto_thread

Type: boolean — Default: true When enabled, every @mention in a regular text channel automatically creates a new thread for the conversation. This keeps the main channel clean and gives each conversation its own isolated session history. Once a thread is created, subsequent messages in that thread don’t require @mention — the bot knows it’s already participating. Set thread_require_mention to true to disable this in-thread shortcut for multi-bot setups. Messages sent in existing threads or DMs are unaffected by this setting. Channels listed in discord.no_thread_channels, and channels listed in discord.free_response_channels unless discord.free_response_auto_thread is true, also bypass auto-threading and get inline replies instead.

discord.reactions

Type: boolean — Default: true Controls whether the bot adds emoji reactions to messages as visual feedback:
  • 👀 added when the bot starts processing your message
  • ✅ added when the response is delivered successfully
  • ❌ added if an error occurs during processing
Disable this if you find the reactions distracting or if the bot’s role doesn’t have the Add Reactions permission.

discord.ignored_channels

Type: string or list — Default: [] Channel IDs where the bot never responds, even when directly @mentioned. This takes the highest priority — if a channel is in this list, the bot silently ignores all messages there, regardless of require_mention, free_response_channels, or any other setting.
If a thread’s parent channel is in this list, messages in that thread are also ignored.

discord.no_thread_channels

Type: string or list — Default: [] Channel IDs where the bot responds directly in the channel instead of auto-creating a thread. This only has an effect when auto_thread is true (the default). In these channels, the bot responds inline like a normal message rather than spawning a new thread.
Useful for channels dedicated to bot interaction where threads would add unnecessary noise.

discord.channel_prompts

Type: mapping — Default: {} Per-channel ephemeral system prompts that are injected on every turn in the matching Discord channel or thread without being persisted to transcript history.
Behavior:
  • Exact thread/channel ID matches win.
  • If a message arrives inside a thread or forum post and that thread has no explicit entry, Mibyan falls back to the parent channel/forum ID.
  • Prompts are applied ephemerally at runtime, so changing them affects future turns immediately without rewriting past session history.

discord.history_backfill

Type: boolean — Default: true When enabled, the bot recovers missed channel messages on each @mention. With require_mention: true, the bot only processes messages that tag it directly — everything else in the channel is invisible to the session transcript. History backfill scans backwards through recent channel history when triggered, collecting messages between the bot’s last response and the current mention, and includes them as context. Behavior by surface:
  • Server channels (with require_mention: true): backfill scans the channel since the bot’s last response. Useful when other participants posted while the bot wasn’t addressed.
  • Threads: backfill scans the thread only — Discord’s channel.history() on a thread returns only that thread’s messages, not the parent channel. This is the right scope because threads are usually self-contained conversations.
  • DMs: skipped. Every DM message triggers the bot, so the session transcript is already complete — there’s no mention gap to fill.
  • Free-response channels and bot’s own auto-created threads: skipped for the same reason — no mention gating means no gap.
Per-user sessions (group_sessions_per_user: true, the default) also benefit: a user’s session is missing the context posted by other channel participants and the user’s own messages from before they tagged the bot. Backfill fills both gaps.
To turn it off:
Note: Messages that arrive while the bot is processing (between a trigger and its response) are not captured. This is an accepted simplification — the user can re-send or tag again.

discord.history_backfill_limit

Type: integer — Default: 50 Maximum number of messages to scan backwards when recovering channel context. In practice the scan usually stops much earlier — at the bot’s own last message in the channel, which is the natural boundary between turns. This limit is a safety cap for cold starts and long gaps where no prior bot message exists in recent history.

discord.missed_message_backfill

Type: object — Default: disabled Discord’s WebSocket resume window can expire during a restart or network outage. Messages sent during that gap are not delivered as live gateway events. When this option is enabled, Mibyan scans a bounded set of configured channel and thread histories after Discord reconnects, then sends still-unhandled messages through the same authorization, mention, channel, deduplication, and dispatch path as live events.
If channels is empty, Mibyan uses discord.free_response_channels. Set it to "*" only when the bot should inspect every reachable server text channel. The recovery ledger is stored per profile under gateway/discord_message_recovery.db, preventing a successfully answered message from being replayed again after a later restart. A message counts as answered once its turn delivered a final reply, whether or not that reply carried a Discord reply reference (reply_to_mode: "off", streamed replies and media-only replies included). max_dispatches caps one scan; max_attempts (default 3) caps how many times a single message can ever be re-dispatched, so a message whose turn keeps failing is not re-run on every reconnect. window_seconds is always honoured: the per-channel scan cursor can narrow a scan but never reaches further back than the window.

group_sessions_per_user

Type: boolean — Default: true This is a global gateway setting (not Discord-specific) that controls whether users in the same channel get isolated session histories. When true: Alice and Bob talking in #research each have their own separate conversation with Mibyan. When false: the entire channel shares one conversation transcript and one running-agent slot.
See the Session Model section above for the full implications of each mode.

display.tool_progress

Type: string — Default: "all" — Values: off, new, all, verbose Controls whether the bot sends progress messages in the chat while processing (e.g., “Reading file…”, “Running terminal command…”). This is a global gateway setting that applies to all platforms.
  • off — no progress messages
  • new — only show the first tool call per turn
  • all — show all tool calls (truncated to 40 characters in gateway messages)
  • verbose — show full tool call details (can produce long messages)

display.tool_progress_command

Type: boolean — Default: false When enabled, makes the /verbose slash command available in the gateway, letting you cycle through tool progress modes (off → new → all → verbose → off) without editing config.yaml.

display.reasoning_style

Type: string — Default (Discord): "subtext" — Values: code, blockquote, subtext Controls how the model’s reasoning block is rendered when reasoning display is enabled. Discord defaults to subtext, which uses Discord’s native -# small grey metadata text so reasoning stays visually secondary to the answer. blockquote renders it as a > quote, and code (the default on other platforms) uses a fenced code block. Long reasoning is collapsed to the first 15 lines.

Slash Command Access Control

By default, every allowed user can run every slash command. To split your allowlist into admins (full slash command access) and regular users (only commands you explicitly enable), add allow_admin_from and user_allowed_commands to the Discord platform’s extra block:
Behavior:
  • A user in allow_admin_from for a scope (DM or server channel) can run every registered slash command — built-in AND plugin-registered — through the live command registry.
  • A user not in allow_admin_from can only run commands listed in user_allowed_commands, plus the always-allowed floor: /help and /whoami.
  • Plain chat (non-slash messages) is unaffected. Non-admin users can still talk to the agent normally; they just can’t trigger arbitrary commands.
  • Backward compat: if allow_admin_from is not set for a scope, slash command gating is disabled for that scope. Existing installs keep working with no changes.
  • DM admin status does not imply server-channel admin status. Each scope has its own admin list.
Use /whoami to see the active scope, your tier (admin / user / unrestricted), and which slash commands you can run.

Interactive Model Picker

Send /model with no arguments in a Discord channel to open a dropdown-based model picker:
  1. Provider selection — a Select dropdown showing available providers (up to 25).
  2. Model selection — a second dropdown with models for the chosen provider (up to 25).
The picker times out after 120 seconds. Only authorized users (those in DISCORD_ALLOWED_USERS) can interact with it. If you know the model name, type /model <name> directly.

Native Slash Commands for Skills

Mibyan automatically registers installed skills as native Discord Application Commands. This means skills appear in Discord’s autocomplete / menu alongside built-in commands.
  • Each skill becomes a Discord slash command (e.g., /code-review, /ascii-art)
  • Skills accept an optional args string parameter
  • Discord has a limit of 100 application commands per bot — if you have more skills than available slots, extra skills are skipped with a warning in the logs
  • Skills are registered during bot startup alongside built-in commands like /model, /reset, and /bg
No extra configuration is needed — any skill installed via mibyan skills install is automatically registered as a Discord slash command on the next gateway restart.

Disabling Slash Command Registration

If you run multiple Mibyan gateways against the same Discord application (e.g. staging + production), only one of them should own the global slash-command registration — otherwise the last startup wins and the registrations flap. Turn slash registration off on the “follower” gateway:
Leaving this at true on the “primary” gateway keeps the normal behavior — global /-menu commands for built-ins and installed skills.

Sending Media (inline MEDIA: tags)

The Discord adapter supports native file uploads for every common media type via inline MEDIA:/path/to/file tags emitted in the agent’s response — the adapter strips the tag and auto-uploads the file: Discord’s per-upload size limit depends on the server’s boost tier (25 MB free, up to 500 MB). If Mibyan gets an HTTP 413, the adapter falls back to a link pointing at the local cache path rather than failing silently.

Receiving Arbitrary File Types

Any file type a user uploads is accepted. Authorization to message the agent is the gate — not the file extension. Every upload is downloaded, cached under ~/.mibyan/cache/documents/, and surfaced to the agent as a DOCUMENT-typed message event so it can inspect the file with terminal (ffprobe, unzip, file, strings, etc.) or read_file.
  • Known types (PDF, docx/xlsx/pptx, zip, images/audio/video, etc.) keep their precise MIME.
  • Unknown types fall back to the upload’s reported content type, or application/octet-stream when none is given.
  • Small UTF-8-decodable files (text, code, config, HTML, CSS, JSON, YAML, …) have their contents auto-injected into the prompt up to 100 KiB. Binary files that can’t be decoded are surfaced as a path-pointing context note only (auto-translated for Docker/Modal sandboxed terminals via to_agent_visible_cache_path), so they don’t blow up the context window.
The only inbound limit is the per-file size cap (default 32 MiB):
Equivalent env var: DISCORD_MAX_ATTACHMENT_BYTES=33554432 (or 0 for no cap). The legacy discord.allow_any_attachment flag is now a no-op — any file type is always accepted — and is kept only so existing configs don’t error.
Memory cost of unlimitedDisabling the size cap (max_attachment_bytes: 0) means a user can drop a multi-GB file on the bot and the gateway will dutifully buffer it through memory while caching to disk. Only set this in trusted single-user installs. For shared bots, keep the default 32 MiB or raise it conservatively.

Interactive Prompts (clarify)

When the agent calls the clarify tool — to ask which approach you prefer, get post-task feedback, or check before a non-trivial decision — Discord renders the question with one button per choice:
Which framework should I use for the dashboard? [1. Next.js] [2. Remix] [3. Astro] [Other (type answer)]
Click a numbered button to answer, or click Other to type a free-form response (the next message you send in that channel becomes the answer). Open-ended clarify calls (no preset choices) skip the buttons and just capture your next message. The buttons disable themselves once a choice is made so duplicate clicks don’t double-resolve the prompt. Configure the response timeout via agent.clarify_timeout in ~/.mibyan/config.yaml (default 3600 seconds; 0 or less = unlimited). If you don’t respond within the timeout, the agent unblocks with "outcome": "timed_out" and adapts rather than hanging. Reply skip to skip a question.

Prompt layout

Interactive prompts (command approvals, clarify questions, and slash-command confirmations) share one layout: the plain message carries the full payload — the command and why it was flagged plus the approval deadline, or the question and reply hint — the embed card underneath is a header only, and the buttons sit below the card. Everything you need to decide is in the plain text, so the prompt reads correctly on clients that hide or detach embeds, and nothing is shown twice on clients that render them.

Home Channel

You can designate a “home channel” where the bot sends proactive messages (such as cron job output, reminders, and notifications). There are two ways to set it:

Using the Slash Command

Type /sethome in any Discord channel where the bot is present. That channel becomes the home channel.

Manual Configuration

Add these to your ~/.mibyan/.env:
Replace the ID with the actual channel ID (right-click → Copy Channel ID with Developer Mode on).

Voice Messages

Mibyan supports Discord voice messages:
  • Incoming voice messages are automatically transcribed using the configured STT provider: local faster-whisper (no key), Groq Whisper (GROQ_API_KEY), or OpenAI Whisper (VOICE_TOOLS_OPENAI_KEY).
  • Text-to-speech: Use /voice tts to have the bot send spoken audio responses alongside text replies.
  • Discord voice channels: Mibyan can also join a voice channel, listen to users speaking, and talk back in the channel.
For the full setup and operational guide, see:

Voice Channel Audio Effects (ambient + verbal acks)

When the bot is in a voice channel, you can give it a more conversational feel: a short verbal acknowledgement (“let me look into that”) before it starts working, and a subtle ambient “thinking” bed that plays underneath while tools run — the speech ducks the ambient down and swells it back when finished, similar to Grok voice mode. discord.py plays only one audio stream per connection, so Mibyan installs a software mixer on the outgoing stream that sums an ambient loop, acknowledgements, and TTS replies into that single stream — they overlap instead of cutting each other off. This is off by default. Enable it in config.yaml:
Notes:
  • Set voice_channel_inactivity_timeout_seconds: 0 if you want the bot to remain in the voice channel until an explicit /voice leave or manual disconnect. The default preserves the historical 300-second idle auto-leave.
  • voice_playback_timeout_seconds is a floor, not a hard cap for long TTS. Mibyan probes the generated audio duration and waits for duration + 30s when that is longer than the configured floor.
  • The acknowledgement fires at most once per turn, only when the bot is in a voice channel and the mixer is active. It uses your configured TTS provider.
  • ambient_path accepts any file ffmpeg can decode; it’s looped seamlessly. Leave it empty to use the built-in synthesised pad (no asset needed).
  • All settings live in config.yaml (not .env) — they’re behavioral, not secrets.
  • When voice_fx.enabled is false, voice playback uses the original one-shot path and nothing changes.

Forum Channels

Discord forum channels (type 15) don’t accept direct messages — every post in a forum must be a thread. Mibyan auto-detects forum channels and creates a new thread post whenever it needs to send there, so text replies, TTS, images, voice messages, and file attachments all work without special handling from the agent.
  • Thread name is derived from the first line of the message (markdown heading prefix stripped, capped at 100 chars). When the message is attachment-only, the filename is used as the fallback thread name.
  • Attachments ride along on the starter message of the new thread — no separate upload step, no partial sends.
  • One call, one thread: each forum send creates a new thread. Successive sends to the same forum will therefore produce separate threads.
  • Detection is three-layered: the channel directory cache first, a process-local probe cache second, and a live GET /channels/{id} probe as a last resort (whose result is then memoized for the life of the process).
Refreshing the directory (/channels refresh on platforms that expose it, or a gateway restart) populates the cache with any forum channels created after the bot started.

Troubleshooting

Bot is online but not responding to messages

Cause: Either Message Content Intent is disabled, or Discord auth is failing closed because no access policy is configured. Fix:
  1. Go to Developer Portal → your app → Bot → Privileged Gateway Intents → enable Message Content Intent → Save Changes.
  2. Verify that at least one Discord access policy is configured:
  3. Restart the gateway:
If the gateway log says Discord is connected and REST API checks work, but every inbound message is silent, look for this warning in ~/.mibyan/logs/gateway.log:
Mibyan 0.18 intentionally fails closed on externally reachable adapters. A Discord bot with no DISCORD_ALLOWED_USERS, no DISCORD_ALLOWED_ROLES, no DISCORD_ALLOWED_CHANNELS, and no explicit allow-all flag will connect successfully but deny inbound users before normal message handling.

”Privileged intents” / PrivilegedIntentsRequired error on startup

Cause: Mibyan requests privileged Gateway Intents that are not enabled for your bot in the Developer Portal. Discord then rejects the WebSocket connection. Mibyan always requests Message Content Intent. It also requests Server Members Intent when your allowlist uses usernames (not numeric IDs) or when DISCORD_ALLOWED_ROLES is set. Presence Intent is not required. Fix:
  1. Go to Developer Portal → your app → Bot → Privileged Gateway Intents.
  2. Enable Message Content Intent (required). Enable Server Members Intent if you use usernames or role allowlists.
  3. Click Save Changes, then restart the gateway (mibyan gateway restart).
The gateway log should name the exact intent(s) Mibyan requested. Until they are enabled, Discord will keep rejecting the connection — this is a portal configuration error, not a flaky network issue.

Bot can’t see messages in a specific channel

Cause: The bot’s role doesn’t have permission to view that channel. Fix: In Discord, go to the channel’s settings → Permissions → add the bot’s role with View Channel and Read Message History enabled.

403 Forbidden errors

Cause: The bot is missing required permissions. Fix: Re-invite the bot with the correct permissions using the URL from Step 5, or manually adjust the bot’s role permissions in Server Settings → Roles.

Bot is offline

Cause: The Mibyan gateway isn’t running, or the token is incorrect. Fix: Check that mibyan gateway is running. Verify DISCORD_BOT_TOKEN in your .env file. If you recently reset the token, update it.

”User not allowed” / Bot ignores you

Cause: Your User ID isn’t in DISCORD_ALLOWED_USERS. Fix: Add your User ID to DISCORD_ALLOWED_USERS in ~/.mibyan/.env and restart the gateway.

People in the same channel are sharing context unexpectedly

Cause: group_sessions_per_user is disabled, or the platform cannot provide a user ID for the messages in that context. Fix: Set this in ~/.mibyan/config.yaml and restart the gateway:
If you intentionally want a shared room conversation, leave it off — just expect shared transcript history and shared interrupt behavior.

Security

Always set DISCORD_ALLOWED_USERS (or DISCORD_ALLOWED_ROLES) to restrict who can interact with the bot. Without either, the gateway denies all users by default as a safety measure. Only authorize people you trust — authorized users have full access to the agent’s capabilities, including tool use and system access.

Role-Based Access Control

For servers where access is managed by roles instead of individual user lists (moderator teams, support staff, internal tooling), use DISCORD_ALLOWED_ROLES — a comma-separated list of role IDs. Any member with one of those roles is authorized.
Semantics:
  • OR with user allowlist. A user is authorized if their ID is in DISCORD_ALLOWED_USERS or they have any role in DISCORD_ALLOWED_ROLES.
  • Server Members Intent auto-enabled. When DISCORD_ALLOWED_ROLES is set, the bot enables the Members intent on connect — required for Discord to send role information with member records.
  • Role IDs, not names. Grab them from Discord: User Settings → Advanced → Developer Mode ON, then right-click any role → Copy Role ID.
  • DM fallback. In DMs the role check scans mutual guilds; a user with an allowed role in any shared server is authorized in DMs too.
This is the preferred pattern when the moderation team churns — new moderators get access the moment the role is granted, with no .env edit or gateway restart.

Mention Control

By default, Mibyan blocks the bot from pinging @everyone, @here, and role mentions, even if its reply contains those tokens. This prevents a poorly-worded prompt or echoed user content from spamming a whole server. Individual @user pings and reply-reference pings (the little “replying to…” chip) stay enabled so normal conversation still works. You can relax these defaults via either env vars or config.yaml:
Leave everyone and roles at false unless you know exactly why you need them. It is very easy for an LLM to produce the string @everyone inside a normal-looking response; without this protection, that would notify every member of your server.
For more information on securing your Mibyan deployment, see the Security Guide.