Documentation Index
Fetch the complete documentation index at: https://docs.mirage.strukto.ai/llms.txt
Use this file to discover all available pages before exploring further.
The Discord resource exposes guild, channel, and member data as a virtual
filesystem mounted at some prefix such as /discord/.
For token setup, see Discord Setup.
Config
import os
from mirage import MountMode, Workspace
from mirage.resource.discord import DiscordConfig, DiscordResource
config = DiscordConfig(token=os.environ["DISCORD_BOT_TOKEN"])
resource = DiscordResource(config=config)
ws = Workspace({"/discord": resource}, mode=MountMode.READ)
Filesystem Layout
/discord/
<guild-name>_<guild-id>/
channels/
<channel-name>_<channel-id>/
<yyyy-mm-dd>.jsonl
...
members/
<username>_<user-id>.json
...
Example:
/discord/
My Server_111222333444555666/
channels/
general_777888999000111222/
2026-04-04.jsonl
2026-04-05.jsonl
random_777888999000111223/
2026-04-11.jsonl
members/
alice_444555666777888999.json
bob_444555666777888900.json
Directory and file names embed the Discord snowflake ID so that write
commands (discord-send-message --channel_id, etc.) can reference
the correct resource without extra lookups.
Guilds
The root lists one directory per guild the bot has access to.
Names containing / are replaced with ∕ (U+2215).
The guild ID is appended after _.
Channels
/discord/<guild>/channels/ lists text channels (types 0, 5, 15).
The channel ID is appended after _.
Each channel directory contains day-partitioned .jsonl history files
for the 30 days leading up to the channel’s last message.
The date range is derived from last_message_id on the channel object,
so inactive channels show dates around their last activity, not today.
Members
/discord/<guild>/members/ lists one .json file per member.
The user ID is appended after _ in the filename.
Reading a member file returns the full member payload from the Discord API.
Smart Commands
grep / rg at different scopes
When grep or rg target a channel or guild directory (not a specific file),
they use the Discord search API instead of downloading every .jsonl file:
# FILE level - downloads the .jsonl, greps locally
grep hello "/discord/My Server/channels/general/2026-04-04.jsonl"
# CHANNEL level - uses Discord search API (GET /guilds/{id}/messages/search)
grep hello "/discord/My Server/channels/general/"
# GUILD level - searches across all channels
grep hello "/discord/My Server/"
Scope detection is handled by mirage/core/discord/scope.py.
head / tail
head and tail on file-level paths use the Discord messages API directly
(GET /channels/{id}/messages) instead of downloading the full day’s history.
Cache
The Discord resource uses IndexCacheStore (same as RAM/S3/disk/GitHub).
Index entries store guild IDs, channel IDs, and last_message_id for
date range computation. There is no separate content cache - file content
caching is handled by the workspace IOResult mechanism.
Example
import asyncio
import os
from dotenv import load_dotenv
from mirage import MountMode, Workspace
from mirage.resource.discord import DiscordConfig, DiscordResource
load_dotenv(".env.development")
config = DiscordConfig(token=os.environ["DISCORD_BOT_TOKEN"])
resource = DiscordResource(config=config)
async def main():
ws = Workspace({"/discord": resource}, mode=MountMode.READ)
# List guilds
r = await ws.execute("ls /discord/")
print(await r.stdout_str())
guild = r.stdout_str().strip().split("\n")[0].strip()
# List channels
r = await ws.execute(f'ls "/discord/{guild}/channels/"')
print(await r.stdout_str())
ch = r.stdout_str().strip().splitlines()[0].strip()
base = f"/discord/{guild}/channels/{ch}"
# Read messages from a specific date
r = await ws.execute(f'cat "{base}/2026-04-04.jsonl" | head -n 3')
print(await r.stdout_str())
# Extract usernames with jq
r = await ws.execute(
f'jq -r ".[] | .author.username" "{base}/2026-04-04.jsonl"')
print(await r.stdout_str())
# Count messages per user
r = await ws.execute(
f'cat "{base}/2026-04-04.jsonl"'
' | jq -r ".[] | .author.username" | sort | uniq -c')
print(await r.stdout_str())
# Search across channel (uses Discord search API)
r = await ws.execute(f'grep hello "{base}/"')
print(await r.stdout_str())
# Search across guild
r = await ws.execute(f'grep hello "/discord/{guild}/"')
print(await r.stdout_str())
# Navigate with cd/pwd
await ws.execute(f'cd "{base}"')
r = await ws.execute("pwd")
print(await r.stdout_str())
# Relative paths after cd
r = await ws.execute("ls | tail -n 5")
print(await r.stdout_str())
if __name__ == "__main__":
asyncio.run(main())
See examples/chat/discord.py for the full working example.
Finding IDs
Resource-specific commands require Discord snowflake IDs
(channel_id, guild_id, message_id). These can be extracted
from the filesystem:
# Guild ID - use stat
stat "/discord/My Server"
# → extra={"guild_id": "1256522563555819574"}
# Channel ID - use stat
stat "/discord/My Server/channels/general"
# → extra={"channel_id": "1256522563555819574"}
# Message ID - inside JSONL messages
jq -r '.[] | "\(.id) [\(.author.username)] \(.content)"' \
"/discord/My Server/channels/general/2026-04-04.jsonl"
# → 1489887688978075769 [alice] hello world
# Find a message then reply
jq -r '.[] | select(.content | test("hello")) | .id' \
"/discord/My Server/channels/general/2026-04-04.jsonl"
# → 1489887688978075769
discord-send-message --channel_id 1256522563555819574 \
--text "Reply" --message_id 1489887688978075769
Working with Large Channels
Tips for efficient access on busy channels:
# Check message count per day
wc -l "/discord/My Server/channels/general/2026-04-04.jsonl"
# Read only recent messages
tail -n 10 "/discord/My Server/channels/general/2026-04-04.jsonl"
# Search uses Discord API at channel/guild level (no file download)
grep "keyword" "/discord/My Server/channels/general/"
# Extract specific fields
jq -r '.[] | "\(.author.username): \(.content)"' \
"/discord/My Server/channels/general/2026-04-04.jsonl" | head -n 20
# Count messages per user
cat "/discord/My Server/channels/general/2026-04-04.jsonl" \
| jq -r '.[] | .author.username' | sort | uniq -c
Note: grep/rg at channel or guild level uses the Discord search
API instead of downloading every .jsonl file, making it efficient
even for large channels.
Shell Commands
Standard commands available on the mounted Discord tree:
| Command | Notes |
|---|
ls | List guilds, channels, members, dates |
cat | Read .jsonl history or member .json |
head / tail | Smart: uses messages API for file scope |
grep / rg | Smart: uses search API for channel/guild scope |
jq | Query JSON; use .[] prefix for JSONL files |
wc | Line/word/byte counts |
stat | File metadata (name, size, type) |
find | Recursive search with -name, -maxdepth |
tree | Directory tree view |
Resource-specific commands:
discord-send-message
Post a message to a channel, optionally as a reply.
discord-send-message --channel_id 1256522563555819574 --text "Hello from MIRAGE"
discord-send-message --channel_id 1256522563555819574 --text "Reply" --message_id 1489887688978075769
| Option | Required | Description |
|---|
--channel_id | yes | Discord channel snowflake ID |
--text | yes | Message text to send |
--message_id | no | Message ID to reply to |
The channel ID can be found in directory names under
/discord/<guild>/channels/ or via stat. Returns the
posted message JSON.
discord-add-reaction
Add an emoji reaction to a message.
discord-add-reaction --channel_id 1256522563555819574 --message_id 1489887688978075769 --reaction 👍
| Option | Required | Description |
|---|
--channel_id | yes | Discord channel snowflake ID |
--message_id | yes | Message snowflake ID |
--reaction | yes | Emoji (unicode or name) |
discord-list-members
Search guild members by name.
discord-list-members --guild_id 1256522563555819574 --query "alice"
| Option | Required | Description |
|---|
--guild_id | yes | Discord guild snowflake |
--query | yes | Username search query |
Returns matching members as JSON array.
discord-get-server-info
Get full guild metadata from the Discord API.
discord-get-server-info --guild_id 1256522563555819574
| Option | Required | Description |
|---|
--guild_id | yes | Discord guild snowflake |
Returns the guild object JSON (name, icon, member count, etc.).