yandex-webmaster-mcp 1.0.0 → 1.2.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 +20 -0
- package/package.json +3 -3
- package/src/index.mjs +395 -1
package/README.md
CHANGED
|
@@ -23,6 +23,8 @@ MCP server for [Yandex Webmaster API](https://yandex.com/dev/webmaster/doc/en/)
|
|
|
23
23
|
| `get-diagnostics` | Site health diagnostics |
|
|
24
24
|
| `get-popular-queries` | Popular search queries (shows, clicks, positions) |
|
|
25
25
|
| `get-query-history` | Aggregated query statistics over time |
|
|
26
|
+
| `get-single-query-history` | Statistics over time for one specific query |
|
|
27
|
+
| `get-query-analytics` | Query↔URL intersection report (last ~2 weeks) |
|
|
26
28
|
| `get-indexing-history` | Pages downloaded by robot (by HTTP status) |
|
|
27
29
|
| `get-indexing-samples` | Examples of downloaded pages |
|
|
28
30
|
| `get-insearch-history` | Pages in search results over time |
|
|
@@ -36,9 +38,17 @@ MCP server for [Yandex Webmaster API](https://yandex.com/dev/webmaster/doc/en/)
|
|
|
36
38
|
| `get-sitemaps` | Auto-detected sitemap files |
|
|
37
39
|
| `get-sitemap` | Specific sitemap details |
|
|
38
40
|
| `get-user-sitemaps` | User-added sitemaps |
|
|
41
|
+
| `get-user-sitemap` | Details of a single user-added sitemap |
|
|
39
42
|
| `get-important-urls` | Monitored important pages |
|
|
40
43
|
| `get-important-url-history` | Important page change history |
|
|
41
44
|
| `get-recrawl-quota` | Reindexing quota status |
|
|
45
|
+
| `get-recrawl-queue` | List of pending/recent recrawl tasks |
|
|
46
|
+
| `get-recrawl-task` | Status of a specific recrawl task |
|
|
47
|
+
| `submit-recrawl` | Queue a URL for reindexing (write; uses quota) |
|
|
48
|
+
| `get-feeds` | Data feeds loaded for the site |
|
|
49
|
+
| `get-feed-status` | Status of an async feed upload task |
|
|
50
|
+
| `get-feed-regions` | Regions supported for feed uploads |
|
|
51
|
+
| `get-region-ids` | Static reference of common Yandex region IDs |
|
|
42
52
|
|
|
43
53
|
### Installation
|
|
44
54
|
|
|
@@ -143,6 +153,8 @@ Set the token as `YANDEX_WEBMASTER_TOKEN` environment variable.
|
|
|
143
153
|
| `get-diagnostics` | Диагностика проблем сайта |
|
|
144
154
|
| `get-popular-queries` | Популярные поисковые запросы |
|
|
145
155
|
| `get-query-history` | История статистики запросов |
|
|
156
|
+
| `get-single-query-history` | История статистики по конкретному запросу |
|
|
157
|
+
| `get-query-analytics` | Отчёт пересечения запросов и URL (~2 недели) |
|
|
146
158
|
| `get-indexing-history` | История загрузки страниц роботом |
|
|
147
159
|
| `get-indexing-samples` | Примеры загруженных страниц |
|
|
148
160
|
| `get-insearch-history` | История страниц в поиске |
|
|
@@ -156,9 +168,17 @@ Set the token as `YANDEX_WEBMASTER_TOKEN` environment variable.
|
|
|
156
168
|
| `get-sitemaps` | Обнаруженные файлы Sitemap |
|
|
157
169
|
| `get-sitemap` | Детали конкретного Sitemap |
|
|
158
170
|
| `get-user-sitemaps` | Добавленные пользователем Sitemap |
|
|
171
|
+
| `get-user-sitemap` | Детали конкретного добавленного Sitemap |
|
|
159
172
|
| `get-important-urls` | Мониторинг важных страниц |
|
|
160
173
|
| `get-important-url-history` | История изменений важных страниц |
|
|
161
174
|
| `get-recrawl-quota` | Квота на переобход |
|
|
175
|
+
| `get-recrawl-queue` | Список задач на переобход |
|
|
176
|
+
| `get-recrawl-task` | Статус задачи на переобход |
|
|
177
|
+
| `submit-recrawl` | Отправить URL на переобход (запись; расходует квоту) |
|
|
178
|
+
| `get-feeds` | Загруженные фиды сайта |
|
|
179
|
+
| `get-feed-status` | Статус асинхронной загрузки фида |
|
|
180
|
+
| `get-feed-regions` | Регионы, доступные для загрузки фидов |
|
|
181
|
+
| `get-region-ids` | Справочник частых ID регионов Яндекса |
|
|
162
182
|
|
|
163
183
|
### Установка
|
|
164
184
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "yandex-webmaster-mcp",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.2.0",
|
|
4
4
|
"description": "MCP server for Yandex Webmaster API - site analytics, indexing status, search queries, and SEO diagnostics",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "src/index.mjs",
|
|
@@ -43,7 +43,7 @@
|
|
|
43
43
|
"node": ">=18.0.0"
|
|
44
44
|
},
|
|
45
45
|
"dependencies": {
|
|
46
|
-
"@modelcontextprotocol/sdk": "^1.
|
|
47
|
-
"zod": "^4.3
|
|
46
|
+
"@modelcontextprotocol/sdk": "^1.29.0",
|
|
47
|
+
"zod": "^4.4.3"
|
|
48
48
|
}
|
|
49
49
|
}
|
package/src/index.mjs
CHANGED
|
@@ -70,7 +70,111 @@ async function runServer() {
|
|
|
70
70
|
return new Date(date).toISOString();
|
|
71
71
|
}
|
|
72
72
|
|
|
73
|
-
|
|
73
|
+
// Static reference of common Yandex geo region IDs. Used for the region_ids
|
|
74
|
+
// parameter of query-analytics and feeds tools. Yandex has thousands of
|
|
75
|
+
// regions; this is a curated shortlist of the most commonly used ones.
|
|
76
|
+
const REGION_IDS = [
|
|
77
|
+
{ id: 225, name: 'Russia' },
|
|
78
|
+
{ id: 213, name: 'Moscow' },
|
|
79
|
+
{ id: 2, name: 'Saint Petersburg' },
|
|
80
|
+
{ id: 1, name: 'Moscow and Moscow Oblast' },
|
|
81
|
+
{ id: 10174, name: 'Saint Petersburg and Leningrad Oblast' },
|
|
82
|
+
{ id: 65, name: 'Novosibirsk' },
|
|
83
|
+
{ id: 54, name: 'Yekaterinburg' },
|
|
84
|
+
{ id: 47, name: 'Nizhny Novgorod' },
|
|
85
|
+
{ id: 43, name: 'Kazan' },
|
|
86
|
+
{ id: 56, name: 'Chelyabinsk' },
|
|
87
|
+
{ id: 51, name: 'Samara' },
|
|
88
|
+
{ id: 66, name: 'Omsk' },
|
|
89
|
+
{ id: 39, name: 'Rostov-on-Don' },
|
|
90
|
+
{ id: 172, name: 'Ufa' },
|
|
91
|
+
{ id: 35, name: 'Krasnodar' },
|
|
92
|
+
{ id: 62, name: 'Krasnoyarsk' },
|
|
93
|
+
{ id: 193, name: 'Voronezh' },
|
|
94
|
+
{ id: 63, name: 'Volgograd' },
|
|
95
|
+
{ id: 187, name: 'Ukraine' },
|
|
96
|
+
{ id: 143, name: 'Kyiv' },
|
|
97
|
+
{ id: 149, name: 'Belarus' },
|
|
98
|
+
{ id: 157, name: 'Minsk' },
|
|
99
|
+
{ id: 159, name: 'Kazakhstan' },
|
|
100
|
+
{ id: 162, name: 'Almaty' },
|
|
101
|
+
{ id: 10393, name: 'Nur-Sultan (Astana)' },
|
|
102
|
+
{ id: 983, name: 'Uzbekistan' },
|
|
103
|
+
{ id: 10295, name: 'Armenia' },
|
|
104
|
+
{ id: 10277, name: 'Azerbaijan' },
|
|
105
|
+
{ id: 20544, name: 'Georgia' },
|
|
106
|
+
{ id: 207, name: 'Kyrgyzstan' },
|
|
107
|
+
];
|
|
108
|
+
|
|
109
|
+
// Static reference of regions available for YML feed uploads (feeds tools).
|
|
110
|
+
// Per the Yandex docs, feeds only support Russia (225, the default) and the
|
|
111
|
+
// Russian regions below — there is no API endpoint for this list.
|
|
112
|
+
// Source: https://yandex.com/dev/webmaster/doc/en/reference/feeds-regions
|
|
113
|
+
const FEED_REGIONS = [
|
|
114
|
+
{ id: 225, name: 'Россия (default)' },
|
|
115
|
+
{ id: 20, name: 'Архангельск' },
|
|
116
|
+
{ id: 37, name: 'Астрахань' },
|
|
117
|
+
{ id: 197, name: 'Барнаул' },
|
|
118
|
+
{ id: 4, name: 'Белгород' },
|
|
119
|
+
{ id: 77, name: 'Благовещенск' },
|
|
120
|
+
{ id: 191, name: 'Брянск' },
|
|
121
|
+
{ id: 24, name: 'Великий Новгород' },
|
|
122
|
+
{ id: 75, name: 'Владивосток' },
|
|
123
|
+
{ id: 33, name: 'Владикавказ' },
|
|
124
|
+
{ id: 192, name: 'Владимир' },
|
|
125
|
+
{ id: 38, name: 'Волгоград' },
|
|
126
|
+
{ id: 21, name: 'Вологда' },
|
|
127
|
+
{ id: 193, name: 'Воронеж' },
|
|
128
|
+
{ id: 1106, name: 'Грозный' },
|
|
129
|
+
{ id: 54, name: 'Екатеринбург' },
|
|
130
|
+
{ id: 5, name: 'Иваново' },
|
|
131
|
+
{ id: 63, name: 'Иркутск' },
|
|
132
|
+
{ id: 41, name: 'Йошкар-Ола' },
|
|
133
|
+
{ id: 43, name: 'Казань' },
|
|
134
|
+
{ id: 22, name: 'Калининград' },
|
|
135
|
+
{ id: 64, name: 'Кемерово' },
|
|
136
|
+
{ id: 7, name: 'Кострома' },
|
|
137
|
+
{ id: 35, name: 'Краснодар' },
|
|
138
|
+
{ id: 62, name: 'Красноярск' },
|
|
139
|
+
{ id: 53, name: 'Курган' },
|
|
140
|
+
{ id: 8, name: 'Курск' },
|
|
141
|
+
{ id: 9, name: 'Липецк' },
|
|
142
|
+
{ id: 28, name: 'Махачкала' },
|
|
143
|
+
{ id: 1, name: 'Москва и Московская область' },
|
|
144
|
+
{ id: 213, name: 'Москва' },
|
|
145
|
+
{ id: 1092, name: 'Назрань' },
|
|
146
|
+
{ id: 30, name: 'Нальчик' },
|
|
147
|
+
{ id: 47, name: 'Нижний Новгород' },
|
|
148
|
+
{ id: 65, name: 'Новосибирск' },
|
|
149
|
+
{ id: 66, name: 'Омск' },
|
|
150
|
+
{ id: 10, name: 'Орел' },
|
|
151
|
+
{ id: 48, name: 'Оренбург' },
|
|
152
|
+
{ id: 49, name: 'Пенза' },
|
|
153
|
+
{ id: 50, name: 'Пермь' },
|
|
154
|
+
{ id: 25, name: 'Псков' },
|
|
155
|
+
{ id: 39, name: 'Ростов-на-Дону' },
|
|
156
|
+
{ id: 11, name: 'Рязань' },
|
|
157
|
+
{ id: 51, name: 'Самара' },
|
|
158
|
+
{ id: 2, name: 'Санкт-Петербург' },
|
|
159
|
+
{ id: 42, name: 'Саранск' },
|
|
160
|
+
{ id: 12, name: 'Смоленск' },
|
|
161
|
+
{ id: 239, name: 'Сочи' },
|
|
162
|
+
{ id: 36, name: 'Ставрополь' },
|
|
163
|
+
{ id: 973, name: 'Сургут' },
|
|
164
|
+
{ id: 13, name: 'Тамбов' },
|
|
165
|
+
{ id: 14, name: 'Тверь' },
|
|
166
|
+
{ id: 67, name: 'Томск' },
|
|
167
|
+
{ id: 15, name: 'Тула' },
|
|
168
|
+
{ id: 195, name: 'Ульяновск' },
|
|
169
|
+
{ id: 172, name: 'Уфа' },
|
|
170
|
+
{ id: 76, name: 'Хабаровск' },
|
|
171
|
+
{ id: 45, name: 'Чебоксары' },
|
|
172
|
+
{ id: 56, name: 'Челябинск' },
|
|
173
|
+
{ id: 1104, name: 'Черкесск' },
|
|
174
|
+
{ id: 16, name: 'Ярославль' },
|
|
175
|
+
];
|
|
176
|
+
|
|
177
|
+
const server = new McpServer({ name: 'yandex-webmaster', version: '1.0.0' });
|
|
74
178
|
|
|
75
179
|
// ============ Core Tools ============
|
|
76
180
|
|
|
@@ -137,6 +241,23 @@ async function runServer() {
|
|
|
137
241
|
},
|
|
138
242
|
);
|
|
139
243
|
|
|
244
|
+
server.registerTool(
|
|
245
|
+
'get-region-ids',
|
|
246
|
+
{
|
|
247
|
+
title: 'Get Region IDs Reference',
|
|
248
|
+
description:
|
|
249
|
+
'Returns a static reference list of common Yandex geo region IDs (with names) for use in the region_ids parameter of get-query-analytics and feeds tools. No API call is made.',
|
|
250
|
+
inputSchema: {},
|
|
251
|
+
},
|
|
252
|
+
async () => {
|
|
253
|
+
const text = REGION_IDS.map((r) => `- ${r.id}: ${r.name}`).join('\n');
|
|
254
|
+
return {
|
|
255
|
+
content: [{ type: 'text', text: `Common Yandex region IDs:\n\n${text}` }],
|
|
256
|
+
structuredContent: { regions: REGION_IDS },
|
|
257
|
+
};
|
|
258
|
+
},
|
|
259
|
+
);
|
|
260
|
+
|
|
140
261
|
// ============ Statistics & Summary ============
|
|
141
262
|
|
|
142
263
|
server.registerTool(
|
|
@@ -330,6 +451,102 @@ async function runServer() {
|
|
|
330
451
|
},
|
|
331
452
|
);
|
|
332
453
|
|
|
454
|
+
server.registerTool(
|
|
455
|
+
'get-single-query-history',
|
|
456
|
+
{
|
|
457
|
+
title: 'Get Single Query History',
|
|
458
|
+
description:
|
|
459
|
+
'Returns statistics over time (shows, clicks, positions) for one specific search query. Use get-popular-queries to obtain a query_id.',
|
|
460
|
+
inputSchema: {
|
|
461
|
+
host_id: z.string().describe('Site identifier'),
|
|
462
|
+
query_id: z.string().describe('Search query identifier (from get-popular-queries)'),
|
|
463
|
+
device_type: z
|
|
464
|
+
.enum(['ALL', 'DESKTOP', 'MOBILE', 'TABLET', 'MOBILE_AND_TABLET'])
|
|
465
|
+
.optional()
|
|
466
|
+
.describe('Device filter (default: ALL)'),
|
|
467
|
+
date_from: z.string().optional().describe('Start date (YYYY-MM-DD)'),
|
|
468
|
+
date_to: z.string().optional().describe('End date (YYYY-MM-DD)'),
|
|
469
|
+
},
|
|
470
|
+
},
|
|
471
|
+
async ({ host_id, query_id, device_type, date_from, date_to }) => {
|
|
472
|
+
let endpoint = await hostEndpoint(host_id, `/search-queries/${encodeURIComponent(query_id)}/history`);
|
|
473
|
+
const params = new URLSearchParams();
|
|
474
|
+
params.set('query_indicator', 'TOTAL_SHOWS');
|
|
475
|
+
params.append('query_indicator', 'TOTAL_CLICKS');
|
|
476
|
+
params.append('query_indicator', 'AVG_SHOW_POSITION');
|
|
477
|
+
if (device_type) params.set('device_type_indicator', device_type);
|
|
478
|
+
if (date_from) params.set('date_from', formatDate(date_from));
|
|
479
|
+
if (date_to) params.set('date_to', formatDate(date_to));
|
|
480
|
+
endpoint += `?${params}`;
|
|
481
|
+
|
|
482
|
+
const data = await apiRequest(endpoint);
|
|
483
|
+
|
|
484
|
+
const shows = data.indicators?.TOTAL_SHOWS || [];
|
|
485
|
+
const text = shows
|
|
486
|
+
.slice(-14)
|
|
487
|
+
.map((p) => `${p.date.split('T')[0]}: ${p.value.toLocaleString()} shows`)
|
|
488
|
+
.join('\n');
|
|
489
|
+
|
|
490
|
+
return {
|
|
491
|
+
content: [{ type: 'text', text: `Query history (last 14 days shown):\n\n${text || 'No data'}` }],
|
|
492
|
+
structuredContent: data,
|
|
493
|
+
};
|
|
494
|
+
},
|
|
495
|
+
);
|
|
496
|
+
|
|
497
|
+
server.registerTool(
|
|
498
|
+
'get-query-analytics',
|
|
499
|
+
{
|
|
500
|
+
title: 'Get Query Analytics',
|
|
501
|
+
description:
|
|
502
|
+
'Returns the query↔URL intersection report (last ~2 weeks) with shows, clicks, CTR and positions, grouped by URL or query. Supports device, region and custom filters. Note: this endpoint is not part of the public API reference index.',
|
|
503
|
+
inputSchema: {
|
|
504
|
+
host_id: z.string().describe('Site identifier'),
|
|
505
|
+
text_indicator: z
|
|
506
|
+
.enum(['URL', 'QUERY'])
|
|
507
|
+
.optional()
|
|
508
|
+
.describe('Group results by URL or by query text (default: URL)'),
|
|
509
|
+
device_type_indicator: z
|
|
510
|
+
.enum(['ALL', 'DESKTOP', 'MOBILE', 'TABLET', 'MOBILE_AND_TABLET'])
|
|
511
|
+
.optional()
|
|
512
|
+
.describe('Device filter (default: ALL)'),
|
|
513
|
+
region_ids: z.array(z.number()).optional().describe('Filter by Yandex region IDs (see get-region-ids)'),
|
|
514
|
+
limit: z.number().min(1).max(500).optional().describe('Number of rows (default: 20)'),
|
|
515
|
+
offset: z.number().min(0).optional().describe('Offset for pagination'),
|
|
516
|
+
filters: z.record(z.any()).optional().describe('Optional API filters object (advanced, passed through as-is)'),
|
|
517
|
+
sort_by_date: z
|
|
518
|
+
.record(z.any())
|
|
519
|
+
.optional()
|
|
520
|
+
.describe('Optional sort_by_date object (advanced, passed through as-is)'),
|
|
521
|
+
},
|
|
522
|
+
},
|
|
523
|
+
async ({ host_id, text_indicator, device_type_indicator, region_ids, limit, offset, filters, sort_by_date }) => {
|
|
524
|
+
const endpoint = await hostEndpoint(host_id, '/query-analytics/list');
|
|
525
|
+
const body = {
|
|
526
|
+
text_indicator: text_indicator || 'URL',
|
|
527
|
+
device_type_indicator: device_type_indicator || 'ALL',
|
|
528
|
+
limit: limit ?? 20,
|
|
529
|
+
offset: offset ?? 0,
|
|
530
|
+
};
|
|
531
|
+
if (region_ids) body.region_ids = region_ids;
|
|
532
|
+
if (filters) body.filters = filters;
|
|
533
|
+
if (sort_by_date) body.sort_by_date = sort_by_date;
|
|
534
|
+
|
|
535
|
+
const data = await apiRequest(endpoint, { method: 'POST', body });
|
|
536
|
+
|
|
537
|
+
const rows = data.text_indicator_to_statistics || data.rows || [];
|
|
538
|
+
return {
|
|
539
|
+
content: [
|
|
540
|
+
{
|
|
541
|
+
type: 'text',
|
|
542
|
+
text: `Query analytics retrieved (${Array.isArray(rows) ? rows.length : 0} rows). See structuredContent for details.`,
|
|
543
|
+
},
|
|
544
|
+
],
|
|
545
|
+
structuredContent: data,
|
|
546
|
+
};
|
|
547
|
+
},
|
|
548
|
+
);
|
|
549
|
+
|
|
333
550
|
// ============ Indexing ============
|
|
334
551
|
|
|
335
552
|
server.registerTool(
|
|
@@ -752,6 +969,32 @@ async function runServer() {
|
|
|
752
969
|
},
|
|
753
970
|
);
|
|
754
971
|
|
|
972
|
+
server.registerTool(
|
|
973
|
+
'get-user-sitemap',
|
|
974
|
+
{
|
|
975
|
+
title: 'Get User-Added Sitemap Info',
|
|
976
|
+
description: 'Returns detailed information about a single sitemap file that was manually added by the user.',
|
|
977
|
+
inputSchema: {
|
|
978
|
+
host_id: z.string().describe('Site identifier'),
|
|
979
|
+
sitemap_id: z.string().describe('User-added sitemap identifier (from get-user-sitemaps)'),
|
|
980
|
+
},
|
|
981
|
+
},
|
|
982
|
+
async ({ host_id, sitemap_id }) => {
|
|
983
|
+
const endpoint = await hostEndpoint(host_id, `/user-added-sitemaps/${encodeURIComponent(sitemap_id)}`);
|
|
984
|
+
const data = await apiRequest(endpoint);
|
|
985
|
+
|
|
986
|
+
return {
|
|
987
|
+
content: [
|
|
988
|
+
{
|
|
989
|
+
type: 'text',
|
|
990
|
+
text: `User-added sitemap: ${data.sitemap_url}\nAdded: ${data.added_date?.split('T')[0] || 'N/A'}\nURLs: ${data.urls_count ?? 'N/A'}\nErrors: ${data.errors_count ?? 'N/A'}`,
|
|
991
|
+
},
|
|
992
|
+
],
|
|
993
|
+
structuredContent: data,
|
|
994
|
+
};
|
|
995
|
+
},
|
|
996
|
+
);
|
|
997
|
+
|
|
755
998
|
// ============ Important URLs ============
|
|
756
999
|
|
|
757
1000
|
server.registerTool(
|
|
@@ -840,6 +1083,157 @@ async function runServer() {
|
|
|
840
1083
|
},
|
|
841
1084
|
);
|
|
842
1085
|
|
|
1086
|
+
server.registerTool(
|
|
1087
|
+
'get-recrawl-queue',
|
|
1088
|
+
{
|
|
1089
|
+
title: 'Get Recrawl Queue',
|
|
1090
|
+
description: 'Returns the list of pending/recent reindexing (recrawl) tasks submitted for the site.',
|
|
1091
|
+
inputSchema: {
|
|
1092
|
+
host_id: z.string().describe('Site identifier'),
|
|
1093
|
+
limit: z.number().min(1).max(100).optional().describe('Number of tasks (default: 10)'),
|
|
1094
|
+
offset: z.number().min(0).optional().describe('Offset for pagination'),
|
|
1095
|
+
},
|
|
1096
|
+
},
|
|
1097
|
+
async ({ host_id, limit = 10, offset }) => {
|
|
1098
|
+
let endpoint = await hostEndpoint(host_id, '/recrawl/queue');
|
|
1099
|
+
const params = new URLSearchParams();
|
|
1100
|
+
params.set('limit', limit.toString());
|
|
1101
|
+
if (offset) params.set('offset', offset.toString());
|
|
1102
|
+
endpoint += `?${params}`;
|
|
1103
|
+
|
|
1104
|
+
const data = await apiRequest(endpoint);
|
|
1105
|
+
|
|
1106
|
+
const tasks = data.tasks || [];
|
|
1107
|
+
const text = tasks
|
|
1108
|
+
.map((t) => `- ${t.task_id}: ${t.url} [${t.state}] (added: ${t.added_time?.split('T')[0] || 'N/A'})`)
|
|
1109
|
+
.join('\n');
|
|
1110
|
+
|
|
1111
|
+
return {
|
|
1112
|
+
content: [
|
|
1113
|
+
{ type: 'text', text: `Recrawl queue (${data.count ?? tasks.length} total):\n\n${text || 'No tasks'}` },
|
|
1114
|
+
],
|
|
1115
|
+
structuredContent: data,
|
|
1116
|
+
};
|
|
1117
|
+
},
|
|
1118
|
+
);
|
|
1119
|
+
|
|
1120
|
+
server.registerTool(
|
|
1121
|
+
'get-recrawl-task',
|
|
1122
|
+
{
|
|
1123
|
+
title: 'Get Recrawl Task Status',
|
|
1124
|
+
description: 'Returns the status of a specific reindexing (recrawl) task.',
|
|
1125
|
+
inputSchema: {
|
|
1126
|
+
host_id: z.string().describe('Site identifier'),
|
|
1127
|
+
task_id: z.string().describe('Recrawl task identifier (from submit-recrawl or get-recrawl-queue)'),
|
|
1128
|
+
},
|
|
1129
|
+
},
|
|
1130
|
+
async ({ host_id, task_id }) => {
|
|
1131
|
+
const endpoint = await hostEndpoint(host_id, `/recrawl/queue/${encodeURIComponent(task_id)}`);
|
|
1132
|
+
const data = await apiRequest(endpoint);
|
|
1133
|
+
|
|
1134
|
+
return {
|
|
1135
|
+
content: [
|
|
1136
|
+
{
|
|
1137
|
+
type: 'text',
|
|
1138
|
+
text: `Recrawl task ${data.task_id}\nURL: ${data.url}\nState: ${data.state}\nAdded: ${data.added_time?.split('T')[0] || 'N/A'}`,
|
|
1139
|
+
},
|
|
1140
|
+
],
|
|
1141
|
+
structuredContent: data,
|
|
1142
|
+
};
|
|
1143
|
+
},
|
|
1144
|
+
);
|
|
1145
|
+
|
|
1146
|
+
server.registerTool(
|
|
1147
|
+
'submit-recrawl',
|
|
1148
|
+
{
|
|
1149
|
+
title: 'Submit URL for Recrawl',
|
|
1150
|
+
description:
|
|
1151
|
+
'Queues a URL for reindexing by the Yandex robot. This is a WRITE operation that consumes the daily recrawl quota (check get-recrawl-quota). Returns a task_id that can be tracked via get-recrawl-task.',
|
|
1152
|
+
inputSchema: {
|
|
1153
|
+
host_id: z.string().describe('Site identifier'),
|
|
1154
|
+
url: z.string().describe('Full URL of the page to recrawl (must belong to the site)'),
|
|
1155
|
+
},
|
|
1156
|
+
},
|
|
1157
|
+
async ({ host_id, url }) => {
|
|
1158
|
+
const endpoint = await hostEndpoint(host_id, '/recrawl/queue');
|
|
1159
|
+
const data = await apiRequest(endpoint, { method: 'POST', body: { url } });
|
|
1160
|
+
|
|
1161
|
+
return {
|
|
1162
|
+
content: [{ type: 'text', text: `URL queued for recrawl.\nTask ID: ${data.task_id}\nURL: ${url}` }],
|
|
1163
|
+
structuredContent: data,
|
|
1164
|
+
};
|
|
1165
|
+
},
|
|
1166
|
+
);
|
|
1167
|
+
|
|
1168
|
+
// ============ Feeds ============
|
|
1169
|
+
|
|
1170
|
+
server.registerTool(
|
|
1171
|
+
'get-feeds',
|
|
1172
|
+
{
|
|
1173
|
+
title: 'Get Feeds',
|
|
1174
|
+
description: 'Returns the list of data feeds loaded for the site (enhanced site representation / фиды).',
|
|
1175
|
+
inputSchema: {
|
|
1176
|
+
host_id: z.string().describe('Site identifier'),
|
|
1177
|
+
},
|
|
1178
|
+
},
|
|
1179
|
+
async ({ host_id }) => {
|
|
1180
|
+
const endpoint = await hostEndpoint(host_id, '/feeds/list');
|
|
1181
|
+
const data = await apiRequest(endpoint);
|
|
1182
|
+
|
|
1183
|
+
const feeds = data.feeds || [];
|
|
1184
|
+
const text = feeds.map((f) => `- ${f.url || f.feed_url} [${f.state || f.status || 'N/A'}]`).join('\n');
|
|
1185
|
+
|
|
1186
|
+
return {
|
|
1187
|
+
content: [{ type: 'text', text: `Feeds (${feeds.length}):\n\n${text || 'No feeds'}` }],
|
|
1188
|
+
structuredContent: data,
|
|
1189
|
+
};
|
|
1190
|
+
},
|
|
1191
|
+
);
|
|
1192
|
+
|
|
1193
|
+
server.registerTool(
|
|
1194
|
+
'get-feed-status',
|
|
1195
|
+
{
|
|
1196
|
+
title: 'Get Feed Upload Status',
|
|
1197
|
+
description: 'Returns the status of an asynchronous feed upload task.',
|
|
1198
|
+
inputSchema: {
|
|
1199
|
+
host_id: z.string().describe('Site identifier'),
|
|
1200
|
+
task_id: z.string().describe('Feed upload task identifier'),
|
|
1201
|
+
},
|
|
1202
|
+
},
|
|
1203
|
+
async ({ host_id, task_id }) => {
|
|
1204
|
+
let endpoint = await hostEndpoint(host_id, '/feeds/add/info');
|
|
1205
|
+
const params = new URLSearchParams();
|
|
1206
|
+
params.set('task_id', task_id);
|
|
1207
|
+
endpoint += `?${params}`;
|
|
1208
|
+
|
|
1209
|
+
const data = await apiRequest(endpoint);
|
|
1210
|
+
|
|
1211
|
+
return {
|
|
1212
|
+
content: [
|
|
1213
|
+
{ type: 'text', text: `Feed upload status: ${data.state || data.status || 'see structuredContent'}` },
|
|
1214
|
+
],
|
|
1215
|
+
structuredContent: data,
|
|
1216
|
+
};
|
|
1217
|
+
},
|
|
1218
|
+
);
|
|
1219
|
+
|
|
1220
|
+
server.registerTool(
|
|
1221
|
+
'get-feed-regions',
|
|
1222
|
+
{
|
|
1223
|
+
title: 'Get Feed Regions',
|
|
1224
|
+
description:
|
|
1225
|
+
'Returns the static reference list of regions that can be specified when uploading a YML feed (Russia is the default, id 225). This is a documentation reference, not a live API call.',
|
|
1226
|
+
inputSchema: {},
|
|
1227
|
+
},
|
|
1228
|
+
async () => {
|
|
1229
|
+
const text = FEED_REGIONS.map((r) => `- ${r.id}: ${r.name}`).join('\n');
|
|
1230
|
+
return {
|
|
1231
|
+
content: [{ type: 'text', text: `Feed upload regions:\n\n${text}` }],
|
|
1232
|
+
structuredContent: { regions: FEED_REGIONS },
|
|
1233
|
+
};
|
|
1234
|
+
},
|
|
1235
|
+
);
|
|
1236
|
+
|
|
843
1237
|
// Start server
|
|
844
1238
|
const transport = new StdioServerTransport();
|
|
845
1239
|
await server.connect(transport);
|