Skip to main content
Glama

iikocloud-mcp

MCP-сервер поверх Iikocloud-manager (IikoCloudApiClientManager): интроспекцией менеджера сервер отдаёт 236 методов iikoCloud в 22 доменах как MCP-тулы, но запускается с вариативно задаваемым подмножеством, а не целиком. Мультиарендный: учётные данные iikoCloud передаёт клиент через канал транспорта (HTTP-заголовки под TLS или переменные окружения для stdio) — секреты никогда не попадают в аргументы тулов, а значит и в контекст модели или логи.

Прямой аналог iikoserver-mcp: тот же принцип, та же модель безопасности, отличия — только там, где различаются сами API (auth v2 вместо логина/пароля, асинхронные команды с опросом, кэш справочников).

  • Транспорты: stdio и streamable-HTTP.

  • Отбор тулов: по домену, по типу операции (read/write), по именам/glob, из разных источников (CLI / env / YAML).

  • Безопасность по умолчанию: read-only; запись — явным опт-ином, под подтверждением.

Зачем подмножество, а не все 236 тулов

В контекст модели при каждом подключении идут и JSON-схемы тулов, и их описания — считать надо обе части (здесь и далее вес в символах: у схем это практически байты, у русских описаний в UTF-8 байт вдвое больше).

Подмножество

Тулов

Схемы

Описания

Итого в контекст

все

236

~356 КБ

~83 КБ

~439 КБ

только read

106

~95 КБ

~29 КБ

~124 КБ

домен invoice_processing

93

~114 КБ

~23 КБ

~137 КБ

Сервер сам вырезает из схем служебные поля, которые ничего не дают модели (pydantic-title, дублирующий имя свойства, и описания вида «Latitude.», буквально повторяющие имя поля): без обрезки схемы весили бы ~433 КБ вместо ~356 КБ, то есть экономия около 18% (на описания обрезка не влияет). Самый тяжёлый отдельный тул — discounts__calculate_loyalty_checkin, 18.9 КБ схемы. Из 236 методов 106 — read, 130 — write; ошибок схематизации при интроспекции — 0, регистрируются все.

Отсюда практический совет: выбирайте --domains под конкретную задачу клиента, а не поднимайте сервер со всем каталогом — это и экономит контекст модели, и сокращает поверхность записи.

Related MCP server: OpenAPI MCP Server

Установка

# как пакет в активированное окружение: даёт команду iikocloud-mcp на PATH
uv pip install "iikocloud-mcp @ git+https://github.com/UserVanya/Iikocloud-mcp.git"

# или для разработки: команда доступна только как `uv run iikocloud-mcp`,
# на PATH сама по себе она не появляется
git clone https://github.com/UserVanya/Iikocloud-mcp.git && cd Iikocloud-mcp && uv sync

Требуется Python 3.12+ и креды iikoCloud auth v2 (api_key, app_id, client_secret).

Примеры ниже написаны для второго (dev) пути, поэтому идут с префиксом uv run. Если пакет установлен первым способом и его окружение активировано, префикс не нужен.

Быстрый старт

# stdio: клиент запускает сервер как подпроцесс, креды — через env
IIKOCLOUD_API_KEY=key IIKOCLOUD_APP_ID=app IIKOCLOUD_CLIENT_SECRET=secret \
  uv run iikocloud-mcp --transport stdio --domains organizations,menu

# HTTP: сервер на VPS, только чтение по организациям и меню.
# Слушаем 127.0.0.1 — TLS терминирует reverse-proxy на этом же хосте.
uv run iikocloud-mcp --transport http --host 127.0.0.1 --port 8000 \
  --domains organizations,menu,dictionaries,addresses

--host 0.0.0.0 оправдан только если TLS-прокси работает на другом хосте: сам сервер говорит по HTTP без шифрования, а в каждом запросе едут X-Iikocloud-Api-Key, X-Iikocloud-App-Id и X-Iikocloud-Client-Secret. Открытый в интернет порт — это те же креды открытым текстом (см. Безопасность).

Тот же результат — через конфиг-файл (см. server.example.yml):

cp server.example.yml server.yml   # server.yml в .gitignore
uv run iikocloud-mcp --config server.yml

Отбор тулов

Тул включается, если: домен разрешён И тип операции разрешён И (нет allow ИЛИ имя совпало с allow) И имя не совпало с deny. deny всегда побеждает allow.

Имена тулов — <домен>__<метод> (например menu__get_nomenclature, deliveries__create_delivery_order). 22 домена: addresses, banquets, customer_categories, customers, deliveries, deliveries_retrieve, delivery_restrictions, dictionaries, discounts, drafts, employees, invoice_processing, marketing_sources, menu, messages, notifications, operations, orders, organizations, report, terminal_groups, webhooks.

Способ

CLI

env

YAML

Домены

--domains a,b

IIKOCLOUD_MCP_DOMAINS=a,b

domains: [a, b]

Разрешить запись

--allow-write

IIKOCLOUD_MCP_ALLOW_WRITE=1

operations: [read, write]

Allowlist имён/glob

--allow 'get_*' --allow '*_report'

IIKOCLOUD_MCP_ALLOW=get_*,*_report

allow: ["get_*"]

Denylist имён/glob

--deny 'delete_*'

IIKOCLOUD_MCP_DENY=delete_*

deny: ["delete_*"]

Выключить подтверждение записи

--no-confirm-writes

IIKOCLOUD_MCP_CONFIRM_WRITES=0

confirm_writes: false

Фолбэк без elicitation

--write-fallback open

IIKOCLOUD_MCP_WRITE_FALLBACK=open

write_fallback: open

Лимит JSON-ответа (символы)

--max-output-chars N

IIKOCLOUD_MCP_MAX_OUTPUT_CHARS=N

max_output_chars: N

Таймаут вызова, с

--call-timeout N

IIKOCLOUD_MCP_CALL_TIMEOUT=N

call_timeout: N

Потолок окна лимитера, с

--max-rate-window N

IIKOCLOUD_MCP_MAX_RATE_WINDOW=N

max_rate_window: N

TTL кэша справочников, с

--cache-ttl N

IIKOCLOUD_MCP_CACHE_TTL=N

cache_ttl: N

Потолок записей кэша

--cache-max-entries N

IIKOCLOUD_MCP_CACHE_MAX_ENTRIES=N

cache_max_entries: N

Хост / порт

--host / --port

IIKOCLOUD_MCP_HOST / IIKOCLOUD_MCP_PORT

host: / port:

Транспорт

--transport {stdio,http}

IIKOCLOUD_MCP_TRANSPORT

transport:

Приоритет источников: CLI > env > YAML-файл. Источник, задавший поле, заменяет его целиком (списки не мержаются). Путь к YAML — --config server.yml или IIKOCLOUD_MCP_CONFIG. См. server.example.yml.

--allow-write — это только включение записи со стороны CLI: чтобы выключить её обратно, просто не передавайте флаг. У env-переменной IIKOCLOUD_MCP_ALLOW_WRITE есть и включающее, и выключающее значение (1/0, true/false и т. п.).

Примеры:

# всё чтение по доставкам, но без карт лояльности
uv run iikocloud-mcp --transport http --domains deliveries,deliveries_retrieve \
  --deny '*loyalty*'

# запись включена, но без операций очистки
uv run iikocloud-mcp --transport http --allow-write --deny '*__clear_*'

Пустое значение — это осознанный ноль, а не «без ограничения»: --domains ''domains: [] в YAML) даёт сервер без единого тула, а не со всеми 236. Так значение из файла можно очистить из CLI. Про непонятое имя домена и про нулевую регистрацию сервер пишет в лог предупреждение.

Передача учётных данных

Секреты идут только по каналу транспорта, не как аргументы тулов — модель их не видит.

HTTP-заголовок

env для stdio

API-ключ

X-Iikocloud-Api-Key

IIKOCLOUD_API_KEY

App ID

X-Iikocloud-App-Id

IIKOCLOUD_APP_ID

Client secret

X-Iikocloud-Client-Secret

IIKOCLOUD_CLIENT_SECRET

Фолбэка на Authorization: Basic нет: он вмещает два секрета, а iikoCloud требует три.

Для HTTP-транспорта обязателен TLS — терминируйте HTTPS на reverse-proxy перед сервером, заголовки с кредами передавайте только под ним. Отсюда и дефолт --host 127.0.0.1: сам сервер шифрования не делает, поэтому наружу он должен смотреть только через прокси. stdio — переменные окружения подпроцесса (см. выше), сервер как локальный процесс отдельного TLS не требует.

Один сервер обслуживает несколько аккаунтов iikoCloud: экземпляр менеджера кэшируется по отпечатку sha1(api_key:app_id:client_secret) (ApiCredentials.key_id). Все три секрета обязательны в отпечатке: если бы он строился только по api_key и app_id, вызывающий с верным ключом и app_id, но чужим или неверным client_secret, получил бы доступ к кэшу и уже авторизованной сессии другого арендатора.

Подключение MCP-клиента

stdio (например, конфиг Claude Desktop):

{
  "mcpServers": {
    "iikocloud": {
      "command": "iikocloud-mcp",
      "args": ["--transport", "stdio", "--domains", "organizations,menu,dictionaries"],
      "env": {
        "IIKOCLOUD_API_KEY": "key",
        "IIKOCLOUD_APP_ID": "app",
        "IIKOCLOUD_CLIENT_SECRET": "secret"
      }
    }
  }
}

command должен быть исполняемым файлом: голое iikocloud-mcp работает, только если пакет установлен через uv pip install в окружение, видимое клиенту. Для dev-установки укажите "command": "uv" и "args": ["run", "--directory", "/путь/к/Iikocloud-mcp", "iikocloud-mcp", "--transport", "stdio", ...].

Удалённый HTTP:

{
  "mcpServers": {
    "iikocloud": {
      "url": "https://mcp.example.com/mcp",
      "headers": {
        "X-Iikocloud-Api-Key": "key",
        "X-Iikocloud-App-Id": "app",
        "X-Iikocloud-Client-Secret": "secret"
      }
    }
  }
}

Программный API

from iikocloud_mcp import ServerConfig, ToolFilter, create_server

cfg = ServerConfig(
    # host="0.0.0.0" — только если TLS терминирует прокси на другом хосте
    transport="http", host="127.0.0.1", port=8000,
    tool_filter=ToolFilter(domains={"menu", "dictionaries"}, operations=frozenset({"read"})),
)
server = create_server(cfg)        # FastMCP с зарегистрированными тулами
server.run(transport="streamable-http")

Подтверждение операций записи

Write-тулы по умолчанию требуют подтверждения пользователя перед мутацией — сервер вызывает MCP-elicitation и выполняет метод только при явном accept. Это серверный гейт, а не просто хинт клиенту (destructiveHint): даже клиент с авто-подтверждением тулов не выполнит запись без ответа пользователя. Политику задаёт оператор при запуске (не LLM):

  • по умолчанию — подтверждение включено, фолбэк closed;

  • --write-fallback open — если клиент не умеет elicitation, выполнять запись без подтверждения (оператор берёт риск на себя); по умолчанию (closed) такая запись блокируется;

  • --no-confirm-writes — полностью отключить гейт (для доверенной автоматизации).

Встроенные подсказки

Сервер обогащает описание и результат каждого тула, не полагаясь на память модели.

Асинхронные команды. 42 тула из 236 возвращают только correlationId — это квитанция о принятой команде, самого результата в ответе нет. Описание такого тула получает пометку:

⏳ Асинхронная команда: ответ содержит только correlationId, результата в нём нет. Чтобы узнать исход, вызовите operations__wait_command с этим correlationId и organizationId.

Ещё у 8 write-тулов ответ содержательный, и сервер ищет в нём поле creationStatus: если оно равно InProgress, к JSON-результату добавляется ключ _iikocloudMcpHint с той же инструкцией — опросить operations__wait_command. Реально сработать подсказка может у 4 из них — тех, что возвращают OrderInfo-подобную структуру: deliveries__create_delivery_order, drafts__commit_delivery_draft, orders__create_table_order, banquets__create_reserve. У остальных четырёх поля creationStatus в ответе нет вовсе: drafts__create_delivery_draft и drafts__save_delivery_draft возвращают CreateOrSaveDraftResponse (correlationId, orderId), employees__open_personal_session и employees__close_personal_sessioncorrelationId и error. Проверка идёт по значению в ответе, а не по типу, поэтому лишнего она не добавляет.

«Где взять ID». Схема параметров каждого тула сверяется с курируемой картой полей вида organizationId → organizations__get_organizations, terminalGroupId → terminal_groups__get_terminal_groups, productId → menu__get_nomenclature и т. д. Совпавшие поля попадают в описание тула строкой «Где взять ID: …», так что модель не пытается угадывать идентификаторы.

Как часто можно звать. Если у метода есть запись в лимитере менеджера, описание получает приписку вида «Троттлинг MCP-сервера (не лимит iikoCloud): не чаще N запрос(ов) за M с — кэшируйте результат в диалоге». Приписка намеренно говорит о клиентском троттлинге этого сервера (значения лимитера менеджера, уже с потолком --max-rate-window), а не о лимите самого API: настоящие лимиты iikoCloud бывают жёстче и описаны в докстрингах SDK, которые сервер не трогает (например, webhooks__update_webhook_settings — примерно 1 обновление в час).

У 17 тулов записи в лимитере нет: это тулы-обёртки вроде customers__get_customer_by_phone, который под капотом зовёт лимитируемый customers__get_customer_info. Молчание модель прочла бы как «ограничений нет», поэтому такие тулы получают общую приписку: своего троттлинга нет, но вызов расходует квоту нижележащего метода. Ручной карты «обёртка → метод» нет намеренно — она протухала бы при каждом изменении менеджера.

Версии внешнего меню. У menu__get_external_menu_by_id результат — объединение ExternalMenuV2 | ExternalMenuV3 | ExternalMenuV4: форма ответа зависит от параметра version. Описание тула вручную перечисляет все переименования полей между версиями и рекомендует явно указывать version=4.

Адрес доставки. У deliveries__create_delivery_order описание отдельно поясняет: формат адреса задаёт сама организация (addressFormatType, значение — из organizations__get_organization_settings), улицу можно передать и id (addresses__get_streets_by_city), и просто name вместе с city, а город указывать нужно всегда — он определяет разбор остального адреса.

Кэш справочников

15 из 22 тулов-источников идентификаторов (тех самых, куда отправляют подсказки «где взять ID») допускают не чаще одного запроса в 60 секунд — а диалог с моделью легко делает несколько похожих запросов подряд. Поэтому ответы примерно 27 справочных read-тулов (организации, домены справочников, адреса, меню, курьеры и т. п.) кэшируются в памяти процесса с TTL.

  • --cache-ttl — время жизни записи в секундах, по умолчанию 300; 0 полностью выключает кэш.

  • --cache-max-entries — потолок числа записей (LRU-вытеснение), по умолчанию 256; обязателен, потому что ответ menu__get_nomenclature может весить мегабайты.

Ключ кэша учитывает аккаунт (по хэшу кредов, не сами секреты) и аргументы вызова — на HTTP-транспорте один процесс безопасно обслуживает разных арендаторов. Инвалидация — только по TTL.

Лимит размера ответа и таймаут вызова

По умолчанию лимита на размер ответа нет. Если установить положительный --max-output-chars, слишком длинный результат вернётся не оборванным текстом, а корректным JSON-объектом:

{
  "truncated": true,
  "totalChars": 250000,
  "limitChars": 100000,
  "contentPrefix": "..."
}

contentPrefix — начало полного JSON-результата, так модель может распознать усечение и сузить запрос вместо того, чтобы принять обрезанный ответ за полный.

--call-timeout (по умолчанию 120 с) ограничивает время одного вызова тула — лимитер менеджера иначе просто блокирует запрос без таймаута. При превышении сервер тоже возвращает корректный JSON, а не обрыв соединения:

{
  "timeout": true,
  "tool": "menu__get_nomenclature",
  "timeoutSeconds": 120,
  "reason": "Вызов не уложился в лимит времени. Обычная причина — rate limit метода: лимитер ждёт освобождения окна. Повторите позже или сузьте запрос."
}

Текст reason у write-тулов другой, и это принципиально: asyncio.wait_for отменяет только ожидание внутри сервера, а запрос в iikoCloud уже ушёл и мог там выполниться — вместе с настоящим correlationId, который при таймауте теряется. Поэтому write-тул на таймауте сообщает, что операция могла пройти, что повтор создаст дубль (второй заказ, вторую бронь, второе пополнение) и что проверять надо чтением — для доставок семейством deliveries_retrieve__*.

--max-rate-window (по умолчанию 120 с) отдельно зажимает окна лимитера методов сверху: у методов с окном шире потолка (например 1 запрос/1800 с) окно укорачивается до потолка при сохранении числа запросов — иначе --call-timeout по умолчанию не успел бы дождаться собственного окна лимитера.

Тесты

uv run pytest -m unit -v          # 196 тестов, быстро, без сети и кредов
uv run pytest -m integration -v   # 4 read-only теста; без креда IIKOCLOUD_TEST_CONFIG — skip, не fail

Интеграционный набор (tests/integration/) гоняет реальные тулы через _make_tool против живого iikoCloud: получение организаций, camelCase-алиасы в ответе, попадание в кэш при повторном вызове, усечение по max_output_chars. Он только читающий — write-тестов против живого API нет и не будет: мутационный путь проверяется моками в tests/test_server.py.

Креды берёт из YAML по пути IIKOCLOUD_TEST_CONFIG (секция read) — см. config.test.example.yml и .env.example. Скопируйте оба в config.test.yml и .env: оба файла в .gitignore, секреты в репозиторий не попадают. Без IIKOCLOUD_TEST_CONFIG в окружении фикстура read_creds делает pytest.skip, а не падение, — так что набор безопасно запускать и на машине без кредов.

Безопасность

  • Секреты не попадают в аргументы тулов (модель их не видит), не логируются, живут только в памяти на время сессии.

  • Для HTTP обязателен TLS (reverse-proxy). Заголовки с кредами — только под HTTPS.

  • Дефолт и все примеры — host: 127.0.0.1. 0.0.0.0 открывает нешифрованный порт с кредами в заголовках; он оправдан только когда TLS-прокси стоит на другом хосте.

  • Своих гейтов по организациям или app_id сервер не вводит — доступ ограничивают настройки самого iikoCloud-аккаунта.

  • Дефолт read-only: мутации требуют явного --allow-write.

  • Дефолт: write-тулы требуют подтверждения пользователя (elicitation, см. выше).

Документация дизайна

Related MCP Connectors

Related MCP Servers

  • A
    license
    Not graded
    quality
    C
    maintenance
    A generic MCP server that dynamically converts OpenAPI-defined REST APIs into tools for LLMs like Claude. It supports multiple authentication methods and transport protocols, enabling seamless interaction with any OpenAPI-compliant API.
    20 npm
    MIT
  • F
    license
    Not graded
    quality
    B
    maintenance
    An MCP server with HTTP/stdio support, a web admin panel for managing services, capabilities, and user permissions with Bearer token authentication, enabling relay and access control for MCP tools.
    -