MCP Сервер cenaly.ru — Справочник для AI / GPT интеграции
Этот документ предназначен для AI-ассистентов, GPT-ботов и автоматических интеграций. Содержит точные детали протокола, схемы инструментов и поток аутентификации.
Информация о сервере#
| Свойство | Значение |
|---|---|
| Имя сервера | meni-user-data (в serverInfo ответа initialize) |
| Протокол | MCP 2024-11-05 (Streamable HTTP) |
| Транспорт | HTTP POST (stateless) |
| Базовый URL | https://api.cenaly.ru/mcp |
| Content-Type | application/json |
| Формат | JSON-RPC 2.0 |
Аутентификация#
Все вызовы инструментов требуют заголовок Authorization.
Вариант 1: Пользовательский MCP API-ключ (рекомендуется)#
Authorization: Bearer XXXXXXXXXXXX... (64 hex-символа, без префикса)
Привязан к конкретному пользователю. Бот видит только данные этого пользователя.
Значение вида
mk_…, которое видно в списке ключей админ-панели, — это идентификатор ключа (используется вDELETE /api/keys/{keyId}), а не сам ключ.
Вариант 2: Cognito JWT-токен#
Authorization: Bearer eyJraWQi... (JWT id_token)
Получается через POST /auth/login. Истекает через 1 час.
Вариант 3: Admin API Key#
Authorization: Bearer <admin_api_key>
Полный доступ ко всем данным. Только для администраторов.
Получение JWT-токена#
POST https://api.cenaly.ru/mcp/auth/login
Content-Type: application/json
{
"email": "user@example.com",
"password": "password123"
}
Ответ:
{
"idToken": "eyJraWQi...",
"accessToken": "eyJraWQi...",
"refreshToken": "eyJjdHki...",
"expiresIn": 3600,
"tokenType": "Bearer"
}
Протокол MCP#
Инициализация#
POST https://api.cenaly.ru/mcp
Authorization: Bearer <token>
Content-Type: application/json
{
"jsonrpc": "2.0",
"id": 1,
"method": "initialize",
"params": {
"protocolVersion": "2024-11-05",
"capabilities": {},
"clientInfo": { "name": "my-bot", "version": "1.0" }
}
}
Список инструментов#
{ "jsonrpc": "2.0", "id": 2, "method": "tools/list", "params": {} }
Вызов инструмента#
{
"jsonrpc": "2.0",
"id": 3,
"method": "tools/call",
"params": {
"name": "имя_инструмента",
"arguments": { ... }
}
}
Ответ (успех): result.content[0].text — JSON-строка с данными.
Ответ (ошибка): result.isError: true, result.content[0].text — текст ошибки.
Эндпоинты#
| Метод | Путь | Авторизация | Описание |
|---|---|---|---|
GET |
/ |
Нет | Информация о сервере + список инструментов |
POST |
/ |
Да | MCP JSON-RPC |
GET |
/health |
Нет | Проверка → {"status":"ok"} |
POST |
/auth/login |
Нет | Логин → JWT-токены |
GET |
/api/keys |
JWT или свой MCP-ключ | Список API-ключей |
POST |
/api/keys |
JWT или свой MCP-ключ | Создать API-ключ |
DELETE |
/api/keys/{keyId} |
JWT или свой MCP-ключ | Отозвать API-ключ |
Маршрут
/api/keysавторизуется от имени пользователя: подойдёт и Cognito JWT, и собственный 64-символьный MCP-ключ. Не подойдёт администраторский ключ — у него нет привязки к пользователю, ответ будет403 user identity required.
Все пути относительно
https://api.cenaly.ru/mcp.
Полный справочник инструментов#
Уровни доступа#
- USER — Доступно всем авторизованным пользователям (данные по своему аккаунту)
- ADMIN — Только администраторы (полный доступ)
Самообслуживание#
| Инструмент | Доступ | Аргументы |
|---|---|---|
whoami |
USER | нет |
my_profile |
USER | нет |
update_my_profile |
USER | fields (object, обязательный) |
my_locations |
USER | нет |
my_orders |
USER | limit (int, опц.) |
my_images |
USER | type (enum: all, menu-photos, category-photos, locations, опц.) |
Профили пользователей#
| Инструмент | Доступ | Аргументы |
|---|---|---|
list_users |
ADMIN | limit (int, опц.) |
get_user_profile |
USER/ADMIN | userId (string, обяз.) |
update_user_profile |
USER/ADMIN | userId (string, обяз.), fields (object, обяз.) |
search_user_by_email |
ADMIN | email (string, обяз.) |
Локации#
| Инструмент | Доступ | Аргументы |
|---|---|---|
list_locations |
USER/ADMIN | userId (string, обяз.) |
get_location_profile |
USER/ADMIN | locationId (string, обяз.), userId (string, опц. — автоопределение для обычных) |
update_location_profile |
USER/ADMIN | locationId (string, обяз.), fields (object, обяз.), userId (string, опц.) |
get_location_menu |
USER/ADMIN | locationId (string, обяз.), language (string, опц.), userId (string, опц.) |
update_location_profile— обновляемые поля: displayName, phone, address, facebookUrl, instagramUrl, status, settings, workingHours. Синхронизируется с CDN автоматически. Для смены домена используйтеset_location_domain.
Меню#
| Инструмент | Доступ | Аргументы |
|---|---|---|
list_menu_items |
USER/ADMIN | categoryId (string, опц. — для полных данных), userId (string, опц.) |
get_menu_item |
USER/ADMIN | itemId (string, обяз.), categoryId (string, опц. — автопоиск), userId (string, опц.) |
update_menu_item |
USER/ADMIN | itemId (string, обяз.), fields (object, обяз.), categoryId (string, опц.), userId (string, опц.) |
create_menu_item |
USER/ADMIN | locationId, categoryId, itemId, name (string, обяз.), price (number, обяз.), nameTranslations (object, опц.), description (string, опц.), status (enum, опц.), sortOrder (number, опц.), userId (string, опц.) |
create_menu_category |
USER/ADMIN | locationId, categoryId, name (string, обяз.), nameTranslations (object, опц.), status (enum, опц.), sortOrder (number, опц.), userId (string, опц.) |
update_menu_category |
USER/ADMIN | locationId, categoryId (string, обяз.), name (string, опц.), nameTranslations (object, опц.), status (enum, опц.), sortOrder (number, опц.), userId (string, опц.) |
move_menu_item |
USER/ADMIN | locationId, itemId, fromCategoryId, toCategoryId (string, обяз.), userId (string, опц.) |
merge_categories |
USER/ADMIN | locationId, sourceCategoryId, targetCategoryId (string, обяз.), keepSourceName (bool, опц.), userId (string, опц.) |
delete_menu_category |
USER/ADMIN | locationId, categoryId (string, обяз.), force (bool, опц. — удалить с позициями), userId (string, опц.) |
check_translations |
USER/ADMIN | locationId (string, обяз.), languages (array, опц. — список языков для проверки), userId (string, опц.) |
check_translations— сканирует все категории и блюда локации, находит отсутствующие переводы на включённых языках и возвращает детальный отчёт по языкам с перечнем категорий и блюд без переводов.
Языки приложения#
| Инструмент | Доступ | Аргументы |
|---|---|---|
get_my_languages |
USER | нет |
set_my_languages |
USER | languages (string[], обяз. — полный список кодов включённых языков) |
reorder_my_languages |
USER | order (string[], обяз. — коды в нужном порядке; не указанные уходят в конец) |
Допустимые коды:
ka, en, ru, tr, hy, zh, hi, es, fr, ar, bn, pt, id, ur, de, ja, ko, vi, it, pl, uk, fa, he, az, kk, uz, ab, sq, sr, mk, bg, ro, el, nl, mt, sv, no, da, fi, cs, hu, hr, lt, th, ms.
Часы работы#
| Инструмент | Доступ | Аргументы |
|---|---|---|
set_working_hours |
USER/ADMIN | locationId (обяз.), days (string[], опц.), schedule (object, опц. — {closed, periods:[{open,close}]}), perDay (object, опц. — карта день→график, перекрывает days+schedule), userId (опц.) |
list_schedule_exceptions |
USER/ADMIN | locationId (обяз.), userId (опц.) |
add_schedule_exception |
USER/ADMIN | locationId, date (YYYY-MM-DD), isClosed (bool) — обяз.; timeFrom, timeTo (HH:MM, обяз. при isClosed:false), userId (опц.) |
remove_schedule_exception |
USER/ADMIN | locationId (обяз.), date или exceptionId, userId (опц.) |
Не указанные в
set_working_hoursдни остаются без изменений. Повторныйadd_schedule_exceptionна ту же дату заменяет существующее исключение.
update_menu_item— обновляемые поля: name, description, price, status, tags, variantGroups, addons, nameTranslations, descriptionTranslations, locationPrices, sortOrder. Синхронизируется с CDN автоматически.
Заказы#
| Инструмент | Доступ | Аргументы |
|---|---|---|
list_orders |
USER/ADMIN | userId (опц.), domain (опц.), limit (int, опц.) |
get_order |
USER/ADMIN | orderId (string, обяз.), domain (string, опц.), locationId (string, опц.) |
Домены#
| Инструмент | Доступ | Аргументы |
|---|---|---|
check_domain_availability |
USER | domainName (string, обяз.), currentDomainName (string, опц.) |
set_location_domain |
USER/ADMIN | locationId (string, обяз.), domainName (string, обяз.) |
resolve_domain |
USER | domain (string, обяз.) |
list_domains |
ADMIN | prefix (string, опц.) |
CDN#
| Инструмент | Доступ | Аргументы |
|---|---|---|
get_cdn_profile |
USER | domain (string, обяз.) |
get_cdn_menu |
USER | domain (string, обяз.), language (string, обяз.) |
list_cdn_files |
USER | domain (string, обяз.) |
invalidate_cdn_cache |
ADMIN | paths (string[], обяз.) |
Изображения#
| Инструмент | Доступ | Аргументы |
|---|---|---|
list_user_images |
USER/ADMIN | userId (string, обяз.), type (enum, опц.) |
get_image_upload_url |
USER | type (enum: menu-photos, category-photos, locations, обяз.), itemId (string, опц.), locationId (string, опц.), filename (string, опц.), contentType (enum, опц.) |
delete_image |
USER/ADMIN | key (string, обяз. — полный S3-ключ в i.cenaly.ru) |
get_image_upload_url— возвращает presigned URL (15 мин). После загрузки пайплайн автоматически генерирует миниатюры и синхронизирует с CDN.
S3 (хранилище)#
| Инструмент | Доступ | Аргументы |
|---|---|---|
s3_read |
USER/ADMIN | bucket (enum), key (string) — обяз. |
s3_write |
ADMIN | bucket (enum: data.meni, cdn.cenaly.ru, o.cenaly.ru), key (string), data (object) — обяз. |
s3_list |
USER/ADMIN | bucket (enum), prefix (string) — обяз., limit (int, опц.) |
s3_delete |
ADMIN | bucket (enum: data.meni, cdn.cenaly.ru, o.cenaly.ru), key (string) — обяз. |
Enum для bucket: data.meni, cdn.cenaly.ru, i.cenaly.ru, o.cenaly.ru
Cognito#
| Инструмент | Доступ | Аргументы |
|---|---|---|
cognito_list_users |
ADMIN | filter (string, опц.), limit (int, опц.) |
cognito_get_user |
ADMIN | username (string, обяз.) |
Система#
| Инструмент | Доступ | Аргументы |
|---|---|---|
get_system_stats |
ADMIN | нет |
Контроль доступа#
- Обычные пользователи — только свои данные (по userId)
- S3 доступ ограничен префиксом
users/{userId}/ - Для инструментов локаций/меню
userIdавтоопределяется для обычных пользователей (только админы указывают его) - Admin-only:
list_users,search_user_by_email,list_domains,invalidate_cdn_cache,s3_write,s3_delete,cognito_list_users,cognito_get_user,get_system_stats update_user_profileдоступен и обычному пользователю: аргументuserIdобязателен по схеме, но для не-админа сервер всегда подставляет его собственный идентификатор — чужой профиль этим инструментом не изменить- Управление ключами (
/api/keys) требует авторизации от имени пользователя: Cognito JWT или собственный MCP-ключ (администраторский ключ не подходит)
Коды ошибок#
| HTTP | Значение |
|---|---|
200 |
Успех (проверьте result.isError для ошибок инструментов) |
400 |
Невалидный JSON или отсутствует метод |
401 |
Отсутствует или невалидная аутентификация |
403 |
user identity required — обращение к /api/keys без привязки к пользователю (администраторским ключом) |
404 |
Неизвестный путь |
405 |
Неверный HTTP-метод |
| Ошибка инструмента | Значение |
|---|---|
🔒 admin access required |
Требуется роль админа |
🔒 access denied |
Попытка доступа к чужим данным |
missing required argument: <name> |
Обязательный параметр не передан |
unknown tool: <name> |
Неизвестный инструмент |
Пример сессии#
→ POST / {"jsonrpc":"2.0","id":1,"method":"initialize","params":{"protocolVersion":"2024-11-05","capabilities":{},"clientInfo":{"name":"bot","version":"1.0"}}}
← {"jsonrpc":"2.0","id":1,"result":{"protocolVersion":"2024-11-05","capabilities":{"tools":{}},"serverInfo":{"name":"meni-user-data","version":"1.0.0"}}}
→ POST / {"jsonrpc":"2.0","id":2,"method":"tools/call","params":{"name":"whoami","arguments":{}}}
← {"jsonrpc":"2.0","id":2,"result":{"content":[{"type":"text","text":"{\"userId\":\"abc-123\",\"email\":\"user@example.com\"}"}]}}
Конфигурация клиента#
Claude Desktop#
{
"mcpServers": {
"meni": {
"url": "https://api.cenaly.ru/mcp",
"headers": { "Authorization": "Bearer <API_KEY>" }
}
}
}
cURL#
curl -X POST https://api.cenaly.ru/mcp \
-H "Content-Type: application/json" \
-H "Authorization: Bearer <API_KEY>" \
-d '{"jsonrpc":"2.0","id":1,"method":"tools/call","params":{"name":"whoami","arguments":{}}}'