discord-web
by VKirill
README.md
---
title: Discord Web
status: experimental
version: 0.3.5
updated: 2026-09-20
tags: [discord, mcp, chrome-extension]
---
# Discord Web
Локальный мост: агент пишет и читает **открытую вкладку Discord Web** от вашего аккаунта. Без бота, без user token и без `discord.com/api`.
Расширение Chrome (MV3, unpacked) кликает DOM. HTTP-мост слушает только `127.0.0.1`. MCP отдаёт инструменты агенту.
> [!CAUTION]
> Автоматизация пользовательского аккаунта нарушает правила Discord и может привести к ограничениям или бану. Редкие свои посты в своём тестовом канале — да. Рассылка, чужой аккаунт, личные сообщения — нет.
## Что умеет
| Действие | Инструмент | Нужен allowlist |
|---|---|---|
| Текст | `discord_send_message` | да |
| Ответ | `discord_send_message` + `replyTo` | да |
| Реакция | `discord_add_reaction` | да |
| GIF из пикера | `discord_send_gif` | да |
| Статус вкладки | `discord_status` | нет |
| Серверы / каналы / треды | `discord_list_*` | нет |
| Сообщения из ленты | `discord_read_messages` | нет |
| Обход каналов | `discord_collect_content` | только при `scope=allowlist` |
| Reload расширения | `discord_reload_extension` | нет |
Чтение — любой канал сервера в открытой вкладке. Запись — только URL из списка в настройках расширения.
Личные сообщения (`@me`) не поддерживаются.
## Схема
```mermaid
flowchart LR
A[агент] -->|MCP stdio| B[server/mcp.js]
B -->|HTTP 127.0.0.1| C[server/bridge.js]
D[расширение] -->|long-poll| C
D --> E[вкладка discord.com]
```
```tree
discord-web/
├── README.md
├── LIVE-CHECK.md
├── mcp-config.example.json
├── server/ # мост + MCP
├── extension/ # Chrome MV3, Load unpacked
├── tests/
└── bin/chrome-with-extension.sh
```
## Требования
- Node.js 22+
- Google Chrome
- Открытая вкладка `discord.com` / `ptb` / `canary`, вход вы делаете сами
## Установка
1. Клонируйте репозиторий и поставьте зависимости:
```sh
git clone https://github.com/VKirill/discord-web.git
cd discord-web
npm install
```
2. Запустите мост: `npm start`. При первом запуске появятся `.local/bridge.json` и `extension/local-config.js`. В лог идёт только `127.0.0.1:порт`, не ключ.
3. Chrome → `chrome://extensions` → Developer mode → Load unpacked → папка `extension/`.
4. После первого запуска моста нажмите Reload у расширения, чтобы подтянуть `local-config.js`.
5. В Chrome Web Store ничего публиковать не нужно.
Порт: `DISCORD_WEB_BRIDGE_PORT` или поле в `.local/bridge.json`. Слушает только `127.0.0.1`.
## Discord и вход
1. В том же профиле Chrome откройте Discord.
2. Войдите сами: логин, капча и 2FA расширение не заполняет.
3. Откройте канал или тред сервера.
4. Нет вкладки Discord — `tab_not_open`. Расширение вкладку само не создаёт.
## Список каналов на запись
1. Откройте настройки расширения.
2. Вставьте URL `https://discord.com/channels/<guildId>/<channelId>`.
3. Пустой список блокирует только отправку (`channel_not_allowed`). Читать можно без списка.
4. Список хранится в `chrome.storage.local` этого браузера.
## MCP
Образец: [mcp-config.example.json](mcp-config.example.json). `cwd` — корень клона, команда `node server/mcp.js` (или `npm run mcp`). Мост уже должен работать.
```json
{
"mcpServers": {
"discord-web": {
"command": "node",
"args": ["server/mcp.js"],
"cwd": "."
}
}
}
```
Проверка без отправки:
```json
{
"name": "discord_status",
"arguments": {
"url": "https://discord.com/channels/123456789012345678/987654321098765432"
}
}
```
Отправка, ответ, реакция, GIF — только канал из allowlist:
```json
{"name": "discord_send_message", "arguments": {"url": "https://discord.com/channels/…/…", "text": "тест из локального моста"}}
{"name": "discord_send_message", "arguments": {"url": "https://discord.com/channels/…/…", "text": "ответ", "replyTo": "111222333444555666"}}
{"name": "discord_add_reaction", "arguments": {"url": "https://discord.com/channels/…/…", "messageId": "111222333444555666", "emoji": "👍"}}
{"name": "discord_send_gif", "arguments": {"url": "https://discord.com/channels/…/…", "query": "cat"}}
```
Либо `guildId` + `channelId`. Для треда передайте `threadId` вместо `channelId`.
Перезагрузка unpacked-расширения (подхватывает файлы с диска):
```json
{"name": "discord_reload_extension", "arguments": {"extension": true, "bridge": false}}
```
`bridge: true` перезапускает LaunchAgent, если вы его ставили сами.
Чтение ленты (DOM вкладки, не архив Discord):
```json
{"name": "discord_list_guilds", "arguments": {}}
{"name": "discord_list_channels", "arguments": {"url": "https://discord.com/channels/…/…"}}
{"name": "discord_list_threads", "arguments": {"url": "https://discord.com/channels/…/…"}}
{"name": "discord_allowlist_add", "arguments": {"url": "https://discord.com/channels/…/…"}}
{"name": "discord_read_messages", "arguments": {"url": "https://discord.com/channels/…/…", "limit": 50, "after": "2026-09-01", "before": "2026-09-21", "scroll": true}}
{"name": "discord_collect_content", "arguments": {"url": "https://discord.com/channels/…/…", "scope": "visible", "includeThreads": true, "limit": 20, "maxChannels": 8, "maxThreads": 4}}
```
`discord_read_messages` возвращает `author`, `text`, `timestamp` (ISO), `day`. Дата в `after` / `before` режет уже загруженные сообщения. Если Discord не дорисовал историю в вкладке, мост её не достанет.
`discord_collect_content` обходит текстовые и форумные каналы сайдбара и треды. `scope: visible` — любой канал сервера; `scope: allowlist` — только список отправки. Лимит: до 12 каналов и 8 тредов на канал. Отправку не делает.
> [!TIP]
> После правки `extension/` вызовите `discord_reload_extension`. Иначе service worker держит старый код. GIF подтверждается только если в ленте появилось сообщение с video/img — не по клику на плитку категории.
## Ошибки
| Код | Что значит | Что сделать |
|---|---|---|
| `bridge_unavailable` | MCP не достучался до моста | `npm start` на этой машине |
| `extension_not_connected` | Расширение не опрашивало мост ≤ 70 с | Включите unpacked, откройте Discord, Reload после запуска моста |
| `invalid_target` | Неверный URL или id, либо `@me` | Канал `discord.com` / `ptb` / `canary`, id 15–21 цифра |
| `text_invalid` | Пусто или длиннее 2000 после trim | Сократите текст |
| `rate_limited` | Чаще 1 / 5 с, больше 10 / 10 мин или очередь > 20 | Подождите, одно сообщение |
| `timeout` | Нет ответа расширения | Проверьте вкладку и `discord_status` |
| `channel_not_allowed` | Канала нет в списке отправки | Для чтения список не нужен |
| `tab_not_open` | Нет вкладки Discord | Откройте discord.com сами |
| `not_logged_in` | Страница входа | Войдите в браузере |
| `channel_unavailable` | Канал не открылся | Откройте его вручную |
| `composer_not_found` | Нет поля Slate | Дождитесь загрузки чата |
| `no_send_permission` | Поле заблокировано | Проверьте право писать |
| `send_not_confirmed` | Сообщение не появилось за 15 с | Не считайте его отправленным |
| `message_not_found` | Цели нет в видимой ленте | Прокрутите канал |
| `action_not_found` | Нет кнопки Reply / реакция / GIF | Наведите курсор или откройте композер |
| `emoji_invalid` | Пустой emoji | Unicode или shortcode |
| `emoji_not_found` | Нет такого эмодзи в пикере | Например 👍 |
| `reaction_not_confirmed` | Реакция не появилась | Проверьте канал глазами |
| `gif_not_found` | Пикер без результата | Подождите сетку GIF и повторите |
| `internal_error` | Сбой моста или расширения | Повторите, затем перезапустите мост |
## Нельзя
- Discord Bot Token, user token, cookies и `discord.com/api/*`
- Сторонние discord-mcp и публикация этого расширения в Chrome Web Store
- Чтение storage Discord, заголовка `Authorization`, webpack-модулей страницы
- Открывать вкладку за пользователя, проходить логин, капчу и 2FA
- Личные сообщения и рассылки
- Чужой аккаунт
## Безопасность
- Ключ моста (≥32 байт hex) — в `.local/bridge.json` и `extension/local-config.js`. Оба в `.gitignore`. Значение не пишите в чаты и логи.
- Кроме `GET /v1/health` нужен заголовок `x-bridge-key` (`timingSafeEqual`). Неверный ключ → 401.
- `Origin` не из `chrome-extension://` → 403.
- Пароль Discord, cookies и токены сессии компонент не хранит и не запрашивает.
## Восстановление
1. Остановите `node server/bridge.js`.
2. Удалите `.local/bridge.json` и `extension/local-config.js`.
3. Снова `npm start` — ключ и порт создадутся заново.
4. Reload unpacked-расширения.
5. Список каналов в Chrome от удаления ключа не сбрасывается.
## Ограничения
Селекторы Discord Web (`[role="textbox"][data-slate-editor="true"]`, `[data-list-id="chat-messages"]`, пикер GIF) могут смениться без предупреждения. Тогда будет `composer_not_found`, `send_not_confirmed` или `gif_not_found`. Диагностика: `discord_status` и [LIVE-CHECK.md](LIVE-CHECK.md).
`discord_read_messages` не отдаёт вложения: у GIF в ленте часто пустой `text`.
## Проверка
```sh
npm test
```
Проходят `tests/server`, `tests/extension` и `tests/e2e` (успех `send_message` и ошибки `tab_not_open`, `not_logged_in`, `composer_not_found`, `extension_not_connected` через настоящий мост и MCP stdio).
Живая отправка — только вами, по [LIVE-CHECK.md](LIVE-CHECK.md).
## Лицензия
MIT. См. [LICENSE](LICENSE).
This server cannot be deployed
Maintenance
ActivityMaintained
ResponsivenessNo issues