yandex-direct-mcp-plus 1.3.0 → 1.4.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +85 -37
- package/dist/app/registry.js +12 -5
- package/dist/shared/api/errors.d.ts +1 -0
- package/dist/shared/api/errors.js +31 -0
- package/dist/shared/api/fetch.js +2 -0
- package/dist/shared/config/enums.d.ts +8 -3
- package/dist/shared/config/enums.js +25 -7
- package/dist/shared/config/limits.d.ts +6 -0
- package/dist/shared/config/limits.js +12 -0
- package/dist/tools/bid-adjustments/handler.d.ts +3 -1
- package/dist/tools/bid-adjustments/handler.js +150 -1
- package/dist/tools/bid-adjustments/schema.d.ts +19 -1
- package/dist/tools/bid-adjustments/schema.js +70 -10
- package/dist/tools/bid-adjustments/tool.d.ts +2 -0
- package/dist/tools/bid-adjustments/tool.js +20 -4
- package/dist/tools/campaigns/schema.d.ts +1 -1
- package/dist/tools/campaigns/schema.js +7 -2
- package/dist/tools/campaigns/tool.js +3 -3
- package/dist/tools/changes/handler.js +13 -2
- package/dist/tools/changes/schema.d.ts +2 -2
- package/dist/tools/changes/schema.js +2 -2
- package/dist/tools/changes/tool.js +1 -1
- package/dist/tools/dictionaries/handler.js +16 -0
- package/dist/tools/dictionaries/schema.d.ts +1 -0
- package/dist/tools/dictionaries/schema.js +3 -0
- package/dist/tools/dictionaries/tool.js +1 -1
- package/dist/tools/keywords/handler.d.ts +2 -1
- package/dist/tools/keywords/handler.js +19 -0
- package/dist/tools/keywords/schema.d.ts +8 -0
- package/dist/tools/keywords/schema.js +31 -1
- package/dist/tools/keywords/tool.d.ts +1 -0
- package/dist/tools/keywords/tool.js +10 -2
- package/dist/tools/retargeting/handler.d.ts +3 -1
- package/dist/tools/retargeting/handler.js +39 -9
- package/dist/tools/retargeting/schema.d.ts +17 -0
- package/dist/tools/retargeting/schema.js +46 -15
- package/dist/tools/retargeting/tool.d.ts +2 -0
- package/dist/tools/retargeting/tool.js +19 -3
- package/dist/tools/sitelinks/handler.d.ts +2 -1
- package/dist/tools/sitelinks/handler.js +6 -0
- package/dist/tools/sitelinks/schema.d.ts +3 -0
- package/dist/tools/sitelinks/schema.js +9 -1
- package/dist/tools/sitelinks/tool.d.ts +1 -0
- package/dist/tools/sitelinks/tool.js +11 -3
- package/dist/tools/vcards/handler.d.ts +2 -1
- package/dist/tools/vcards/handler.js +4 -0
- package/dist/tools/vcards/schema.d.ts +3 -0
- package/dist/tools/vcards/schema.js +6 -0
- package/dist/tools/vcards/tool.d.ts +1 -0
- package/dist/tools/vcards/tool.js +11 -3
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -1,33 +1,60 @@
|
|
|
1
1
|
# yandex-direct-mcp-plus
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
Ведение контекстной рекламы Яндекс.Директа из диалога с ассистентом: собрать кампанию, разобрать поисковые запросы, вычистить минус-фразы, поправить ставки и посмотреть расход — не переключаясь между разделами кабинета. Работает в любом MCP-клиенте: Claude Code, Claude Desktop, Cursor и другие.
|
|
4
4
|
|
|
5
|
+
[](https://www.npmjs.com/package/yandex-direct-mcp-plus)
|
|
5
6
|
[](https://opensource.org/licenses/MIT)
|
|
6
7
|
[](https://nodejs.org)
|
|
7
8
|
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
9
|
+
- **58 инструментов**, из них 24 только читают. Кампании и стратегии, группы, объявления и модерация, ключевые фразы и ставки, минус-фразы и общие наборы, быстрые ссылки, уточнения, изображения, визитки, корректировки ставок, ретаргетинг, аудиторные и динамические цели, фиды, расписание показов, статистика, поисковые запросы, баланс и справочники.
|
|
10
|
+
- **Деньги — в рублях**, на вводе и на выводе; в микроединицы API сервер переводит сам. Поддержан агентский режим (`Client-Login`).
|
|
11
|
+
- **ID — строками** (`"1915016273214320641"`): 64-битные идентификаторы Директа не помещаются в число JavaScript и молча теряют точность. Здесь это стережёт правило линтера, а не внимательность.
|
|
12
|
+
- **Реклама боевая.** Тестовой среды у Директа больше нет — какие инструменты тратят деньги и что удаляют необратимо, перечислено в разделе [Что меняет данные](#что-меняет-данные).
|
|
13
|
+
- **Телеметрии нет.** Сервер не отправляет никуда ничего, кроме запросов к API Яндекса.
|
|
11
14
|
|
|
12
|
-
|
|
13
|
-
|
|
15
|
+
## Содержание
|
|
16
|
+
|
|
17
|
+
- [Что можно делать](#что-можно-делать) — примеры запросов обычным текстом
|
|
18
|
+
- [Установка](#установка) — Claude Code, Claude Desktop, Cursor, из исходников
|
|
19
|
+
- [Токен](#токен) — как получить и какие переменные окружения нужны
|
|
20
|
+
- [Что меняет данные](#что-меняет-данные) — что тратит бюджет и что необратимо
|
|
21
|
+
- [Инструменты](#инструменты-58) — полный список с описаниями
|
|
22
|
+
- [Разработка](#разработка) — сборка, тесты, архитектура
|
|
23
|
+
|
|
24
|
+
## Что можно делать
|
|
25
|
+
|
|
26
|
+
Обычным текстом в чате — инструменты сервер подставляет сам:
|
|
27
|
+
|
|
28
|
+
```
|
|
29
|
+
Собери кампанию «Летняя распродажа»: бюджет 5000 ₽/день, старт 1 мая, показы будни 9–21
|
|
30
|
+
Добавь минус-фразы «бесплатно» и «скачать» в кампанию 12345, не затерев остальные
|
|
31
|
+
Посмотри поисковые запросы за месяц и предложи, что заминусовать
|
|
32
|
+
Подними ставку до 25 ₽ там, где CTR выше 8%, а показов меньше сотни
|
|
33
|
+
Что изменилось в кампаниях со вчера?
|
|
34
|
+
Покажи расход по кампаниям за неделю и баланс аккаунта
|
|
35
|
+
Найди код региона для Новосибирска
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
Полный список — [58 инструментов](#инструменты-58) ниже.
|
|
14
39
|
|
|
15
40
|
## Установка
|
|
16
41
|
|
|
42
|
+
Нужен Node.js 22+ и OAuth-токен Яндекс.Директа — [как его получить](#токен).
|
|
43
|
+
|
|
44
|
+
### Claude Code
|
|
45
|
+
|
|
17
46
|
```bash
|
|
18
|
-
|
|
19
|
-
cd yandex-direct-mcp-plus
|
|
20
|
-
npm ci && npm run build
|
|
47
|
+
claude mcp add yandex-direct -e YANDEX_DIRECT_TOKEN=ваш_токен -- npx -y yandex-direct-mcp-plus
|
|
21
48
|
```
|
|
22
49
|
|
|
23
|
-
### Claude Desktop
|
|
50
|
+
### Claude Desktop, Cursor и другие клиенты
|
|
24
51
|
|
|
25
52
|
```json
|
|
26
53
|
{
|
|
27
54
|
"mcpServers": {
|
|
28
55
|
"yandex-direct": {
|
|
29
|
-
"command": "
|
|
30
|
-
"args": ["
|
|
56
|
+
"command": "npx",
|
|
57
|
+
"args": ["-y", "yandex-direct-mcp-plus"],
|
|
31
58
|
"env": {
|
|
32
59
|
"YANDEX_DIRECT_TOKEN": "ваш_токен"
|
|
33
60
|
}
|
|
@@ -36,12 +63,19 @@ npm ci && npm run build
|
|
|
36
63
|
}
|
|
37
64
|
```
|
|
38
65
|
|
|
39
|
-
###
|
|
66
|
+
### Из исходников
|
|
40
67
|
|
|
41
68
|
```bash
|
|
42
|
-
|
|
69
|
+
git clone git@github.com:Pavelsiba/yandex-direct-mcp-plus.git
|
|
70
|
+
cd yandex-direct-mcp-plus
|
|
71
|
+
npm ci && npm run build
|
|
43
72
|
```
|
|
44
|
-
|
|
73
|
+
|
|
74
|
+
Дальше тот же конфиг, но `"command": "node"` и путь к `dist/app/index.js` вместо `npx`.
|
|
75
|
+
|
|
76
|
+
## Токен
|
|
77
|
+
|
|
78
|
+
OAuth-токен выпускается для приложения, зарегистрированного в [Яндекс OAuth](https://oauth.yandex.ru/), с доступом к API Директа. Подробности — [регистрация приложения и получение токена](https://yandex.ru/dev/direct/doc/ru/token). Доступ к API нужно [запросить в интерфейсе Директа](https://yandex.ru/dev/direct/doc/ru/access-request) — заявку рассматривают от часа до нескольких суток.
|
|
45
79
|
|
|
46
80
|
| Переменная | Обязательна | Назначение |
|
|
47
81
|
|------------|:-----------:|------------|
|
|
@@ -49,17 +83,36 @@ claude mcp add yandex-direct -e YANDEX_DIRECT_TOKEN=ваш_токен -- node /
|
|
|
49
83
|
| `YANDEX_DIRECT_LOGIN` | нет | Логин клиента для агентских токенов (заголовок `Client-Login`). Обязателен, если токен агентский |
|
|
50
84
|
| `YANDEX_DIRECT_POLYGON_CAMPAIGN_ID` | нет | Только для `npm run test:int`: ID кампании-полигона, оставленной черновиком. Сетевые тесты пишут в неё и ни во что другое; без переменной они пропускаются |
|
|
51
85
|
|
|
52
|
-
|
|
86
|
+
## Что меняет данные
|
|
53
87
|
|
|
54
|
-
|
|
88
|
+
Тестовой среды у Яндекс.Директа больше нет: песочница отключена с июля 2026, и любой вызов идёт по боевому аккаунту. Отлаживать сценарии приходится на отдельной кампании, оставленной черновиком, — показов она не даёт и потому не тратит бюджет, пока не пройдёт модерацию и не будет включена.
|
|
55
89
|
|
|
56
|
-
|
|
90
|
+
Граница проходит не по «чтение или запись», а по скорости, с которой действие превращается в деньги.
|
|
57
91
|
|
|
58
|
-
|
|
92
|
+
**24 инструмента только читают** — все `list_*`, `get_*` и справочники. Вызвать их безопасно всегда.
|
|
59
93
|
|
|
60
|
-
|
|
94
|
+
**Тратят бюджет или запускают показы** — восемь:
|
|
95
|
+
|
|
96
|
+
| Инструмент | Чем именно |
|
|
97
|
+
|------------|------------|
|
|
98
|
+
| `manage_campaigns` | `resume` — включает показы остановленной кампании |
|
|
99
|
+
| `manage_ads` | `resume` и `moderate` — возвращает объявления в показ |
|
|
100
|
+
| `moderate_ads` | Отправляет объявления на модерацию, после неё начнутся показы |
|
|
101
|
+
| `update_campaign` | Меняет дневной бюджет |
|
|
102
|
+
| `set_keyword_bids` | Меняет ставки, то есть цену клика |
|
|
103
|
+
| `set_strategy` | Меняет стратегию — переписывает всю экономику кампании |
|
|
104
|
+
| `add_bid_adjustments` | Заводит корректировку: +N% к ставке на срезе аудитории |
|
|
105
|
+
| `set_bid_adjustments` | Меняет коэффициент существующей корректировки |
|
|
106
|
+
|
|
107
|
+
**Удаляют необратимо** — эти инструменты помечены аннотацией `DESTRUCTIVE`, и хороший MCP-клиент спросит подтверждение перед вызовом:
|
|
61
108
|
|
|
62
|
-
|
|
109
|
+
`manage_campaigns` (`delete`), `manage_ads` (`delete`), `manage_keywords` (`delete`), `delete_ad_groups`, `delete_ad_extensions`, `delete_sitelinks`, `delete_vcards`, `delete_bid_adjustments`, `delete_retargeting_lists`, `manage_ad_images` (`delete`), `manage_dynamic_targets` (`delete`), `set_audience_targets` (`delete`), `manage_negative_keyword_shared_sets` (`delete`).
|
|
110
|
+
|
|
111
|
+
Сюда же — `set_campaign_negative_keywords` и `set_ad_group_negative_keywords` в режиме `replace`: он затирает прежний список минус-фраз целиком. Именно поэтому у них нет режима по умолчанию — `mode` приходится назвать явно.
|
|
112
|
+
|
|
113
|
+
Остальные инструменты создают и правят объекты. Пока кампания не прошла модерацию и не включена, показов по ней нет и бюджет не расходуется.
|
|
114
|
+
|
|
115
|
+
## Инструменты (58)
|
|
63
116
|
|
|
64
117
|
**Кампании**
|
|
65
118
|
|
|
@@ -69,7 +122,7 @@ OAuth-токен выпускается для приложения, зарег
|
|
|
69
122
|
| `get_campaign` | Детальная информация о кампании по ID |
|
|
70
123
|
| `create_campaign` | Создать кампанию (бюджет в рублях, выбор стратегии, часовой пояс, UTM-разметка) |
|
|
71
124
|
| `update_campaign` | Обновить название/бюджет/UTM-разметку и/или статус (SUSPEND/RESUME/ARCHIVE/UNARCHIVE) |
|
|
72
|
-
| `manage_campaigns` | suspend/resume/archive/unarchive для списка кампаний |
|
|
125
|
+
| `manage_campaigns` | suspend/resume/archive/unarchive/delete для списка кампаний |
|
|
73
126
|
| `get_strategy` | Получить стратегию текстово-графической кампании |
|
|
74
127
|
| `set_strategy` | Сменить стратегию: ручная, максимум кликов, средняя цена клика/конверсии, оплата за конверсию |
|
|
75
128
|
| `get_time_targeting` | Расписание показов: часовой пояс, часы по дням недели, праздники |
|
|
@@ -100,6 +153,7 @@ OAuth-токен выпускается для приложения, зарег
|
|
|
100
153
|
|------------|----------|
|
|
101
154
|
| `list_keywords` | Ключевые фразы в группах (ставки в рублях) |
|
|
102
155
|
| `add_keywords` | Добавить ключевые фразы |
|
|
156
|
+
| `update_keywords` | Изменить текст фразы и подстановочные переменные `{param1}`/`{param2}` |
|
|
103
157
|
| `set_keyword_bids` | Установить ставки (поиск/сети, рубли) на фразах/группах/кампаниях |
|
|
104
158
|
| `manage_keywords` | suspend/resume/delete |
|
|
105
159
|
| `set_campaign_negative_keywords` | Минус-фразы кампании: `mode` обязателен — `replace`, `add` или `remove` |
|
|
@@ -111,12 +165,15 @@ OAuth-токен выпускается для приложения, зарег
|
|
|
111
165
|
|------------|----------|
|
|
112
166
|
| `list_sitelinks` | Получить наборы быстрых ссылок |
|
|
113
167
|
| `set_sitelinks` | Создать новый набор быстрых ссылок |
|
|
168
|
+
| `delete_sitelinks` | Удалить наборы быстрых ссылок |
|
|
114
169
|
| `list_ad_extensions` | Получить уточнения (callouts) |
|
|
115
170
|
| `add_ad_extensions` | Создать уточнения |
|
|
116
171
|
| `delete_ad_extensions` | Удалить уточнения |
|
|
117
172
|
| `manage_ad_images` | Загрузить, получить или удалить изображения |
|
|
118
|
-
| `get_bid_adjustments` | Получить
|
|
173
|
+
| `get_bid_adjustments` | Получить корректировки: устройства, пол и возраст, аудитории, регионы, платёжеспособность, размещение |
|
|
174
|
+
| `add_bid_adjustments` | Создать корректировки на кампаниях или группах |
|
|
119
175
|
| `set_bid_adjustments` | Изменить коэффициенты существующих корректировок |
|
|
176
|
+
| `delete_bid_adjustments` | Удалить корректировки по ID |
|
|
120
177
|
|
|
121
178
|
**Аудитории, цели и фиды**
|
|
122
179
|
|
|
@@ -124,6 +181,8 @@ OAuth-токен выпускается для приложения, зарег
|
|
|
124
181
|
|------------|----------|
|
|
125
182
|
| `list_retargeting_lists` | Получить условия ретаргетинга и подбора аудитории |
|
|
126
183
|
| `add_retargeting_list` | Создать условие ретаргетинга |
|
|
184
|
+
| `update_retargeting_lists` | Изменить название, описание и правила условий (правила заменяются целиком) |
|
|
185
|
+
| `delete_retargeting_lists` | Удалить условия ретаргетинга |
|
|
127
186
|
| `list_audience_targets` | Получить аудиторные цели |
|
|
128
187
|
| `set_audience_targets` | add/set_bids/suspend/resume/delete аудиторных целей |
|
|
129
188
|
| `list_dynamic_targets` | Получить динамические цели |
|
|
@@ -139,26 +198,15 @@ OAuth-токен выпускается для приложения, зарег
|
|
|
139
198
|
|------------|----------|
|
|
140
199
|
| `get_statistics` | Статистика за период (показы, клики, расход, CTR, CPC) |
|
|
141
200
|
| `get_search_queries` | Фактические поисковые запросы для подбора минус-фраз |
|
|
142
|
-
| `get_changes` | Проверить изменения кампаний,
|
|
201
|
+
| `get_changes` | Проверить изменения кампаний, групп, объявлений и справочников |
|
|
143
202
|
| `list_vcards` | Получить виртуальные визитки |
|
|
144
203
|
| `add_vcard` | Создать виртуальную визитку |
|
|
204
|
+
| `delete_vcards` | Удалить визитки по ID |
|
|
145
205
|
| `list_businesses` | Получить профили организаций Яндекс Бизнеса |
|
|
146
206
|
| `get_account_balance` | Баланс аккаунта (Live API v4) |
|
|
147
|
-
| `get_regions` | Справочник кодов регионов (225 = Россия) |
|
|
207
|
+
| `get_regions` | Справочник кодов регионов (225 = Россия), с вложенностью по запросу |
|
|
148
208
|
| `list_time_zones` | Справочник часовых поясов для расписания показов |
|
|
149
209
|
|
|
150
|
-
## Примеры запросов
|
|
151
|
-
|
|
152
|
-
```
|
|
153
|
-
Покажи все активные рекламные кампании
|
|
154
|
-
Создай кампанию "Летняя распродажа" с бюджетом 5000 ₽/день, старт 2026-05-01
|
|
155
|
-
Установи ставку 25 ₽ на ключевые фразы 111 и 222
|
|
156
|
-
Добавь минус-фразы "бесплатно", "скачать" в кампанию 12345
|
|
157
|
-
Какая статистика у кампаний 12345 и 67890 за последнюю неделю?
|
|
158
|
-
Найди код региона для Новосибирска
|
|
159
|
-
Покажи баланс аккаунта
|
|
160
|
-
```
|
|
161
|
-
|
|
162
210
|
## Разработка
|
|
163
211
|
|
|
164
212
|
```bash
|
package/dist/app/registry.js
CHANGED
|
@@ -4,21 +4,21 @@ import { createAdGroupTool, deleteAdGroupsTool, listAdGroupsTool } from "#tools/
|
|
|
4
4
|
import { manageAdImagesTool } from "#tools/ad-images/tool";
|
|
5
5
|
import { createTextAdTool, listAdsTool, manageAdsTool, moderateAdsTool, updateTextAdTool } from "#tools/ads/tool";
|
|
6
6
|
import { listAudienceTargetsTool, setAudienceTargetsTool } from "#tools/audience-targets/tool";
|
|
7
|
-
import { getBidAdjustmentsTool, setBidAdjustmentsTool } from "#tools/bid-adjustments/tool";
|
|
7
|
+
import { addBidAdjustmentsTool, deleteBidAdjustmentsTool, getBidAdjustmentsTool, setBidAdjustmentsTool } from "#tools/bid-adjustments/tool";
|
|
8
8
|
import { listBusinessesTool } from "#tools/businesses/tool";
|
|
9
9
|
import { createCampaignTool, getCampaignTool, getStrategyTool, listCampaignsTool, manageCampaignsTool, setStrategyTool, updateCampaignTool } from "#tools/campaigns/tool";
|
|
10
10
|
import { getChangesTool } from "#tools/changes/tool";
|
|
11
11
|
import { getRegionsTool, listTimeZonesTool } from "#tools/dictionaries/tool";
|
|
12
12
|
import { listDynamicTargetsTool, manageDynamicTargetsTool } from "#tools/dynamic-targets/tool";
|
|
13
13
|
import { listFeedsTool } from "#tools/feeds/tool";
|
|
14
|
-
import { addKeywordsTool, listKeywordsTool, manageKeywordsTool, setKeywordBidsTool } from "#tools/keywords/tool";
|
|
14
|
+
import { addKeywordsTool, listKeywordsTool, manageKeywordsTool, setKeywordBidsTool, updateKeywordsTool } from "#tools/keywords/tool";
|
|
15
15
|
import { getCampaignNegativeKeywordsTool, linkNegativeKeywordSetsTool, listNegativeKeywordSharedSetsTool, manageNegativeKeywordSharedSetsTool, setAdGroupNegativeKeywordsTool, setCampaignNegativeKeywordsTool } from "#tools/negative-keywords/tool";
|
|
16
|
-
import { addRetargetingListTool, listRetargetingListsTool } from "#tools/retargeting/tool";
|
|
16
|
+
import { addRetargetingListTool, deleteRetargetingListsTool, listRetargetingListsTool, updateRetargetingListsTool } from "#tools/retargeting/tool";
|
|
17
17
|
import { getSearchQueriesTool } from "#tools/search-queries/tool";
|
|
18
|
-
import { listSitelinksTool, setSitelinksTool } from "#tools/sitelinks/tool";
|
|
18
|
+
import { deleteSitelinksTool, listSitelinksTool, setSitelinksTool } from "#tools/sitelinks/tool";
|
|
19
19
|
import { getStatisticsTool } from "#tools/statistics/tool";
|
|
20
20
|
import { getTimeTargetingTool, setTimeTargetingTool } from "#tools/time-targeting/tool";
|
|
21
|
-
import { addVcardTool, listVcardsTool } from "#tools/vcards/tool";
|
|
21
|
+
import { addVcardTool, deleteVcardsTool, listVcardsTool } from "#tools/vcards/tool";
|
|
22
22
|
export const tools = [
|
|
23
23
|
// Кампании и стратегии
|
|
24
24
|
listCampaignsTool,
|
|
@@ -43,6 +43,7 @@ export const tools = [
|
|
|
43
43
|
// Ключевые фразы и ставки
|
|
44
44
|
listKeywordsTool,
|
|
45
45
|
addKeywordsTool,
|
|
46
|
+
updateKeywordsTool,
|
|
46
47
|
manageKeywordsTool,
|
|
47
48
|
setKeywordBidsTool,
|
|
48
49
|
// Минус-фразы
|
|
@@ -55,12 +56,14 @@ export const tools = [
|
|
|
55
56
|
// Ассеты объявления
|
|
56
57
|
listSitelinksTool,
|
|
57
58
|
setSitelinksTool,
|
|
59
|
+
deleteSitelinksTool,
|
|
58
60
|
listAdExtensionsTool,
|
|
59
61
|
addAdExtensionsTool,
|
|
60
62
|
deleteAdExtensionsTool,
|
|
61
63
|
manageAdImagesTool,
|
|
62
64
|
listVcardsTool,
|
|
63
65
|
addVcardTool,
|
|
66
|
+
deleteVcardsTool,
|
|
64
67
|
// Таргетинг и корректировки
|
|
65
68
|
listAudienceTargetsTool,
|
|
66
69
|
setAudienceTargetsTool,
|
|
@@ -68,8 +71,12 @@ export const tools = [
|
|
|
68
71
|
manageDynamicTargetsTool,
|
|
69
72
|
listRetargetingListsTool,
|
|
70
73
|
addRetargetingListTool,
|
|
74
|
+
updateRetargetingListsTool,
|
|
75
|
+
deleteRetargetingListsTool,
|
|
71
76
|
getBidAdjustmentsTool,
|
|
77
|
+
addBidAdjustmentsTool,
|
|
72
78
|
setBidAdjustmentsTool,
|
|
79
|
+
deleteBidAdjustmentsTool,
|
|
73
80
|
// Отчёты
|
|
74
81
|
getStatisticsTool,
|
|
75
82
|
getSearchQueriesTool,
|
|
@@ -1,10 +1,29 @@
|
|
|
1
1
|
// Разбор ошибок Директа. Хендлеры сюда не заглядывают: их дело — сценарий.
|
|
2
|
+
// 53 — единственная ошибка, которую не чинит ни повтор, ни другой запрос: токен истёк,
|
|
3
|
+
// отозван или указан неверно. Проба 06.09.2026: v5 отдаёт её телом с HTTP 200
|
|
4
|
+
// («Ошибка авторизации» / «Недействительный OAuth-токен»), v4 — тем же кодом, но с пустым
|
|
5
|
+
// error_detail, а Reports — HTTP 400 и XML, где код лежит в <reports:errorCode>.
|
|
6
|
+
const AUTH_ERROR_CODE = 53;
|
|
7
|
+
// Текст адресован модели на другом конце протокола, а не человеку в логе: без явного
|
|
8
|
+
// «повтор не поможет» она уводит вызов в ретраи, и пользователь так и не узнает, что
|
|
9
|
+
// нужно перевыпустить токен.
|
|
10
|
+
const AUTH_ERROR_MESSAGE = [
|
|
11
|
+
"Токен Яндекс.Директа не принят: истёк, отозван или задан неверно.",
|
|
12
|
+
"Это не сбой сети и не временная ошибка — повторять вызов бесполезно.",
|
|
13
|
+
"Нужен новый OAuth-токен (https://yandex.ru/dev/direct/doc/ru/token):",
|
|
14
|
+
"передайте его в переменной YANDEX_DIRECT_TOKEN в конфигурации MCP-клиента и перезапустите сервер."
|
|
15
|
+
].join(" ");
|
|
16
|
+
function isAuthError(code) {
|
|
17
|
+
return Number(code) === AUTH_ERROR_CODE;
|
|
18
|
+
}
|
|
2
19
|
// v5 возвращает ошибку уровня запроса телом с HTTP 200 — статус проверять бесполезно,
|
|
3
20
|
// признак ошибки один: ключ `error`. Док: https://yandex.ru/dev/direct/doc/en/concepts/errors-list
|
|
4
21
|
export function assertNoApiError(data) {
|
|
5
22
|
const error = data?.error;
|
|
6
23
|
if (!error || typeof error !== "object")
|
|
7
24
|
return;
|
|
25
|
+
if (isAuthError(error.error_code))
|
|
26
|
+
throw new Error(AUTH_ERROR_MESSAGE);
|
|
8
27
|
const parts = [`Ошибка API Яндекс.Директ [${error.error_code ?? "?"}]: ${error.error_string ?? "неизвестная ошибка"}`];
|
|
9
28
|
if (error.error_detail)
|
|
10
29
|
parts.push(`— ${error.error_detail}`);
|
|
@@ -16,6 +35,18 @@ export function assertNoApiError(data) {
|
|
|
16
35
|
export function assertNoApiErrorV4(data) {
|
|
17
36
|
if (!data || (data.error_code === undefined && data.error_str === undefined))
|
|
18
37
|
return;
|
|
38
|
+
if (isAuthError(data.error_code))
|
|
39
|
+
throw new Error(AUTH_ERROR_MESSAGE);
|
|
19
40
|
const detail = data.error_detail ? ` — ${data.error_detail}` : "";
|
|
20
41
|
throw new Error(`Ошибка API v4 [${data.error_code ?? "?"}]: ${data.error_str ?? "неизвестная ошибка"}${detail}`);
|
|
21
42
|
}
|
|
43
|
+
const REPORT_ERROR_CODE = /<reports:errorCode>(\d+)<\/reports:errorCode>/;
|
|
44
|
+
// Reports — единственный сервис, отвечающий настоящим HTTP-кодом и XML, поэтому его
|
|
45
|
+
// ошибка не доходит ни до assertNoApiError, ни до схемы: транспорт бросает раньше.
|
|
46
|
+
// Вызывается из fetchWithRetry — там тело неуспешного ответа и оказывается. Код тянем
|
|
47
|
+
// регуляркой: разбирать XML ради одного числа — лишняя зависимость.
|
|
48
|
+
export function assertNoReportAuthError(body) {
|
|
49
|
+
const code = REPORT_ERROR_CODE.exec(body)?.[1];
|
|
50
|
+
if (code && isAuthError(code))
|
|
51
|
+
throw new Error(AUTH_ERROR_MESSAGE);
|
|
52
|
+
}
|
package/dist/shared/api/fetch.js
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
// Транспорт: заголовки, повторы, таймаут. Знает про HTTP и токен, не знает про домен.
|
|
2
|
+
import { assertNoReportAuthError } from "#shared/api/errors";
|
|
2
3
|
import { getClientLogin, getToken } from "#shared/config/env";
|
|
3
4
|
import { MAX_RETRIES, MAX_RETRY_DELAY_MS, REQUEST_TIMEOUT_MS } from "#shared/config/limits";
|
|
4
5
|
export function commonHeaders() {
|
|
@@ -45,6 +46,7 @@ export async function fetchWithRetry(url, options = {}, retries = MAX_RETRIES) {
|
|
|
45
46
|
continue;
|
|
46
47
|
}
|
|
47
48
|
const body = await response.text().catch(() => "");
|
|
49
|
+
assertNoReportAuthError(body);
|
|
48
50
|
throw new Error(`HTTP ${response.status}: ${response.statusText}. ${body}`);
|
|
49
51
|
}
|
|
50
52
|
catch (error) {
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
export declare const CAMPAIGN_STATUSES: readonly ["ACCEPTED", "DRAFT", "MODERATION", "REJECTED"];
|
|
2
2
|
export declare const KEYWORD_ACTIONS: readonly ["suspend", "resume", "delete"];
|
|
3
3
|
export declare const AD_ACTIONS: readonly ["suspend", "resume", "archive", "unarchive", "moderate", "delete"];
|
|
4
|
-
export declare const CAMPAIGN_ACTIONS: readonly ["suspend", "resume", "archive", "unarchive"];
|
|
4
|
+
export declare const CAMPAIGN_ACTIONS: readonly ["suspend", "resume", "archive", "unarchive", "delete"];
|
|
5
5
|
export declare const CAMPAIGN_STATUS_ACTIONS: readonly ["SUSPEND", "RESUME", "ARCHIVE", "UNARCHIVE"];
|
|
6
6
|
export declare const CAMPAIGN_TYPES_CREATABLE: readonly ["TEXT_CAMPAIGN", "DYNAMIC_TEXT_CAMPAIGN"];
|
|
7
7
|
export declare const CAMPAIGN_TYPES: readonly ["TEXT_CAMPAIGN", "MOBILE_APP_CAMPAIGN", "DYNAMIC_TEXT_CAMPAIGN", "CPM_BANNER_CAMPAIGN", "SMART_CAMPAIGN", "UNIFIED_CAMPAIGN"];
|
|
@@ -15,7 +15,12 @@ export declare const AD_IMAGE_ACTIONS: readonly ["add", "get", "delete"];
|
|
|
15
15
|
export declare const ASSOCIATED_FLAGS: readonly ["YES", "NO"];
|
|
16
16
|
export declare const RETARGETING_TYPES: readonly ["RETARGETING", "AUDIENCE"];
|
|
17
17
|
export declare const RETARGETING_RULE_OPERATORS: readonly ["ALL", "ANY", "NONE"];
|
|
18
|
-
export declare const BID_ADJUSTMENT_TYPES: readonly ["MOBILE_ADJUSTMENT", "TABLET_ADJUSTMENT", "DESKTOP_ADJUSTMENT", "DESKTOP_ONLY_ADJUSTMENT", "DEMOGRAPHICS_ADJUSTMENT"];
|
|
18
|
+
export declare const BID_ADJUSTMENT_TYPES: readonly ["MOBILE_ADJUSTMENT", "TABLET_ADJUSTMENT", "DESKTOP_ADJUSTMENT", "DESKTOP_ONLY_ADJUSTMENT", "SMART_TV_ADJUSTMENT", "DEMOGRAPHICS_ADJUSTMENT", "RETARGETING_ADJUSTMENT", "REGIONAL_ADJUSTMENT", "VIDEO_ADJUSTMENT", "SMART_AD_ADJUSTMENT", "SERP_LAYOUT_ADJUSTMENT", "INCOME_GRADE_ADJUSTMENT", "AD_GROUP_ADJUSTMENT"];
|
|
19
|
+
export declare const OPERATING_SYSTEM_TYPES: readonly ["IOS", "ANDROID"];
|
|
20
|
+
export declare const GENDERS: readonly ["GENDER_MALE", "GENDER_FEMALE"];
|
|
21
|
+
export declare const AGE_RANGES: readonly ["AGE_0_17", "AGE_18_24", "AGE_25_34", "AGE_35_44", "AGE_45", "AGE_45_54", "AGE_55"];
|
|
22
|
+
export declare const SERP_LAYOUTS: readonly ["ALONE", "SUGGEST"];
|
|
23
|
+
export declare const INCOME_GRADES: readonly ["VERY_HIGH", "HIGH", "ABOVE_AVERAGE"];
|
|
19
24
|
export declare const BID_ADJUSTMENT_LEVELS: readonly ["CAMPAIGN", "AD_GROUP"];
|
|
20
25
|
export declare const AUDIENCE_TARGET_STATES: readonly ["ON", "SUSPENDED"];
|
|
21
26
|
export declare const AUDIENCE_TARGET_ACTIONS: readonly ["add", "set_bids", "suspend", "resume", "delete"];
|
|
@@ -23,7 +28,7 @@ export declare const STRATEGY_PRIORITIES: readonly ["LOW", "NORMAL", "HIGH"];
|
|
|
23
28
|
export declare const DYNAMIC_TARGET_ACTIONS: readonly ["add", "set_bids", "suspend", "resume", "delete"];
|
|
24
29
|
export declare const WEBPAGE_CONDITION_OPERANDS: readonly ["URL", "DOMAIN", "PAGE_TITLE", "PAGE_CONTENT", "OFFERS_LIST_URL"];
|
|
25
30
|
export declare const WEBPAGE_CONDITION_OPERATORS: readonly ["EQUALS_ANY", "NOT_EQUALS_ALL", "CONTAINS_ANY", "NOT_CONTAINS_ALL"];
|
|
26
|
-
export declare const CHANGES_MODES: readonly ["campaigns", "objects"];
|
|
31
|
+
export declare const CHANGES_MODES: readonly ["campaigns", "objects", "dictionaries"];
|
|
27
32
|
export declare const CHANGES_FIELD_NAMES: readonly ["CampaignIds", "AdGroupIds", "AdIds", "CampaignsStat"];
|
|
28
33
|
export declare const SETTABLE_SEARCH_STRATEGIES: readonly ["HIGHEST_POSITION", "WB_MAXIMUM_CLICKS", "AVERAGE_CPC", "AVERAGE_CPA", "PAY_FOR_CONVERSION", "SERVING_OFF"];
|
|
29
34
|
export declare const SETTABLE_NETWORK_STRATEGIES: readonly ["NETWORK_DEFAULT", "MAXIMUM_COVERAGE", "WB_MAXIMUM_CLICKS", "AVERAGE_CPC", "AVERAGE_CPA", "PAY_FOR_CONVERSION", "SERVING_OFF"];
|
|
@@ -11,8 +11,10 @@ export const CAMPAIGN_STATUSES = ["ACCEPTED", "DRAFT", "MODERATION", "REJECTED"]
|
|
|
11
11
|
export const KEYWORD_ACTIONS = ["suspend", "resume", "delete"];
|
|
12
12
|
// Действия над объявлениями: тоже методы сервиса ads.
|
|
13
13
|
export const AD_ACTIONS = ["suspend", "resume", "archive", "unarchive", "moderate", "delete"];
|
|
14
|
-
// Действия над кампанией: отдельные методы API, набор закрыт.
|
|
15
|
-
|
|
14
|
+
// Действия над кампанией: отдельные методы API, набор закрыт. delete необратим и
|
|
15
|
+
// доступен не всегда: кампанию с накопленной статистикой, поступившими средствами или
|
|
16
|
+
// в статусе CONVERTED Директ удалять отказывается — такую только архивировать.
|
|
17
|
+
export const CAMPAIGN_ACTIONS = ["suspend", "resume", "archive", "unarchive", "delete"];
|
|
16
18
|
// То же действие в update_campaign исторически принимается в верхнем регистре.
|
|
17
19
|
// Регистр — часть внешнего контракта, менять его нельзя: он зашит в чужие сценарии.
|
|
18
20
|
export const CAMPAIGN_STATUS_ACTIONS = ["SUSPEND", "RESUME", "ARCHIVE", "UNARCHIVE"];
|
|
@@ -82,15 +84,30 @@ export const ASSOCIATED_FLAGS = ["YES", "NO"];
|
|
|
82
84
|
// RetargetingListTypeEnum и RetargetingListRuleOperatorEnum.
|
|
83
85
|
export const RETARGETING_TYPES = ["RETARGETING", "AUDIENCE"];
|
|
84
86
|
export const RETARGETING_RULE_OPERATORS = ["ALL", "ANY", "NONE"];
|
|
85
|
-
//
|
|
86
|
-
//
|
|
87
|
+
// BidModifierTypeEnum целиком: тем же набором фильтрует get_bid_adjustments и выбирает
|
|
88
|
+
// вид корректировки add_bid_adjustments — в API это один список.
|
|
87
89
|
export const BID_ADJUSTMENT_TYPES = [
|
|
88
90
|
"MOBILE_ADJUSTMENT",
|
|
89
91
|
"TABLET_ADJUSTMENT",
|
|
90
92
|
"DESKTOP_ADJUSTMENT",
|
|
91
93
|
"DESKTOP_ONLY_ADJUSTMENT",
|
|
92
|
-
"
|
|
94
|
+
"SMART_TV_ADJUSTMENT",
|
|
95
|
+
"DEMOGRAPHICS_ADJUSTMENT",
|
|
96
|
+
"RETARGETING_ADJUSTMENT",
|
|
97
|
+
"REGIONAL_ADJUSTMENT",
|
|
98
|
+
"VIDEO_ADJUSTMENT",
|
|
99
|
+
"SMART_AD_ADJUSTMENT",
|
|
100
|
+
"SERP_LAYOUT_ADJUSTMENT",
|
|
101
|
+
"INCOME_GRADE_ADJUSTMENT",
|
|
102
|
+
"AD_GROUP_ADJUSTMENT"
|
|
93
103
|
];
|
|
104
|
+
// Срезы, на которые вешается корректировка: OperatingSystemTypeEnum, GenderEnum,
|
|
105
|
+
// AgeRangeEnum, SerpLayoutEnum и IncomeGradeEnum из general.xsd.
|
|
106
|
+
export const OPERATING_SYSTEM_TYPES = ["IOS", "ANDROID"];
|
|
107
|
+
export const GENDERS = ["GENDER_MALE", "GENDER_FEMALE"];
|
|
108
|
+
export const AGE_RANGES = ["AGE_0_17", "AGE_18_24", "AGE_25_34", "AGE_35_44", "AGE_45", "AGE_45_54", "AGE_55"];
|
|
109
|
+
export const SERP_LAYOUTS = ["ALONE", "SUGGEST"];
|
|
110
|
+
export const INCOME_GRADES = ["VERY_HIGH", "HIGH", "ABOVE_AVERAGE"];
|
|
94
111
|
// BidModifierLevelEnum.
|
|
95
112
|
export const BID_ADJUSTMENT_LEVELS = ["CAMPAIGN", "AD_GROUP"];
|
|
96
113
|
// AudienceTargetStateEnum и действия сервиса audiencetargets (setBids — наш set_bids).
|
|
@@ -103,8 +120,9 @@ export const STRATEGY_PRIORITIES = ["LOW", "NORMAL", "HIGH"];
|
|
|
103
120
|
export const DYNAMIC_TARGET_ACTIONS = ["add", "set_bids", "suspend", "resume", "delete"];
|
|
104
121
|
export const WEBPAGE_CONDITION_OPERANDS = ["URL", "DOMAIN", "PAGE_TITLE", "PAGE_CONTENT", "OFFERS_LIST_URL"];
|
|
105
122
|
export const WEBPAGE_CONDITION_OPERATORS = ["EQUALS_ANY", "NOT_EQUALS_ALL", "CONTAINS_ANY", "NOT_CONTAINS_ALL"];
|
|
106
|
-
// Режимы get_changes (наши имена методов checkCampaigns/check)
|
|
107
|
-
|
|
123
|
+
// Режимы get_changes (наши имена методов checkCampaigns/check/checkDictionaries)
|
|
124
|
+
// и CheckFieldEnum.
|
|
125
|
+
export const CHANGES_MODES = ["campaigns", "objects", "dictionaries"];
|
|
108
126
|
export const CHANGES_FIELD_NAMES = ["CampaignIds", "AdGroupIds", "AdIds", "CampaignsStat"];
|
|
109
127
|
// Стратегии, которые умеет выставлять set_strategy. Список по-прежнему уже полного
|
|
110
128
|
// (в TextCampaignStrategyBase двенадцать структур настроек), но покрывает переход
|
|
@@ -32,10 +32,16 @@ export declare const RETARGETING_LIMITS: {
|
|
|
32
32
|
readonly description: 4096;
|
|
33
33
|
readonly membershipDays: 540;
|
|
34
34
|
};
|
|
35
|
+
export declare const MAX_RETARGETING_LISTS_PER_CALL = 1000;
|
|
36
|
+
export declare const KEYWORD_TEXT_MAX = 4096;
|
|
37
|
+
export declare const KEYWORD_USER_PARAM_MAX = 255;
|
|
38
|
+
export declare const MAX_KEYWORDS_PER_UPDATE = 1000;
|
|
39
|
+
export declare const MAX_SITELINK_SETS_PER_CALL = 1000;
|
|
35
40
|
export declare const BID_MODIFIER_RANGE: {
|
|
36
41
|
readonly min: 0;
|
|
37
42
|
readonly max: 1300;
|
|
38
43
|
};
|
|
44
|
+
export declare const MAX_ADJUSTMENTS_PER_CALL = 1000;
|
|
39
45
|
export declare const MAX_CAMPAIGNS_PER_ADJUSTMENT_CALL = 10;
|
|
40
46
|
export declare const MAX_CAMPAIGNS_PER_AUDIENCE_CALL = 100;
|
|
41
47
|
export declare const MAX_CAMPAIGNS_PER_CHANGES_CALL = 3000;
|
|
@@ -30,8 +30,20 @@ export const AD_IMAGE_NAME_MAX = 255;
|
|
|
30
30
|
export const MAX_IMAGES_PER_CALL = 100;
|
|
31
31
|
// Ретаргетинг: длины полей и срок учёта цели.
|
|
32
32
|
export const RETARGETING_LIMITS = { name: 250, description: 4096, membershipDays: 540 };
|
|
33
|
+
// Сколько условий ретаргетинга принимают update и delete за вызов.
|
|
34
|
+
export const MAX_RETARGETING_LISTS_PER_CALL = 1_000;
|
|
35
|
+
// Ключевые фразы: длина фразы, подстановочные переменные {param1} и {param2},
|
|
36
|
+
// размер пакета update.
|
|
37
|
+
export const KEYWORD_TEXT_MAX = 4_096;
|
|
38
|
+
export const KEYWORD_USER_PARAM_MAX = 255;
|
|
39
|
+
export const MAX_KEYWORDS_PER_UPDATE = 1_000;
|
|
40
|
+
// Наборов быстрых ссылок за вызов — не больше тысячи. Для визиток такого числа в
|
|
41
|
+
// документации нет (страниц VCards там не осталось вовсе), поэтому у них общий потолок.
|
|
42
|
+
export const MAX_SITELINK_SETS_PER_CALL = 1_000;
|
|
33
43
|
// Коэффициент корректировки ставки, в процентах.
|
|
34
44
|
export const BID_MODIFIER_RANGE = { min: 0, max: 1300 };
|
|
45
|
+
// BidModifiers.add: не больше тысячи корректировок за вызов.
|
|
46
|
+
export const MAX_ADJUSTMENTS_PER_CALL = 1_000;
|
|
35
47
|
// Корректировки читаются не больше чем по десяти кампаниям за вызов.
|
|
36
48
|
export const MAX_CAMPAIGNS_PER_ADJUSTMENT_CALL = 10;
|
|
37
49
|
// Аудиторные цели читаются не больше чем по сотне кампаний за вызов.
|
|
@@ -1,4 +1,6 @@
|
|
|
1
1
|
import type { z } from "zod";
|
|
2
|
-
import type { getBidAdjustmentsSchema, setBidAdjustmentsSchema } from "./schema.js";
|
|
2
|
+
import type { addBidAdjustmentsSchema, deleteBidAdjustmentsSchema, getBidAdjustmentsSchema, setBidAdjustmentsSchema } from "./schema.js";
|
|
3
3
|
export declare function handleGetBidAdjustments(params: z.infer<typeof getBidAdjustmentsSchema>): Promise<string>;
|
|
4
4
|
export declare function handleSetBidAdjustments(params: z.infer<typeof setBidAdjustmentsSchema>): Promise<string>;
|
|
5
|
+
export declare function handleAddBidAdjustments(params: z.infer<typeof addBidAdjustmentsSchema>): Promise<string>;
|
|
6
|
+
export declare function handleDeleteBidAdjustments(params: z.infer<typeof deleteBidAdjustmentsSchema>): Promise<string>;
|