Newer
Older
gnexus-handbook / 10-platform / mcp.md

MCP: мультиюзер доступ для ИИ-агентов

Сервис с MCP-функционалом — сам себе MCP-сервер; доступ — через персональные ключи сервиса (не access-токены gnexus-auth), каждый ключ принадлежит пользователю сервиса. Канон = реализация Gnexus Synapse (docs/07-mcp.md, app/mcp/server.py, страница /mcp-keys); авторизация на тулах — снейпшот роли владельца, вместо набора scopes.

Правила

  • Сервис предоставляет streamable HTTP эндпоинт (/mcp-protocol/, слэш в конце значим — реверс-прокси не буферизует и не переписывает тело). Legacy HTTP/SSE-адаптер — только для клиентов без streamable HTTP. stdio не используется: агент и сервис на разных хостах.
  • Авторизация MCP — Bearer-ключ сервиса в заголовке Authorization (у Synapse: персональный mcp_…; исторический /mcp — alias к /mcp-protocol/).
  • Токены выдаются в UI на отдельной странице «MCP-ключи» (Synapse: /mcp-keys): имя ключа («агент Navi rei»), plaintext показывается ровно один раз при создании, в БД — только sha256-хэш (уникальный) и хвост-хинт (последние символы — опознание ключа в списке).
  • Лимит активных ключей на пользователя (Synapse: ≤10; превышение — 409 «отзовите лишний»).
  • Ключ действует ролью владельца на момент выпуска (снейпшот system_role в строке токена): роль admin+ — весь каталог тулов, роль user — только личный набор («me, свои списки»); админ-тул для user-ключа отвечает {"error": 403, "detail": …}. Смена роли на gnexus-auth не дотягивается до выпущенного ключа — старый отзывают, выпускают заново.
  • Вместо архива — ревок (revoked_at); списки показывают и отозванные (с хвост-хинтом), plaintext никогда не возвращается.
  • Админ видит ключи всех пользователей и может отозвать любой.
  • Блокировка пользователя gnexus-auth (вебхуки user.blocked/deleted/ archived → локальный флаг) гасит все его личные ключи: гард отвечает тем же 401, что и при неверном токене — не раскрывать статус блокировки. SSO-логаут MCP-токены не трогает — это самостоятельные креды (у Synapse так сделано потому, что SSO-сервер не отзывает выданные сервисом ключи).
  • Гард MCP — чистый ASGI-класс (не BaseHTTPMiddleware — SSE-стримы не ломает): резолвит Authorization: Bearer на каждом запросе (хэш-lookup), 401 + www-authenticate: Bearer при промахе, обновляет last_used_at.
  • Тулы не дублируют логику: переиспользуют функции роутов сервиса (та же pydantic-валидация, те же коды ошибок); ошибка возвращается как данные ({"error": 403|409, "detail": "…"}) — агент читает причину и исправляет.
  • Статический MCP_TOKEN из .env — суперадмин-бэкдор: сохраняет совместимость с существующими сценариями агентов. Опционален (пусто — работают только персональные ключи); держать в .env + gnexus-creds.
  • Rate-limit на чувствительные операции.

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

{
  "mcpServers": {
    "<сервис>": {
      "type": "streamable-http",
      "url": "https://<сервис>.gnexus.space/mcp-protocol/",
      "headers": { "Authorization": "Bearer mcp_<персональный ключ>" }
    }
  }
}

Claude Code:

claude mcp add --transport http <сервис> https://<сервис>.gnexus.space/mcp-protocol/ \
  --header "Authorization: Bearer mcp_<персональный ключ>"

Схема хранения (mcp_tokens)

user_id (sub gnexus-auth владельца — ключ выпускает сам себе), name (человекочитаемое назначение), user_email (снейпшот для списков), system_role (снейпшот роли выпуска), token_hash (sha256, unique), token_hint (хвост), created_at, last_used_at, revoked_at.

Ссылки

  • Канон: gnexus-synapse — docs/07-mcp.md (архитектура гейта и каталог тулов), app/mcp/server.py (McpTokenGuard), страница /mcp-keys, resolve_personal_token; таблица mcp_tokens (docs/04-database.md).
  • Первый применённый вариант — gnexus-creds (gnexus_creds/mcp*.py, страница Tokens): те же принципы хранения, но вместо снейпшота роли — scopes (read/reveal/write): приемлемая альтернатива там, где нужны градации внутри одного класса доступа.
  • hard-panel — по этой схеме: персональные mcp_* (страница «MCP-ключи»)
    • статический супер-токен из .env. Особенности: каталог тулов у всех ключей единый (данные панели общие); тулы — чтение плюс управление серверами/сервисами, запись открыта всем валидным ключам (модель доступа панели «любая сессия = админ»), запись лимитирована, ошибки — как данные. Снейпшот роли записывается в токен, но тулов «только для админа» нет: FastMCP 1.x не пробрасывает HTTP-контекст в тулы, поэтому роль владельца внутри тула не видна.