Discord Voice

@tuttiai/discord — give agents a bot that can read, post, and moderate messages on Discord

The Discord voice gives agents a bot account they can use to read, post, and moderate messages.

Write tools (post_message, edit_message, delete_message, add_reaction, send_dm) are marked destructive: true, so HITL-enabled runtimes gate them behind human approval before anything hits a server.

Installation

npx tutti-ai add discord

Required permissions

permissions: ["network"]

Required environment variables

VarDescription
DISCORD_BOT_TOKENDiscord bot token

Add to your .env:

DISCORD_BOT_TOKEN=your_bot_token_here

Bot setup

  1. Open the Discord Developer Portal and click New Application.
  2. BotReset Token → copy into DISCORD_BOT_TOKEN (the token is only shown once).
  3. Enable the Server Members and Message Content privileged gateway intents (needed for list_members, list_messages, get_message, search_messages).
  4. OAuth2URL Generator — tick bot scope plus the permissions you need (minimum: View Channels, Send Messages, Read Message History, Add Reactions; add Manage Messages for delete_message on other users’ messages).
  5. Open the generated URL and invite the bot to your server.

Configuration

// Default: reads DISCORD_BOT_TOKEN from env
new DiscordVoice()

// Explicit token
new DiscordVoice({ token: "..." })

Tool reference

ToolDestructiveDescription
post_messageyesPost to a channel. Optional reply_to_message_id.
edit_messageyesEdit a message the bot wrote.
delete_messageyesDelete a message (own, or any if the bot has Manage Messages).
add_reactionyesReact with a unicode or custom emoji.
send_dmyesDirect-message a user by id.
list_messagesnoRecent messages, newest first, with limit / before / after.
get_messagenoFull detail on a single message.
list_channelsnoText-capable channels with id, name, topic.
list_membersnoGuild members with roles + join timestamps.
search_messagesnoLocal substring search over the last 100 messages in a channel.
get_guild_infonoName, member count, channel count, icon URL.

Example

import { defineScore, AnthropicProvider } from "@tuttiai/core";
import { DiscordVoice } from "@tuttiai/discord";

export default defineScore({
  provider: new AnthropicProvider(),
  agents: {
    mod: {
      name: "mod",
      model: "claude-sonnet-4-20250514",
      system_prompt:
        "You are a community moderator. When users flag content, read the relevant messages, summarise, and propose (but do not execute) moderation actions unless explicitly approved.",
      voices: [new DiscordVoice()],
      permissions: ["network"],
    },
  },
});

Run it:

tutti-ai run mod "Check #reports for new flags"

With a HITL-enabled runtime, any post_message / delete_message / send_dm call pauses for human approval before execution.

Lifecycle

The voice’s discord.js Client is lazily logged in on the first tool call and kept warm for the lifetime of the voice. Call voice.teardown() (or TuttiRuntime.teardown()) on shutdown to close the gateway connection cleanly.

Inbound (inbox)

The same DiscordClientWrapper powers @tuttiai/inbox’s Discord adapter. Discord’s Gateway API allows only one bot session per token, so the inbox adapter and the voice share a single Client via DiscordClientWrapper.forToken(token). Configure the inbox adapter in your score:

inbox: {
  agent: "support",
  adapters: [{ platform: "discord" }],  // token from DISCORD_BOT_TOKEN
}

The default intents now include DirectMessages so DM-flow inbox use works out of the box. Messages from any bot (yours or third-party) are filtered out of the dispatcher to prevent reply loops.

Edit this page on GitHub →