Что умеет сервер, какие у инструментов поля и что означает каждый отказ. Страница для тех, у кого агент уже подключён: если ещё нет — инструкция по подключению здесь, она же нужна вашему клиенту, а не эта.
Сервер
Адрес
https://smmly.ru/api/mcp
Транспорт
Streamable HTTP. Ответ приходит обычным JSON, не потоком событий — клиентам, которые ждут SSE, ничего настраивать не нужно, они это понимают.
Состояние
Stateless: на каждый запрос поднимается свой сервер, сессий между вызовами нет. Ключ из заголовка превращается в пользователя ДО того, как появятся инструменты, поэтому инструменты всегда замкнуты на владельца ключа.
Авторизация
Authorization: Bearer smk_…
Когда нужен ключ
На любой метод, включая самый первый. Постить без каналов нечего, и пусть агент упрётся в понятный отказ при подключении, а не при первом посте.
GET
405 со ссылкой на инструкцию: эндпоинт отвечает только на POST. Открыли адрес в браузере и увидели ошибку — так и должно быть.
Потолок
60 запросов в минуту на ключ, общий с HTTP-API.
Инструментов
9 — список ниже
Версия протокола
Клиент новее нашего SDK подключится: незнакомую версию протокола мы снимаем с запроса и отвечаем на понятной. Протокол обратно совместим, а иначе агент не подключался бы вообще.
Десять инструментов
Посмотреть каналы, расставить им ярлыки, загрузить вложение, выбрать файл с устройства окном, опубликовать, поставить на время, узнать судьбу, найти свои посты, поправить и снять. Подключение и отключение каналов, выдача ключей, тариф, оплата и токены площадок наружу не выставлены намеренно.
smmly_channels
Показывает, куда этот ключ вообще может постить: id, площадка, название и ярлыки.
С него начинается любой разговор: пока агент не увидел ярлыки, адресовать пост ему нечем. Токенов площадок и настроек подключения инструмент не отдаёт.
Таблица листается вбок →
Поле
Тип
Нужно
Что значит
includeDisabled
boolean
нет
Показать и выключенные каналы. Нужен ровно в одном случае: канал перестал получать посты и надо понять почему.
Как зовут
«Покажи мои каналы» — и агент увидит список с ярлыками.
Что приходит
channels: по строке на канал — id, provider (telegram / vk / max), name, tags, disabled, needsReauth. Если каналов нет вовсе, вместо пустоты приходит подсказка со ссылкой на кабинет.
smmly_tag_channels
Вешает и снимает ярлыки — те самые, которыми потом адресуются посты.
Единственный инструмент, который что-то меняет в настройках, и это осознанно: адресовать посты ярлыком агент может, а завести ярлык раньше было можно только руками в кабинете. Наружу он ничего не открывает и откатывается тем же вызовом.
Таблица листается вбок →
Поле
Тип
Нужно
Что значит
channelIds
number[]
нет
Каким каналам менять — id из smmly_channels.
matchTag
string
нет
Взять все каналы, у которых уже есть этот ярлык. Либо он, либо channelIds — без обоих отказ.
add
string[]
нет
Какие ярлыки повесить, не трогая остальные.
remove
string[]
нет
Какие снять.
set
string[]
нет
ЗАМЕНИТЬ набор целиком — прежние ярлыки пропадут. Пустой список снимает все. Вместе с add и remove не передаётся.
Как зовут
«Повесь ярлык акции на оба канала кофейни» — агент возьмёт id из списка каналов и передаст их в add.
Что приходит
channels с новыми наборами ярлыков. Если нормализация что-то срезала — придёт note: не больше 6 ярлыков на канал и 24 символов в каждом, регистр не важен («Тест» и «тест» — один ярлык).
set — самое опасное поле сервера: «поставь ярлык акции» через set снимет с канала все остальные ярлыки, и пост, который ходил туда по ярлыку новости, тихо перестанет доходить. Обычный путь — add и remove.
smmly_upload_media
Кладёт файл в хранилище smmly и возвращает готовый блок media для поста.
Для файла, которого нет в интернете: скриншот, только что нарисованная картинка, файл на диске. У кого ссылка есть — тому инструмент не нужен, ссылка кладётся прямо в mediaUrls поста.
Таблица листается вбок →
Поле
Тип
Нужно
Что значит
data
string
да
Содержимое файла в base64. Префикс data:…;base64, и переносы строк снимаются сами. Принимаем jpg, png, gif, webp, mp4, mov, webm, pdf.
name
string
нет
Имя файла — только подпись. На путь хранения не влияет, тип определяем по содержимому, а не по расширению.
Как зовут
«Вот скриншот, приложи его к посту» — агент закодирует файл и передаст в data.
Что приходит
media из одного элемента (type и rel) плюс размер. Этот массив кладётся в smmly_post полем media как есть, ничего в нём менять не нужно.
smmly_upload_widget
Показывает прямо в переписке окно «выберите файл или перетащите сюда» — файл уходит в smmly с вашего устройства.
Файл, который лежит у ВАС на диске, агент в base64 не переложит: содержимое шло бы через его же ответ, а туда не влезают даже сотни килобайт. Окно обходит это стороной — файл летит из браузера прямо к нам, минуя агента, и он получает назад только готовый блок media. Работает там, где чат умеет показывать окна приложений (MCP Apps); где не умеет — остаётся smmly_upload_media.
Как зовут
«Приложи картинку с моего компьютера» — агент откроет окно, вы выберете файл, дальше он сам предложит поставить пост.
Что приходит
Само окно. Загруженное оно передаёт агенту готовым блоком media — там же, в переписке. Рядом живут два служебных инструмента: один выдаёт окну пропуск на загрузку (агенту он не показывается), второй перечисляет загруженное за этот разговор, если чат потерял контекст.
smmly_post
Публикует сейчас во все каналы, попавшие в отбор. Один вызов — один пост во все выбранные каналы сразу.
Основной инструмент. Пост встаёт в ту же очередь, что и написанный руками в кабинете, и уходит в течение минуты.
«Запости это в новости» — агент возьмёт ярлык и текст.
Что приходит
groupId и по строке на канал. По groupId дальше смотрится судьба поста, им же адресуются правка и снятие.
smmly_schedule_post
Тот же пост, но с датой публикации.
Отдельный инструмент, а не флаг: агент, у которого «опубликовать» и «отложить» — одно действие с необязательным полем, регулярно теряет это поле и публикует сейчас.
Когда публиковать. ISO 8601 ОБЯЗАТЕЛЬНО со смещением зоны: 2026-09-06T09:00:00+03:00 — это 9 утра по Москве. Время в прошлом = публикация сейчас.
Как зовут
«Поставь этот пост на завтра на девять утра» — агент обязан подставить смещение вашей зоны, а не только время.
Что приходит
То же, что у публикации: groupId и строки по каналам, только state будет очередью с временем.
smmly_post_status
Состояние поста по каждому каналу: очередь, публикация, ошибка площадки, ссылка на опубликованное.
Публикация возвращает «принято», а не «опубликовано». Разница в минуту и в отказе площадки — и узнать про отказ можно только здесь.
Таблица листается вбок →
Поле
Тип
Нужно
Что значит
groupId
string
да
Из ответа smmly_post или smmly_schedule_post. Формат — uuid, иначе отказ.
Как зовут
«Пост-то вышел?» — агент сходит с groupId и прочитает состояния.
Что приходит
По строке на канал: state, publishAt, publishedAt, externalUrl, error, число попыток.
smmly_posts
Список постов этого ключа — что стоит в отложке и что уже ушло.
Вход в правку. Между сессиями агент groupId не помнит, а правка и снятие адресуются именно им — без этого инструмента поправить вчерашнюю опечатку было бы нечем.
Таблица листается вбок →
Поле
Тип
Нужно
Что значит
scope
scheduled | published | all
нет
По умолчанию scheduled — ещё не ушедшее, ближайшее сверху. published — опубликованное, свежее сверху.
limit
number
нет
Сколько постов вернуть: от 1 до 50, по умолчанию 20.
Как зовут
«Что у меня стоит на этой неделе?»
Что приходит
Замыслы: groupId, состояние (mixed, если каналы разошлись), время, обрезанный текст и строки по каналам. Полный текст — в самом посте, в списке он подрезан.
smmly_edit_post
Меняет уже созданный пост во всех каналах замысла сразу.
Агент, который умеет поставить пост на завтра, но не умеет поправить в нём опечатку, отправляет человека доделывать в кабинет — то есть не работает.
Таблица листается вбок →
Поле
Тип
Нужно
Что значит
groupId
string
да
Какой замысел правим — из smmly_posts или из ответа публикации.
text
string
нет
Новый текст на все каналы.
textByProvider
объект
нет
Новый текст под площадку — ключи default, telegram, vk, max.
format
plain | html
нет
Разметка нового текста. Не передали — останется прежняя.
title
string
нет
Заголовок. Пустая строка снимает его.
mediaUrls
string[]
нет
Заменить вложения ссылками. Только у неушедших постов.
media
объект[]
нет
Заменить вложения загруженными. Только у неушедших; пустой список снимает вложения.
buttons
объект[]
нет
Заменить кнопки. Пустой список снимает их.
firstComment
string
нет
Второе сообщение. У опубликованного поста не меняется — его правят прямо в канале.
deleteAfterHours
number
нет
Поставить или сдвинуть автоснос: через столько часов после публикации пост уберут из канала. От минуты до 47 часов, дробные можно: сообщение старше двух суток телеграм снять уже не даёт. 0 или null снимают срок — пост остаётся навсегда. У уже опубликованного срок считается от публикации, а не от правки.
publishAt
string
нет
Перенести отложенный пост. Тот же ISO 8601 со смещением. Время в прошлом = отправить сейчас.
retry
boolean
нет
Вернуть в очередь упавшие при отправке — новая попытка с исправленным текстом. Ушедших и черновиков не касается.
Как зовут
«Поправь в завтрашнем посте цену на 450» — агент найдёт замысел через smmly_posts и передаст только text.
Что приходит
Построчно по каналам: ok или error у каждого. Часть могла принять правку, часть отказать — при частичном успехе приходит note «приняли столько-то из стольких-то».
smmly_cancel_post
Убирает пост целиком, по всем каналам замысла.
«Убери этот пост» с равным успехом значит «сними с очереди» и «сотри из канала». Первое обратимо, второе нет — поэтому по умолчанию делается первое.
Таблица листается вбок →
Поле
Тип
Нужно
Что значит
groupId
string
да
Какой замысел снимаем.
fromChannel
boolean
нет
Стереть пост и из самого канала, а не только из журнала smmly. НЕОБРАТИМО, по умолчанию выключено.
Как зовут
«Отмени завтрашний пост» — снимется с очереди и не уйдёт.
Что приходит
Построчно по каналам, как у правки: снято с очереди или удалено из сети.
Уже опубликованное по умолчанию остаётся в канале: из журнала smmly пропадёт, из канала — нет. Сносить опубликованное умеет только телеграм и только сообщения не старше двух суток.
Общий набор полей публикации
Одинаков у smmly_post иsmmly_schedule_post: у второго к нему добавляется только время. Нужен текст (одним из двух полей) и адресация (одним из трёх) — остальное по желанию.
Таблица листается вбок →
Поле
Тип
Нужно
Что значит
text
string
одно из
Текст поста, один на все площадки. Нужен либо он, либо textByProvider — без текста постить нечего.
textByProvider
объект
одно из
Свой текст под площадку, когда один на всех не годится: ключи default, telegram, vk, max.
tags
string[]
одно из
ЯРЛЫКИ каналов — основной способ выбора. Пост уйдёт во все живые каналы, у которых есть любой из перечисленных.
providers
string[]
одно из
Выбор площадкой целиком: telegram, vk, max — все телеграм-каналы, все сообщества ВК.
channelIds
number[]
одно из
Точечный выбор по id. Нужен редко. Хотя бы одно из трёх полей адресации обязано быть — иначе отказ.
format
plain | html
нет
Разметка. html понимают телеграм и MAX; ВК получит тот же текст без тегов. По умолчанию plain.
title
string
нет
Заголовок — там, где площадка его различает. Сегодня не различает ни одна из подключаемых: поле в контракте есть, но ни на что не влияет.
mediaUrls
string[]
нет
Вложения публичными ссылками, до 10. Файлы мы скачиваем сами.
media
объект[]
нет
Вложения, загруженные через smmly_upload_media или окно smmly_upload_widget — массив из ответа как есть. Складывается с mediaUrls, вместе не больше 10.
buttons
объект[]
нет
Кнопки-ссылки под постом: text и url. Вешает только телеграм, до 8, по одной в ряд; ВК и MAX получат пост без них.
firstComment
string
нет
Второе сообщение ответом на пост. Умеет только телеграм, остальные молча его не получат.
deleteAfterHours
number
нет
Снести пост из канала через столько часов после публикации — для рекламы, купленной на срок. Дробные можно: 0.5 — полчаса, от минуты до 47 часов. Не передали — пост висит в канале вечно, как обычно. Умеет только телеграм и только пока сообщению меньше двух суток: канал ВК, MAX или X с этим полем откажется принять пост.
idempotencyKey
string
нет
Вечный ключ повтора: вызовы с одним ключом создают пост один раз, повторный вернёт duplicate и groupId первого. Берите id события («дивиденды SBER за 2026»), а не случайную строку.
Как адресуются каналы
Три способа сказать «куда», и они складываются: канал попадёт в отбор, если подошёл хотя бы по одному. Хотя бы один способ обязан быть указан.
Ярлык — основной путь
Ярлык вешается на канал в кабинете или черезsmmly_tag_channels, и постом адресуется он, а не номер. Смысл простой: канал, подключённый завтра и помеченный тем же ярлыком, подхватится сам — переписывать промпт агенту не придётся. Ярлыки нечувствительны к регистру, до 24 символов, не больше 6 на канал.
Площадка целиком
Все телеграм-каналы, все сообщества ВК, все чаты MAX. Грубо, но иногда ровно то, что нужно: «продублируй во все телеграмы».
Список id — запасной
Точечно, когда ярлыка нет. Хрупко: id приходится держать в промпте, а канал могут удалить — тогда придёт отказ со списком ненайденных.
Время публикации
publishAt — ISO 8601 обязательно со смещением зоны:2026-09-06T09:00:00+03:00 — это девять утра по Москве.
Без смещения то же время читается как всемирное, и пост уходит на три часа раньше московского — в шесть утра. Это самая частая и самая дорогая ошибка при работе через агента: она не отбивается отказом, пост честно выходит, просто не тогда. Проверять стоит именно хвост строки.
Время в прошлом ошибкой не считается — отправщик возьмёт такой пост первым же тиком. Перенести уже поставленный пост можно тем же полем черезsmmly_edit_post.
Вложения
Три пути: публичная ссылка в mediaUrls — если файл уже в интернете; загрузка через smmly_upload_media — если файл есть у агента; окно smmly_upload_widget — если файл лежит у вас на устройстве. Вместе не больше десяти на пост.
Файл передаётся в base64, целиком, одним сообщением протокола. Потолок на вызов — 30 МБ закодированного текста, то есть примерно 22 МБ самого файла. Больше — отказ, и это не наша жадность: тело запроса лежит в памяти целиком, а веб-сервер режет запросы на 55 МБ.
Дальше действуют обычные пределы хранилища: картинка до 10 МБ, видео и pdf до 50. Через base64 до пятидесяти вы не дотянетесь, а вот через окноsmmly_upload_widget — да: файл там идёт из браузера прямо в хранилище, и потолок у него только хранилищный. Где окон нет, большое видео придётся выложить по ссылке и передатьmediaUrls.
Публичной ссылки на загруженный файл не появляется, и она не нужна: площадке файл уходит содержимым, а не адресом. Инструмент возвращает внутренний блок media — его переносят в пост как есть.
Загруженное и никуда не приложенное живёт сутки и удаляется. Попавшее в пост живёт вместе с постом — включая отложенный: ссылку на файл он ставит сразу, поэтому вложение у него не пропадёт. А вот загрузить файл сегодня, чтобы приложить его к посту завтра, нельзя.
Тип файла определяется по содержимому, а не по имени: принимаем jpg, png, gif, webp, mp4, mov, webm, pdf. Имя — только подпись.
Правка и снятие
И то и другое адресуется groupId — идентификатором замысла, а не отдельного поста в канале. Меняется сразу всё, что из этого замысла ушло.
Ответ построчный. У каждого канала своиok или error: телеграм правку принял, ВК отказал — и это нормальный, ожидаемый исход, а не сбой. При частичном успехе приходит отдельная строка «приняли столько-то из стольких-то» именно затем, чтобы агент не отчитался об успехе за половину сделанного.
Неушедший пост правится целиком: текст, разметка, вложения, кнопки, время публикации.
Опубликованный переписывается прямо в канале, и там меняется только текст — вложения и второе сообщение площадка подменить не даёт. Переписывать опубликованное умеет один телеграм; ВК и MAX ответят отказом, и там пост придётся снять и опубликовать заново.
Телеграм не даёт боту трогать сообщения старше двух суток — ни править, ни удалять. Позавчерашнюю опечатку исправить уже нельзя.
Пост, который отправщик уже взял в работу, не правится и не снимается: попробуйте через минуту, когда он опубликуется.
Снятие по умолчанию не трогает канал: пост уходит из журнала smmly и с очереди, но опубликованное остаётся на месте. Стереть из канала — отдельный явный флаг, необратимо.
Ограничения
Повтор вызова: защита есть, но короткая
Одинаковый вызов в те же каналы в пределах пяти минутвторого поста не создаёт: вернётся duplicate и группа уже созданного. Это ловит ровно один случай — вызов отвалился по таймауту уже ПОСЛЕ создания поста, и агент честно повторил его теми же аргументами.
Дальше окно кончается: тот же текст в те же каналы через час — не ретрай, а осознанный повтор, и он опубликуется. «Одинаковый» тоже понимается буквально: агент, который перед повтором перегенерировал текст, отправит уже другой пост. Когда задвоение недопустимо в принципе — передавайтеidempotencyKey с идентификатором события: он вечен. В кроне вместо этого проще взятьHTTP-вход с паройsource + externalKey.
Подключить или отключить канал нельзя. Это живые шаги в чужих интерфейсах — диплинк в телеграме, подключение сообщества во ВКонтакте, добавление бота руками в MAX. Только кабинет.
Выдать или отозвать ключ нельзя, как и посмотреть тариф, оплатить подписку или получить токены площадок. Ключ даёт право вести каналы, а не управлять аккаунтом.
Черновик из агента положить нельзя. Публикация всегда встаёт в очередь; поле состояния наружу не выставлено. Без действующей подписки это означает отказ, а не «сохранили на потом».
Сессий нет. Сервер не помнит предыдущий вызов, иgroupId между разговорами не сохраняется — искать пост придётся через smmly_posts.
Заголовок поста ничего не делает. Поле в схеме есть, но ни одна из подключаемых площадок отдельного заголовка не различает. Всё решает текст.
Отказ площадки приходит не сразу. Инструмент публикации возвращает «принято в очередь». Что пост реально вышел — видно только в его состоянии.
Что означает отказ
Строки ниже — те самые, что агент покажет вам в чате.
Нужен ключ: заголовок Authorization: Bearer smk_…
Клиент не передал ключ вовсе. Проверьте конфиг клиента: у большинства это блок headers рядом с адресом сервера.
Ключ недействителен
Ключа нет в базе или он удалён в кабинете. «Нет такого» и «отозван» снаружи не различаются намеренно. Выпустите новый на странице ключей.
Подтвердите почту аккаунта
Тот же гейт, что и в кабинете: неподтверждённый ящик не постит. Ключ его не обходит.
Нет действующей подписки
Постановка в очередь закрыта. Из MCP это тупик: черновики агенту наружу не выставлены, так что пост придётся положить в кабинете или продлить подписку.
Слишком часто: не больше 60 запросов в минуту на ключ
Потолок на ключ, общий с HTTP-API. Обычному разговору его не достать; упирается обычно цикл, который агент запустил сам.
Не указано, куда постить
Ни ярлыков, ни площадок, ни id. Агент не сходил в smmly_channels — попросите его сначала показать каналы.
Под этот отбор не нашлось ни одного живого канала
Ярлык написан не так, как в кабинете, либо все каналы с ним выключены. Список ярлыков — в smmly_channels.
Канал не найден, удалён или выключен
Постинг по id в канал, которого больше нет. В ответе придёт список missing. Ещё одна причина адресовать ярлыками.
Пост не проходит по площадкам
Проверка длины, вложений и кнопок идёт по ВСЕМ выбранным каналам ДО записи, и не прошло по одной — не создаём ничего. В issues лежит по строке на канал, что именно не так.
Не разобрали publishAt
Время не в ISO 8601. Нужен формат со смещением зоны: 2026-09-06T09:00:00+03:00.
Файл слишком большой для загрузки через MCP
Не пролезает одним сообщением протокола. Выложите файл по публичной ссылке и передайте mediaUrls.
Внутренняя ошибка smmly
Наша поломка, мы её уже видим. Подробности агенту не отдаются намеренно: непойманное исключение вынесло бы наружу текст запроса к базе целиком, а агент показал бы его в чате.
Частые затыки
Агент говорит «пост опубликован», а в канале пусто
Инструмент публикации возвращает «принято в очередь», а не «вышло», и агент честно пересказывает этот ответ. Отправка идёт в течение минуты, а отказ площадки виден только в состоянии поста. Попросите агента проверить через smmly_post_status по groupId — там будет и текст ошибки, и ссылка на опубликованное.
Пост ушёл на три часа раньше, чем я просил
Классика: publishAt без смещения зоны. Время без хвоста +03:00 читается как всемирное, и «девять утра» превращается в шесть по Москве. Правильный вид — 2026-09-06T09:00:00+03:00. Если агент уже поставил пост криво, время переносится через smmly_edit_post тем же полем.
Агент повторил вызов и создал два поста
Одинаковый запрос в те же каналы в пределах пяти минут повтором не считается — вернётся duplicate и второй пост не создастся. Но окно короткое, а «одинаковый» значит побайтово: агент, перегенерировавший текст, отправит уже другой пост. Когда задвоение недопустимо в принципе, передавайте idempotencyKey — он вечный.
Почему агент не может подключить мне канал?
Наружу это не выставлено, и не по недосмотру. Подключение канала — живые шаги в чужих интерфейсах: диплинк в телеграме, вход во ВКонтакте через браузер, добавление бота руками в MAX. Ключ даёт право вести каналы, а не управлять аккаунтом; выдача ключей, тариф, оплата и токены площадок закрыты по той же причине.
Агент снял пост, а в канале он остался
Так и задумано. По умолчанию снятие убирает пост из журнала smmly и с очереди, но опубликованное из канала не стирает — это необратимо, а «убери этот пост» слишком часто значит «сними с очереди». Стереть из канала можно, но агент должен передать fromChannel явно, и лучше, чтобы он у вас перед этим спросил.
Клиент не подключается: сервер отвечает 405
405 приходит на GET — например, если адрес открыли в браузере. MCP-эндпоинт отвечает только на POST. Если же приходит 401 при подключении, дело в ключе: он обязателен на любой метод, включая самый первый.