# Codex Discord

Local Codex plugin that exposes profile-scoped Discord MCP tools and routes
authorized Discord DMs and guild messages into the selected tmux Codex session.

This is intentionally not bound to one Discord account or one channel directory.
Use one directory per Discord identity or tmux session:

```text
~/.codex/channels/<profile>/.env
~/.codex/channels/<profile>/access.json
~/.codex/channels/<profile>/inbox/
```

Select a profile with the MCP tool `profile` argument or by starting Codex with:

```bash
export CODEX_DISCORD_PROFILE=<profile>
```

The `access.json` shape is compatible with Anthropic's Discord channel plugin:

```json
{
  "dmPolicy": "allowlist",
  "allowFrom": ["<discord-user-id>"],
  "groups": {
    "<discord-channel-id>": { "requireMention": true, "allowFrom": [] }
  },
  "pending": {},
  "ackReaction": "👀",
  "replyToMode": "first",
  "textChunkLimit": 2000,
  "chunkMode": "newline"
}
```

The four UX fields are optional. `replyToMode` is `off`, `first`, or `all`;
`chunkMode` is `length` or `newline`. Access changes are local-operator actions:
an inbound Discord message must never be allowed to modify `access.json`.

## Runtime behavior

The MCP server uses two Discord interfaces:

- Discord REST API v10 for reading messages, sending replies, reactions, and
  attachment downloads, current guild search, polls, pins, and forwarding.
- Discord Gateway v10 for a persistent online presence. The connection handles
  Hello, jittered heartbeats, heartbeat ACK validation, Identify/Ready,
  Resume, invalid sessions, fatal close codes, and exponential reconnects.

The Gateway starts automatically for `CODEX_DISCORD_PROFILE` when Codex starts
the plugin's stdio MCP server. It stops when that MCP process exits. In tmux,
authorized inbound messages are pasted into the Codex pane as user input. DMs
must pass `allowFrom`; guild channels must exist in `groups`. Set
`requireMention: false` to trigger on messages without mentioning the bot.
Each inbound turn is a single compact `<channel source="discord" ...>` block.
Stable reply-routing and security guidance is supplied once through MCP server
instructions instead of being repeated in every message, and ordinary inbound
turns do not load the Discord management skill.

After an authorized message passes the inbound gate, the plugin immediately
triggers Discord's typing indicator and renews it every eight seconds. It stops
when `send_message`/`reply` finishes, delivery fails, the process exits, or the
five-minute safety timeout expires. Optional `ackReaction` provides a persistent
"seen" signal for long tasks.

## MCP tools

Core and compatibility tools:

| Tool | Purpose |
| --- | --- |
| `send_message` | Send text, reply to a message, attach up to 10 files, split long text, optionally suppress embeds or notifications. |
| `reply` | Claude-compatible `chat_id` alias for `send_message`. |
| `edit_message` | Edit a message authored by this bot. |
| `delete_message` | Delete a message authored by this bot; refuses other authors. |
| `fetch_messages` | Read up to 100 messages with `before`, `after`, or `around` pagination. |
| `search_messages` | Search an allowlisted guild channel by text, author, or media type. |
| `react` / `remove_reaction` | Add or remove the bot's own reaction. |
| `download_attachments` | Download attachments to the selected profile inbox. |
| `download_attachment` | Claude-compatible singular alias using `chat_id`. |
| `create_poll` | Create a native poll with 2–10 answers and up to 32 days duration. |
| `forward_message` | Forward a readable message as a Discord message snapshot. |
| `get_pins` | Read the current paginated pins endpoint. |
| `list_profiles`, `profile_status`, `whoami` | Inspect profile selection and credentials without exposing tokens. |
| `gateway_status`, `restart_gateway`, `inbound_status` | Diagnose Gateway and tmux delivery health. |

Outbound text sets `allowed_mentions.parse=[]` by default so generated text
cannot accidentally ping users, roles, `@here`, or `@everyone`. File sending
rejects relative paths, profile state files, non-regular files, more than 10
attachments, and requests over Discord's 25 MiB total limit.

`search_messages` uses Discord's bot guild-search API documented in March 2026.
It requires `READ_MESSAGE_HISTORY` and the application's privileged
`MESSAGE_CONTENT` intent, only searches the allowlisted channel passed to the
tool, and is unavailable in DMs.

Gateway configuration is optional:

```bash
# Enabled by default. Set to 0/false/off to keep REST-only behavior.
export CODEX_DISCORD_GATEWAY=1

# Minimal default: no event subscriptions are required for online presence.
# Optional override. Inbound mode defaults to Guilds + GuildMessages +
# DirectMessages + MessageContent (37377).
export CODEX_DISCORD_GATEWAY_INTENTS=37377

# Enabled by default when Codex runs inside tmux.
export CODEX_DISCORD_INBOUND=1

# Initial presence shown by Discord.
export CODEX_DISCORD_STATUS=online       # online, idle, dnd, or invisible
export CODEX_DISCORD_ACTIVITY=Codex
```

Keep `CODEX_DISCORD_GATEWAY_INTENTS=0` unless event subscriptions are actually
needed. Privileged intents must be enabled in the Discord Developer Portal;
invalid or disallowed intents cause Gateway close codes 4013 or 4014.

Use the MCP tools `gateway_status` to inspect Ready/heartbeat/reconnect state
and `restart_gateway` after changing token, presence, or intent settings.

## Backup and disaster recovery

The maintained source is `/data/app/dylan/codex-discord`. The personal
marketplace exposes it through `/root/plugins/codex-discord`, which should be a
symlink to that maintained source. The Codex backup repository exports the
source to `plugins/codex-discord`; `node_modules` and profile `.env` files are
not committed. Secrets are handled separately by the encrypted backup flow.

After a fresh restore:

```bash
bash /opt/codex-backup/scripts/restore-secrets.sh
bash /opt/codex-backup/scripts/restore.sh all --delete
bash /opt/codex-backup/scripts/post-restore-fixups.sh
codex plugin add codex-discord@personal
```

`post-restore-fixups.sh` recreates the marketplace symlink, restores executable
permissions, installs locked npm dependencies when needed, and runs syntax
checks. Start a new Codex thread/session after reinstall so the updated MCP tool
catalog is loaded.

Official references:

- [Codex MCP configuration](https://developers.openai.com/codex/mcp/)
- [Discord Gateway lifecycle](https://docs.discord.com/developers/events/gateway)
- [Discord Gateway event payloads](https://docs.discord.com/developers/events/gateway-events)
- [Discord bots and connection options](https://docs.discord.com/developers/platform/bots)
- [Discord channel and typing endpoints](https://docs.discord.com/developers/resources/channel)
- [Discord message, search, forwarding, pins, and attachment endpoints](https://docs.discord.com/developers/resources/message)
- [Discord poll objects and limits](https://docs.discord.com/developers/resources/poll)
- [Discord HTTP rate limits](https://docs.discord.com/developers/topics/rate-limits)
