ads-telegram-mcp
# ads-telegram-mcp
MCP-сервер для работы с Telegram Ads из ИИ-ассистентов.
Позволяет просматривать аккаунты и объявления, запрашивать статистику,
получать месячные отчёты, создавать объявления и загружать медиа.
Сервер работает локально через **stdio**. Доступны **11 инструментов**:
9 для чтения данных и 2 для создания объявлений и загрузки медиа.
## Установка
Понадобятся:
- Python **3.15 или новее**;
- [uv](https://docs.astral.sh/uv/);
- API-токен из [настроек Telegram Ads](https://ads.telegram.org/account/api);
- MCP-клиент с поддержкой stdio.
Для загрузки видео также нужен `ffprobe` из [FFmpeg](https://ffmpeg.org/download.html),
доступный в `PATH` процесса сервера. Для текстовых объявлений и фото он не требуется.
Скачайте исходники проекта, перейдите в его каталог и установите зависимости:
```sh
cd ads-telegram-mcp
uv sync --locked
cp .env.example .env
```
Откройте `.env` и укажите токен:
```dotenv
TELEGRAM_ADS_TOKEN=your_api_token
```
## Настройки
| Переменная | Назначение |
|---|---|
| `TELEGRAM_ADS_TOKEN` | API-токен для доступа к Telegram Ads. Нужен для запросов данных. |
| `TELEGRAM_ADS_ACCOUNT_ID` | Необязательный ID связанного аккаунта, который будет использоваться по умолчанию. |
Сервер читает `.env` из корня проекта независимо от каталога запуска.
Переменные окружения имеют приоритет над `.env`, включая пустые значения.
Токен можно задать в окружении MCP-клиента вместо файла. `.env` исключён из Git.
При выборе аккаунта действует следующий порядок:
1. `account_id`, переданный инструменту.
2. `TELEGRAM_ADS_ACCOUNT_ID`, если он задан.
3. Аккаунт, которому принадлежит токен.
`get_current_account` всегда возвращает владельца токена.
`list_related_accounts` получает связанные аккаунты владельца токена,
а `get_accounts_by_id` использует переданный список ID.
Чтобы обратиться к владельцу токена при заданном аккаунте по умолчанию,
передайте его ID явно.
## Подключение к MCP-клиенту
Для клиентов с конфигурацией `mcpServers`:
```json
{
"mcpServers": {
"telegram-ads": {
"command": "uv",
"args": [
"--directory", "/absolute/path/ads-telegram-mcp",
"run", "--locked", "python", "-m", "ads_telegram_mcp"
]
}
}
}
```
Замените `/absolute/path/ads-telegram-mcp` на полный путь к проекту.
Если MCP-клиент не находит `uv`, укажите полный путь к исполняемому файлу
в `command`. Токен будет загружен из `.env`.
Для Codex добавьте в `~/.codex/config.toml`:
```toml
[mcp_servers.telegram_ads]
command = "uv"
args = ["--directory", "/absolute/path/ads-telegram-mcp", "run", "--locked", "python", "-m", "ads_telegram_mcp"]
tool_timeout_sec = 300
```
После сохранения настроек перезапустите MCP-подключение.
Для ручного запуска из каталога проекта:
```sh
uv run --locked python -m ads_telegram_mcp
```
При ручном запуске сервер ожидает сообщения MCP на stdin.
Отсутствие приглашения или вывода в терминале — нормальное поведение.
## Использование
После подключения попросите ассистента, например:
- «Покажи баланс и валюту текущего аккаунта Telegram Ads».
- «Выведи первые 20 объявлений и их статусы».
- «Получи дневную статистику аккаунта за прошлую неделю и посчитай расход».
- «Покажи итоги за прошлую неделю отдельно по каждому объявлению».
- «Покажи месячный отчёт по объявлениям за октябрь 2026 года».
- «Проверь параметры двух новых объявлений через dry_run».
- «Загрузи фото и создай с ним объявление без бюджета, на паузе».
Ассистент выберет инструменты и передаст им параметры.
Доступ к данным определяется правами API-токена.
## Доступные инструменты
| Инструмент | Что возвращает |
|---|---|
| `get_current_account` | Аккаунт владельца токена, его балансы и валюту. |
| `list_related_accounts` | Одну страницу связанных аккаунтов. |
| `get_accounts_by_id` | Данные аккаунтов по списку ID. |
| `list_ads` | Одну страницу объявлений выбранного аккаунта. |
| `get_ads_by_id` | Объявления по списку ID, при необходимости с таргетингом. |
| `get_account_stats` | Статистику аккаунта за период с разбивкой по интервалам. |
| `get_ad_stats` | Статистику одного объявления за период с разбивкой по интервалам. |
| `get_ads_stats` | Итоги за произвольный период по выбранным или всем объявлениям; при необходимости — разбивку по интервалам. |
| `get_account_report` | Месячные итоги по активным объявлениям аккаунта. |
| `create_ads` | Результаты создания одного или нескольких объявлений либо локальной проверки через `dry_run`. |
| `upload_media` | ID загруженного фото, видео или изображения сайта и проверенные метаданные файла. |
### Создание объявлений
`create_ads` принимает непустой список `ads`. Общие параметры:
- `account_id` — аккаунт по общим правилам настройки;
- `dry_run=false` — при `true` проверяются только входные данные, без API-запросов;
- `stop_on_error=true` — после первой ошибки остальные элементы не создаются.
При `false` обработка продолжается; ошибки авторизации, прав доступа и частоты
запросов в любом случае останавливают пакет. Неожиданные ошибки клиента также
прекращают дальнейшие записи.
У каждого объявления обязательны `title`, `promote_url`, `cpm`, `placement`,
`target` и `idempotency_key`. `text` обязателен, кроме таргетинга `search`.
Типы таргетинга: `channels`, `users`, `bots`, `search`; параметры каждого типа
описаны в схеме инструмента. `placement` задаётся отдельно от типа таргетинга.
Пример локальной проверки одного объявления:
```json
{
"dry_run": true,
"ads": [
{
"title": "Первый вариант",
"text": "Маршруты и заметки о путешествиях — в нашем канале.",
"promote_url": "https://t.me/example_channel",
"cpm": 0.3,
"placement": "channel_post",
"target": {"type": "channels", "channel_ids": ["@example_placement"]},
"idempotency_key": "3b284a89-7d93-48e0-986d-7866a90d8801"
}
]
}
```
Замените пример своими параметрами. Для создания задайте `dry_run=false`.
Допустимость CPM зависит от аккаунта и параметров объявления; значение в примере
условное. Локальная проверка не подтверждает права, остаток бюджета, допустимость
ставки, media ID или прохождение модерации. В dry run валюта не запрашивается
и возвращается как `null`.
По умолчанию `initial_budget=0`, `is_paused=true`. Положительный `initial_budget`
переводит средства из аккаунта в объявление и может вызвать автоматическую
отправку на модерацию. `activate_date` и `deactivate_date` переопределяют паузу
по правилам API. Все денежные параметры задаются в валюте выбранного аккаунта.
Поддерживаются настройки внешнего сайта, фото или видео, частоты, дневного
лимита, дат, расписания и существующего события конверсии. Для внешней ссылки
нужно `website_name`. Одновременно использовать `photo_id` и `video_id` нельзя.
В ответе находятся `account_id`, `currency`, счётчики, `complete` и `results`
в исходном порядке. У каждого результата есть `index` (с нуля), ключ и `outcome`:
- `succeeded` — получен успешный ответ API и данные объявления;
- `failed` — получен отказ;
- `unknown` — исход не подтверждён, например из-за сетевой ошибки;
- `not_attempted` — элемент не выполнялся после остановки пакета.
`complete=true` означает успех всех элементов. Уже созданные объявления
сохраняются при частичном результате. Успешный ответ может быть повтором
ранее выполненного запроса, а не новым созданием.
При повторе сохраняйте `idempotency_key` и все параметры объявления.
Telegram хранит результат по ключу до 24 часов. После этого тот же запрос
может создать ещё одно объявление. Порядок элементов, повторяющиеся ключи
и объявления сервер не меняет. Дополнительные сведения доступны через `get_ads_by_id`.
### Загрузка медиа
`upload_media` принимает абсолютный `file_path` на машине, где запущен сервер,
`kind` и необязательный `account_id`. Файл отправляется в Telegram Ads.
| `kind` | Требования |
|---|---|
| `ad_photo` | JPEG/PNG, до 5 МиБ, ширина от 640 px, 16:9. |
| `ad_video` | MP4, до 20 МиБ, ширина от 640 px, 16:9, 3–60 секунд. Нужен `ffprobe`. |
| `website_photo` | JPEG/PNG, до 1 МиБ, минимум 150×150 px. |
Ответ содержит `account_id`, `kind`, `photo_id`/`photo_url` или
`video_id`/`video_url`, а также `metadata` с форматом, размером файла,
размерами кадра и длительностью видео.
Передавайте полученный ID в `create_ads`. Его можно использовать повторно
для нескольких объявлений того же аккаунта. Повтор загрузки файла может
вернуть новый ID. Загрузка медиа сама по себе не создаёт объявление.
### Статистика нескольких объявлений
`get_ads_stats` получает статистику каждого объявления за один вызов MCP.
Передайте `from_time` и `to_time`, а также необязательные параметры:
- `ad_ids` — список ID. Пустой или пропущенный список означает все объявления,
доступные в списке аккаунта, включая остановленные. Сервер сам загружает
страницы списка. Переданный список ID используется без изменений.
- `include_timeseries` — по умолчанию `false`. Значение `true` добавляет
к итогам исходные строки статистики за каждый интервал.
- `interval` — 86400 секунд по умолчанию или 300 секунд.
- `account_id` — аккаунт, выбранный по общим правилам настройки.
Пример параметров для всех объявлений за 30 октября — 5 ноября 2026 года по UTC:
```json
{
"from_time": 1793318400,
"to_time": 1793923200,
"ad_ids": []
}
```
Для выбранных объявлений замените `[]` на список, например `[101, 202]`.
В ответе `ads` содержит по каждому объявлению `ad_id`, количество точек
`data_points` и `totals`: показы, открытия, клики, действия, расход и валюту.
Если API вернул пустой ряд, `totals` равен `null`. При `include_timeseries=true`
в каждом элементе появляется `stats` с разбивкой по интервалам.
Проверяйте `complete`, `succeeded_count`, `failed_count` и `errors`:
при ошибке отдельного объявления сервер возвращает доступные результаты
и отмечает неполный ответ как `complete: false`. Ошибки авторизации,
прав доступа или ограничения частоты запросов прекращают операцию.
`total_count` — число обрабатываемых элементов списка ID, включая повторы;
`from_time` и `to_time`
в ответе — границы периода после округления к началу интервала.
Количество запросов к Telegram API зависит от числа объявлений.
Для больших аккаунтов увеличьте таймаут инструмента в MCP-клиенте.
### Параметры и ограничения
- ID аккаунта передаётся строкой. ID объявления — целым числом;
он уникален внутри аккаунта.
- `list_ads` и `list_related_accounts` возвращают по 20 записей по умолчанию;
`limit` — от 1 до 100.
Автоматической загрузки всех страниц нет. Для связанных аккаунтов
увеличивайте `offset` на число полученных записей до `total_count`.
Для объявлений передавайте полученный `next_offset` без изменений,
пока он присутствует в ответе.
- `get_accounts_by_id` и `get_ads_by_id` принимают от 1 до 100 ID за вызов.
- У `list_ads` и `get_ads_by_id` параметр `return_target=true` включает
настройки таргетинга в ответ. По умолчанию он выключен.
- Для статистики нужны `from_time` и `to_time` в Unix-секундах.
Начало периода включается, конец — нет. `interval` равен **86400** секундам
по умолчанию; также доступен интервал **300** секунд.
Границы округляются к началу интервала; период — не более **1000 интервалов**.
Для `get_ads_stats` после округления должен оставаться хотя бы один интервал.
- Для месячного отчёта задайте `year` от 2021 до 2100 и `month` от 1 до 12.
- Денежные значения возвращаются в валюте аккаунта: `EUR`, `TON` или `XTR`.
Если инструмент сообщает об отсутствии `TELEGRAM_ADS_TOKEN`, проверьте `.env`
и окружение MCP-клиента: пустая переменная окружения перекрывает значение из файла.
## Зависимости и документация
Основные зависимости: [MCP Python SDK](https://github.com/modelcontextprotocol/python-sdk),
[TeleAd](https://github.com/Elimeshi1/TeleAd) — клиент Telegram Ads API,
Pydantic, pydantic-settings, AnyIO и Pillow. Версии указаны в `pyproject.toml`
и зафиксированы в `uv.lock`.
- [Telegram Ads API](https://ads.telegram.org/docs/api).
- [Архитектура, добавление инструментов и проверки](docs/development.md).
## Лицензия
[MIT](LICENSE). Copyright (c) 2026 Sergei Popov.
TDQS
Scored across 11 tools
Account tools (current, related, by-id) and ad tools (create, list, by-id) have clearly distinct purposes. The stats family is mostly separable, though get_ads_stats (per-ad totals for an arbitrary window) and get_account_report (per-ad totals for a calendar month) overlap in intent, and get_ads_stats vs get_ad_stats differ only by plural and require reading descriptions to distinguish.
Names follow a consistent snake_case verb_noun convention (get_*, list_*, create_*, upload_*). The singular/plural shifts (get_ad_stats vs get_ads_stats, get_accounts_by_id vs get_current_account) are semantically meaningful but make the surface slightly less predictable at a glance.
Eleven tools is well within a comfortable range for a domain covering accounts, ads, stats, reporting, and media. Each tool maps to a distinct resource/action, so none feels redundant padding.
Read and create paths are solid (accounts, ad listing, ad creation, several stats views, media upload), but there is no update/pause/delete or edit operation for ads, so the lifecycle dead-ends after creation. Targeting appears only as a read flag (return_target), with no way to modify it, which will force agents into workarounds.