Skip to main content
Glama
ads-telegram-mcp

ads-telegram-mcp

by Draniev
README.md
# 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

A3.9/5.0

Scored across 11 tools

Disambiguation4/5

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.

Naming Consistency4/5

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.

Tool Count5/5

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.

Completeness3/5

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.

Maintenance

ActivityMaintained
ResponsivenessNo recent community issues