Channels в Claude Code: подключаем Telegram, Discord и вебхуки
Channels превращают Claude Code в хаб для внешних событий. Показываю, как подключить Telegram, Discord и написать свой webhook-канал за 5 минут.
TL;DR: Channels в Claude Code позволяют получать сообщения из Telegram, Discord и любых вебхуков прямо в терминальную сессию. Claude читает событие, реагирует и может ответить обратно. Показываю, как всё настроить за 5 минут.
Channels появились 20 марта 2026 года в research preview. По сути это MCP-серверы, которые работают как мост между внешним миром и твоей сессией Claude Code. Telegram, Discord, вебхуки от CI, алерты мониторинга, что угодно.
Что понадобится
- Claude Code v2.1.80+ (проверь
claude --version) - Аккаунт claude.ai (API-ключи и Console не поддерживаются)
- Bun (предустановленные плагины написаны на нём, проверь
bun --version) - Для Telegram: бот через @BotFather
- Для Discord: приложение в Developer Portal
- Team/Enterprise: админ должен включить
channelsEnabledв managed settings
Шаг 1. Проверяем на fakechat
Перед тем как подключать настоящие платформы, есть смысл потестировать механику на fakechat. Это демо-канал от Anthropic, который поднимает чат-UI на localhost. Никаких внешних сервисов, токенов, ботов.
Устанавливаем плагин:
/plugin install fakechat@claude-plugins-official
Запускаем Claude Code с каналом:
claude --channels plugin:fakechat@claude-plugins-official
Открывай http://localhost:8787 в браузере. Пишешь сообщение в веб-интерфейсе, оно прилетает в терминальную сессию Claude Code. Claude отвечает, ответ появляется обратно в браузере.
Если всё работает, переходим к настоящим платформам.
Шаг 2. Подключаем Telegram
Telegram, наверное, самый практичный вариант.
Создаём бота
- Открой @BotFather в Telegram
- Отправь
/newbot - Придумай имя и username (должен заканчиваться на
bot) - Скопируй токен
Ставим плагин и конфигурируем
/plugin install telegram@claude-plugins-official
/telegram:configure <твой-токен>
Запускаем с каналом
claude --channels plugin:telegram@claude-plugins-official
Привязываем аккаунт
Это важный момент. Channels используют allowlist отправителей, и пока ты не пройдёшь pairing, бот будет игнорировать все сообщения.
- Найди своего бота в Telegram и отправь ему любое сообщение
- Бот ответит кодом сопряжения
- В терминале Claude Code выполни:
/telegram:access pair <код> - Зафиксируй политику:
/telegram:access policy allowlist
Готово. Теперь пишешь боту «проверь, проходят ли тесты на feature-ветке», и Claude Code в терминале начинает работать. Ответ прилетает обратно в Telegram.
Шаг 3. Подключаем Discord
Процесс похож на Telegram, но чуть больше настроек на стороне Discord.
Создаём бота
- Зайди в Discord Developer Portal
- Нажми New Application, задай имя
- Перейди в Bot → Reset Token → скопируй токен
- Там же включи Message Content Intent в разделе Privileged Gateway Intents (без этого бот не увидит текст сообщений)
- Пригласи бота на сервер с правами: просмотр каналов, отправка сообщений, чтение истории, прикрепление файлов
Ставим и запускаем
/plugin install discord@claude-plugins-official
/discord:configure <твой-токен>
claude --channels plugin:discord@claude-plugins-official
Привязка аккаунта
Напиши боту в личку, получи код сопряжения, подтверди в терминале:
/discord:access pair <код>
Можно подключить оба канала одновременно, перечислив плагины через пробел:
claude --channels plugin:telegram@claude-plugins-official plugin:discord@claude-plugins-official
Шаг 4. Пишем свой webhook-канал
Telegram и Discord покрывают базовые сценарии, но интереснее другое: можно написать свой канал для чего угодно. CI упал? Мониторинг кричит? Grafana шлёт алерт? Всё это можно направить прямо в Claude Code.
Покажу на примере простого HTTP-приёмника, который принимает POST-запросы и пробрасывает их в сессию.
Создаём проект
mkdir webhook-channel && cd webhook-channel
bun add @modelcontextprotocol/sdk
Пишем сервер
Создай файл webhook.ts:
#!/usr/bin/env bun
import { Server } from '@modelcontextprotocol/sdk/server/index.js'
import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js'
const mcp = new Server(
{ name: 'webhook', version: '0.0.1' },
{
capabilities: { experimental: { 'claude/channel': {} } },
instructions: 'Events from the webhook channel arrive as <channel source="webhook" ...>. They are one-way: read them and act, no reply expected.',
},
)
await mcp.connect(new StdioServerTransport())
Bun.serve({
port: 8788,
hostname: '127.0.0.1',
async fetch(req) {
const body = await req.text()
await mcp.notification({
method: 'notifications/claude/channel',
params: {
content: body,
meta: { path: new URL(req.url).pathname, method: req.method },
},
})
return new Response('ok')
},
})
На что обратить внимание:
capabilities: { experimental: { 'claude/channel': {} } }— именно эта строка делает MCP-сервер каналом. Без неё Claude Code не зарегистрирует listenerinstructions— попадает в системный промпт Claude. Объясни, чего ожидать от событий и нужно ли отвечатьhostname: '127.0.0.1'— слушаем только на localhost, ничего наружу не торчит
Регистрируем в .mcp.json
{
"mcpServers": {
"webhook": { "command": "bun", "args": ["./webhook.ts"] }
}
}
Тестируем
Кастомные каналы пока не в официальном allowlist, поэтому нужен флаг разработчика:
claude --dangerously-load-development-channels server:webhook
Claude Code прочитает .mcp.json, запустит webhook.ts как подпроцесс, и HTTP-сервер поднимется автоматически. В другом терминале:
curl -X POST localhost:8788 -d "build failed on main: https://ci.example.com/run/1234"
В сессии Claude Code появится событие:
<channel source="webhook" path="/" method="POST">
build failed on main: https://ci.example.com/run/1234
</channel>
Claude прочитает сообщение и начнёт разбираться. Если в тексте упомянута ссылка на CI, он может сам полезть смотреть логи.
Шаг 5. Добавляем двустороннюю связь
Односторонний канал уже полезен для алертов, но если хочешь, чтобы Claude мог отвечать (например, в чат-бридже), нужно добавить reply tool. Это стандартный MCP-инструмент, ничего специфичного для каналов.
Дополним webhook.ts:
#!/usr/bin/env bun
import { Server } from '@modelcontextprotocol/sdk/server/index.js'
import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js'
import { ListToolsRequestSchema, CallToolRequestSchema } from '@modelcontextprotocol/sdk/types.js'
const mcp = new Server(
{ name: 'webhook', version: '0.0.1' },
{
capabilities: {
experimental: { 'claude/channel': {} },
tools: {}, // включаем обнаружение инструментов
},
instructions: 'Messages arrive as <channel source="webhook" chat_id="...">. Reply with the reply tool, passing the chat_id from the tag.',
},
)
// Регистрируем reply tool
mcp.setRequestHandler(ListToolsRequestSchema, async () => ({
tools: [{
name: 'reply',
description: 'Send a message back over this channel',
inputSchema: {
type: 'object',
properties: {
chat_id: { type: 'string', description: 'The conversation to reply in' },
text: { type: 'string', description: 'The message to send' },
},
required: ['chat_id', 'text'],
},
}],
}))
// Обрабатываем вызовы
mcp.setRequestHandler(CallToolRequestSchema, async req => {
if (req.params.name === 'reply') {
const { chat_id, text } = req.params.arguments as { chat_id: string; text: string }
// Тут вызов API твоей платформы
console.error(`Reply to ${chat_id}: ${text}`)
return { content: [{ type: 'text', text: 'sent' }] }
}
throw new Error(`unknown tool: ${req.params.name}`)
})
await mcp.connect(new StdioServerTransport())
let nextId = 1
Bun.serve({
port: 8788,
hostname: '127.0.0.1',
async fetch(req) {
const body = await req.text()
const chat_id = String(nextId++)
await mcp.notification({
method: 'notifications/claude/channel',
params: {
content: body,
meta: { chat_id, path: new URL(req.url).pathname, method: req.method },
},
})
return new Response('ok')
},
})
Что добавилось:
tools: {}в capabilities, чтобы Claude Code обнаружил инструменты сервераListToolsRequestSchemaописывает reply tool со схемой параметровCallToolRequestSchemaобрабатывает вызовы (замениconsole.errorна реальный API отправки)chat_idв meta, чтобы Claude знал, куда отвечать
Шаг 6. Защита от prompt injection
Открытый канал без фильтрации отправителей — это вектор prompt injection. Любой, кто может отправить POST на твой endpoint, может подсунуть текст прямо в контекст Claude.
Проверяй отправителя до вызова mcp.notification():
const allowed = new Set(loadAllowlist()) // из access.json или аналога
// Внутри обработчика сообщений, ДО отправки:
if (!allowed.has(message.from.id)) {
return // игнорируем молча
}
await mcp.notification({ ... })
message.from.id), а не по ID чата (message.chat.id). В групповых чатах это разные вещи, и фильтрация по комнате позволит любому участнику группы инжектить сообщения в сессию.Официальные плагины Telegram и Discord используют именно этот подход: allowlist по sender ID + pairing flow для первичной привязки.
Результат
Если всё настроил правильно, у тебя теперь есть Telegram/Discord-бот для управления Claude Code с телефона, webhook-приёмник для алертов от CI и мониторинга, и опционально двусторонний канал, через который Claude может отвечать.
В терминале ты видишь входящее сообщение и инструмент, который Claude вызвал для ответа. Сам текст ответа появляется на платформе отправителя, в терминале только подтверждение «sent».
События приходят только пока сессия открыта. Для always-on режима запускай Claude Code в фоновом процессе или persistent-терминале (tmux, screen). Но будь осторожен с --dangerously-skip-permissions — он отключает все подтверждения.
Частые ошибки
Если видишь «blocked by org policy», значит ты в Team или Enterprise организации, а админ не включил channelsEnabled. Включается в claude.ai → Admin settings → Claude Code → Channels.
Канал регистрируется, но сообщения не приходят? Проверь, что ты указал сервер в --channels. Запись в .mcp.json сама по себе не активирует канал, нужно явно передать имя при запуске.
Кастомный канал не загружается. Во время research preview кастомные каналы не в allowlist. Используй --dangerously-load-development-channels server:<имя>. Флаг работает только для конкретного entry, а не для всех каналов в сессии.
Telegram-бот игнорирует сообщения? Скорее всего не прошёл pairing. Напиши боту, получи код, подтверди через /telegram:access pair <код>.
FAQ
Чем Channels отличается от Remote Control?
Remote Control синхронизирует терминальную сессию с claude.ai или мобильным приложением. Ты видишь полный UI и можешь печатать, как будто сидишь за терминалом. Channels работают иначе: ты отправляешь сообщение, Claude обрабатывает его и отвечает. Это модель передачи сообщений, а не screen-sharing.
Чем Channels отличается от Dispatch?
Dispatch — фича Cowork для не-разработчиков. Она связывает мобильное приложение Claude с десктопной сессией для задач вроде организации файлов и создания контента. Channels — для разработчиков, построены на MCP и заточены под event-driven воркфлоу.
Можно ли подключить Slack или WhatsApp?
Официальных плагинов пока нет (март 2026), но протокол открытый. Любой MCP-сервер с claude/channel capability может стать каналом. Нужно написать свой плагин по схеме из шага 4 и запустить с --dangerously-load-development-channels.
Что происходит с сообщениями, когда сессия закрыта?
Они не доставляются. Channels работают только при активной сессии. Для постоянной доступности запускай Claude Code в фоновом процессе.
Безопасно ли давать внешним системам доступ к Claude Code?
При правильной настройке — да. Sender allowlist отсекает всех, кого ты не добавил. Два уровня контроля: на уровне сессии (--channels) и на уровне организации (channelsEnabled). Кастомные каналы слушают только на localhost.
Что ещё почитать
- 10 воркфлоу Claude Code на каждый день — Channels отлично вписываются как ещё один рабочий паттерн
- Claude Dispatch: управляй Cowork с телефона — альтернативный подход к мобильному управлению Claude
- Запланированные задачи в Claude Code: /loop и cron — если нужна реакция по расписанию, а не по событиям
- Claude Code: как сэкономить токены на MCP с Tool Search — полезно, если подключаешь несколько MCP-серверов