MCP: как агент работает с Gigma ERP

MCP в Gigma — это внешний слой tools над обычным ERP API. В Laravel backend нет отдельного маршрута /api/mcp/* и нет отдельного MCP-контроллера.

После получения Agent Token MCP-сервер вызывает обычные ERP endpoints:

MCP tool
→ точный allowlist HTTP method + path
→ Authorization: Bearer <agent_token>
→ middleware auth:user + banned
→ controller и policy конкретного endpoint
→ response обычного ERP API

Наличие route внутри authenticated group ещё не доказывает безопасный MCP-контракт. Для каждого tool нужно отдельно проверить permission, project/branch scope, привязку вложенных ресурсов и состав ответа.

Три разные API-поверхности

ПоверхностьНазначениеКто вызывает
/api/agent-access-requests/*первоначальный запрос доступа и одноразовая выдача первого Agent TokenMCP-клиент и подтверждающий человек
обычные /api/orders, /api/counterparties, /api/nomenclatures и другие ERP endpointsработа с бизнес-даннымиMCP-сервер с Agent Token
/api/agents/* и /api/tables/agentsсоздание, изменение и отключение agent accounts, управление токенамиадминистративный actor; token operations — только человек

/api/agents — не рабочие бизнес-ручки агента. Это административный ресурс для управления учётными записями агентов.

Получение первого токена описано на странице [«Получение доступа»](/ERP/МСП/Получение доступа/), а административные операции — на странице [«Управление агентами»](/ERP/МСП/Управление агентами/).

Откуда берутся права

Agent Token создаётся Laravel Sanctum с abilities *, но эти abilities не являются бизнес-правами. Для endpoints, где policy подключена, фактический доступ определяется текущим agent account:

Bearer
→ User с is_agent = true
→ project_id и branch_id этого User
→ текущие role + permissions
→ middleware, controller и policy конкретного endpoint

Разные токены одного agent account используют один и тот же набор role и permissions. Отдельный токен удобен для ротации или разных установок MCP-сервера, но не создаёт отдельную границу полномочий. Для изоляции профилей нужен отдельный agent account.

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

Статусы профилей

ПрофильСтатусЧто разрешено сейчас
P0 Connectivitycurrentпроверить Bearer через GET /api/user
P1 Sales Readonlycurrentзаказы, контрагенты и безопасные справочники
People Directorysensitive / opt-inсотрудники только при отдельной необходимости и фильтрации ответа
P2 Sales Operatorconditionalcalculator сразу; записи — после проверки permission catalog и negative tests
P3 Catalog / Warehouseconditionalпосле проверки наличия catalog permissions в БД
P4 Applications / Contentblocked for normal assistantтекущие responses раскрывают application token
P5 Agent Administrationcurrentaccount operations по permissions; token operations только для human actor

Профиль — конфигурация MCP-сервера, а не сущность backend. Он состоит из permissions agent account, точного allowlist method + path, request schema и правил подтверждения write-операций.

P0. Preflight: проверить токен

Проверка рабочего Agent Token

Метод
GET
URL
https://api.gigma.ru/api/user
Авторизация
Agent Bearer
Headers
Accept: application/json; Authorization: Bearer <agent_token>
Успешный ответ
200

Endpoint возвращает wrapper user с обычным UserResource: ID, login, ФИО, роль, филиал, отдел, состояние блокировки, permissions и даты активности.

Используйте ответ, чтобы проверить:

  • токен принят backend;
  • учётная запись не заблокирована;
  • роль, филиал и permissions соответствуют ожидаемому профилю tools.

Ограничение текущего контракта: GET /api/user не возвращает is_agent и project_id. Поэтому MCP-сервер не может доказать эти два значения по whoami. Тип учётной записи и ожидаемый проект должны храниться в конфигурации подключения, сформированной при выдаче доступа.

Ошибки

  • 401 — токен отсутствует, недействителен, истёк или отозван;
  • 403 — доступ запрещён middleware или policy;
  • заблокированный agent account не должен доходить до бизнес-контроллера.

P1. Проверенный read-only профиль продаж

Минимальный набор permissions:

[
	"view-orders",
	"view-counterparties"
]

Рабочие списки

GET /api/user

GET /api/orders
GET /api/tables/orders

GET /api/counterparties
GET /api/tables/counterparties

Карточки и безопасные вложенные чтения

Сначала получите ID из списка, доступного текущему agent account, затем вызывайте:

GET /api/orders/{order}
GET /api/orders/{order}/history
GET /api/tables/orders/{order}/files
GET /api/tables/orders/{order}/nomenclatures

GET /api/counterparties/{counterparty}

Для заказов backend применяет project scope, а для пользователя без роли owner/admin и с назначенным филиалом — ещё и branch scope. Вложенные order routes защищены can:view,order. CounterpartyPolicy проверяет permission и проект карточки.

Контекстные справочники только на чтение

В tool allowlist можно добавить только GET:

GET /api/order_statuses
GET /api/counterparty_types
GET /api/delivery_types
GET /api/sales_channels
GET /api/brands
GET /api/cities
GET /api/countries
GET /api/vats
GET /api/storage_units

Не добавляйте POST, PUT, PATCH и DELETE для справочников в обычный read-only профиль.

Подробные query-параметры и response schemas находятся в разделах «Заказы» и «Контрагенты».

Отдельный sensitive-профиль: сотрудники

GET /api/users, GET /api/tables/users и GET /api/users/{user} не входят в минимальный P1. В текущем UserPolicy чтение разрешается не только по view-users, но также при наличии create-users или edit-users. Любое из этих трёх permissions нужно считать доступом к каталогу сотрудников.

Полный UserResource возвращает персональные и кадровые данные, включая phone, birthday, employment_contract и dismissal_date. Табличный resource короче, но всё равно содержит ФИО, login, роль, филиал, отдел и активность.

Подключайте эти endpoints только отдельному people-directory agent account. Для такого профиля:

  • выдавайте минимальное право view-users, если запись сотрудников не нужна;
  • не передавайте модели полный сырой ответ без необходимости;
  • возвращайте только нужные поля, например ID и отображаемое имя;
  • не включайте кадровые поля в логи и контекст модели;
  • не рассчитывайте на отдельный токен того же агента как на изоляцию permissions.

Helper endpoints GET /api/managers и GET /api/responsible_users возвращают более короткие записи, но не вызывают UserPolicy и не требуют view-users, поэтому в production allowlist они пока не рекомендуются.

Endpoints, которые нельзя включать в production MCP allowlist

Ниже перечислены зарегистрированные routes, для которых текущий runtime не обеспечивает ожидаемую комбинацию permission check, project scope, parent-child scope или безопасный response contract.

Задачи

GET   /api/tasks
GET   /api/tables/tasks
GET   /api/tasks/{task}
POST  /api/tasks
PATCH /api/tasks/{task}

TaskController не вызывает policy:

  • list endpoints вручную добавляют только project_id;
  • show принимает route model напрямую;
  • update изменяет полученную модель без object-level project check;
  • permissions view-tasks и edit-tasks в контроллере не проверяются.

Особенно опасны GET /api/tasks/{task} и PATCH /api/tasks/{task}. Не выдавайте их MCP-серверу до backend-hardening и negative tests на объект чужого проекта.

Контакты и история контрагента

GET    /api/counterparties/{counterparty}/history
GET    /api/counterparties/{counterparty}/contacts
GET    /api/counterparties/{counterparty}/contacts/{contact}
POST   /api/counterparties/{counterparty}/contacts
PATCH  /api/counterparties/{counterparty}/contacts/{contact}
DELETE /api/counterparties/{counterparty}/contacts/{contact}

CounterpartyHistoryController не проверяет проект. ContactController проверяет permission, но ContactPolicy не проверяет проект, а nested routes не обеспечивают связь contact → counterparty. Эти endpoints нельзя считать безопасными только из-за наличия view-counterparties или edit-counterparties.

Приложения и интеграционные токены

Не включайте обычному MCP-помощнику:

GET   /api/applications
GET   /api/tables/applications
GET   /api/applications/{application}
POST  /api/applications
PATCH /api/applications/{application}
GET   /api/applications/{application}/history

Текущие ApplicationResource и Tables\ApplicationResource возвращают поле token. Значит даже read-only list раскрывает интеграционный секрет всем, кому выдан view-applications.

Есть и второе несоответствие: list фильтрует приложения по текущему проекту и допускает глобальные записи с project_id = null, а ApplicationPolicy::view() для detail требует точного совпадения application.project_id === user.project_id. Поэтому объект может появиться в списке, но вернуть 403 на карточке.

GET /api/applications/{application}/history дополнительно использует базовый Controller::history() без project check для Application.

До исправления backend нужен один из вариантов:

  1. убрать token из стандартных application resources;
  2. вернуть секрет только отдельным endpoint с отдельным permission и аудитом;
  3. после этого заново доказать list/detail scope и добавить negative tests.

Webhooks и notification channels

Не включайте в обычный assistant-профиль:

/api/applications/{application}/webhooks*
/api/applications/{application}/notification-channels/*

Эти методы могут раскрывать конфигурацию, ротировать секреты и повторно отправлять внешние доставки. Для них нужен отдельный integration-admin agent account, отдельный короткоживущий токен и подтверждение каждой write-операции.

Inventory detail и write

GET    /api/inventories/{inventory}
POST   /api/inventories
PATCH  /api/inventories/{inventory}
DELETE /api/inventories/{inventory}

InventoryController не подключает authorizeResource. show, update и destroy работают с route model без object-level project check; permission view-inventories или edit-inventories также не проверяется. Эти методы требуют исправления backend до подключения к MCP.

P2. Подтверждаемые операции с заказами и контрагентами

Готовый вычислительный tool

POST /api/orders/calculator

Endpoint выполняет чистый расчёт по price, markup и discount, не читает модели из БД и не создаёт бизнес-сущность. Payload всё равно должен проходить фиксированную schema.

Условные write-tools

POST  /api/orders
PATCH /api/orders/{order}
POST  /api/orders/{order}/files
POST  /api/orders/{order}/nomenclatures
PATCH /api/orders/{order}/nomenclatures/{nomenclature}
DELETE /api/orders/{order}/nomenclatures/{nomenclature}

POST  /api/counterparties
PATCH /api/counterparties/{counterparty}

Фактическая логика permissions:

  • создание заказа разрешается с create-orders или edit-orders;
  • изменение заказа требует edit-orders;
  • создание контрагента разрешается с create-counterparties или edit-counterparties;
  • изменение контрагента требует edit-counterparties.

Базовый PermissionSeeder создаёт edit-orders и edit-counterparties, но не создаёт create-orders и create-counterparties. Поэтому create-only профиль нельзя считать доступным во всех инсталляциях. Перед включением write-tools:

  1. проверьте, что точные permission records существуют с guard_name = user;
  2. не подменяйте отсутствующее create-only право более широким edit-* без явного решения владельца;
  3. проверьте положительный запрос в своём проекте и отрицательный запрос к ID другого проекта;
  4. включите только фиксированные methods и paths.

Каждый write-tool обязан:

  1. построить payload по фиксированной schema;
  2. показать человеку окончательные method, path и JSON;
  3. получить явное подтверждение;
  4. выполнить запрос один раз;
  5. перечитать созданный или изменённый объект и сверить результат.

Не повторяйте автоматически POST, PATCH или DELETE после 401, 403, 404, 409 или 422.

P3. Каталог и склады

Контроллеры номенклатуры, категорий и складов используют resource policies и project-scoped списки:

GET   /api/nomenclatures
GET   /api/tables/nomenclatures
GET   /api/nomenclatures/{nomenclature}
POST  /api/nomenclatures
PATCH /api/nomenclatures/{nomenclature}

GET   /api/categories
GET   /api/tables/categories
GET   /api/categories/{category}
POST  /api/categories
PATCH /api/categories/{category}

GET   /api/warehouses
GET   /api/tables/warehouses
GET   /api/warehouses/{warehouse}
POST  /api/warehouses
PATCH /api/warehouses/{warehouse}

Их policies используют permissions вида view-*, create-*, edit-*, но catalog permissions не входят в базовый PermissionSeeder. Профиль можно включать только после проверки, что нужные permission records реально существуют с guard_name = user, назначены agent account и проходят negative tests на чужой проект.

Inventory endpoints в этот профиль не входят из-за описанного выше authorization gap.

P4. Приложения и контент

В текущем backend P4 не является обычным production-профилем. Основные application endpoints возвращают token, а nested CMS/integration routes требуют отдельного аудита scopes и секретов.

До redaction application token и отдельного permission contract не создавайте generic tools для:

/api/applications*
/api/pages*
/api/menus*
/api/applications/{application}/blocks*
/api/applications/{application}/webhooks*
/api/applications/{application}/notification-channels/*

Разрешать отдельные storefront resources можно только отдельным agent account после проверки конкретного controller, policy, permission record и negative test. Не объединяйте content-admin и обычного sales assistant в одном agent account.

P5. Администрирование агентов

Административная поверхность:

GET    /api/agents
GET    /api/tables/agents
GET    /api/agents/{agent}
POST   /api/agents
PATCH  /api/agents/{agent}
DELETE /api/agents/{agent}

GET    /api/agents/{agent}/tokens
POST   /api/agents/{agent}/tokens
DELETE /api/agents/{agent}/tokens/{token}

Для неё используются отдельные permissions:

[
	"view-agents",
	"create-agents",
	"edit-agents",
	"manage-agent-tokens"
]

Текущий UserPolicy не вводит глобальный запрет для agent actor на list, show, create, update или disable. Эти операции доступны authenticated actor, который прошёл соответствующие permission, project и delegation checks. Поэтому обычному MCP-помощнику нельзя выдавать agent-management permissions.

Для token operations действует отдельное правило: UserPolicy::manageAgentTokens() явно запрещает actor с is_agent = true. Выпуск, список и отзыв токенов через /api/agents/{agent}/tokens* выполняются Bearer-токеном человека. Полные request/response contracts находятся на странице [«Управление агентами»](/ERP/МСП/Управление агентами/).

Обязательные правила MCP-сервера

Только точный allowlist

Tool должен хранить конкретные:

HTTP method + path pattern

Запрещён generic REST/curl proxy, через который модель может передать произвольный URL, method или JSON.

ID только после scoped lookup

Не принимайте произвольный ID от модели. Сначала получите сущность из проверенного списка текущего проекта. Затем используйте возвращённый ID в следующем tool.

Исключение «ID получен из списка» не делает route безопасным, если сам list возвращает глобальные записи или чувствительные поля. Именно поэтому application endpoints сейчас заблокированы.

Строгий payload

  • удаляйте неизвестные поля;
  • не отправляйте undefined и UI-placeholder;
  • не используйте 0 как «не выбрано»: middleware ConvertZeroToNull может преобразовать его в null;
  • отправляйте null только когда tool явно поддерживает очистку поля;
  • показывайте человеку именно тот JSON, который будет отправлен.

Секреты и чувствительные данные

Не записывайте в логи и историю:

  • Agent Bearer;
  • request_token;
  • approval_token;
  • ответ первого consume;
  • agent_token.value из административной выдачи;
  • application token;
  • кадровые и персональные поля сотрудников, если они не нужны сценарию.

Ошибки

КодЧто означает для MCP-сервера
401токен отсутствует, истёк или отозван; остановить запросы и запросить ротацию
403permission или policy запретили действие; не обходить другим endpoint
404объект отсутствует либо скрыт scope; не перебирать соседние ID
409конфликт состояния; перечитать ресурс и показать человеку
422payload или бизнес-правило не прошло валидацию; исправить данные, не повторять тот же запрос
429применить backoff только для безопасного read/polling; write не дублировать вслепую

Минимальный production-набор

Для первого агента используйте:

P0 Connectivity
+ P1 Sales Readonly
+ отдельные подтверждаемые write-tools только после проверки permission catalog

Не выдавайте обычному помощнику права и endpoints для сотрудников, приложений, интеграционных секретов, webhooks, ролей, permissions, других агентов и административных операций.

© 2026 Gigma