NODEDC_DESIGN_GUIDELINE/docs/MODULE_FOUNDRY_MCP.md

23 KiB
Raw Permalink Blame History

NDC Module Foundry — базовый MCP-контур

Назначение

NDC Module Foundry — модульный контур поверх канонического NODE.DC Design Guideline. Он создаёт и редактирует экземпляры готовых страниц в Applications; сам Page Library и его шаблоны остаются неизменяемым каноном.

MCP не вводит новую оркестрацию. Доступ выдаётся уже существующему AI Workspace Assistant через его generic entitlement adapter. Поэтому ассистент может продолжать работать с Engine, Ops, Ontology и другими подключёнными модулями по действующим правилам платформы, а Foundry определяет только границы действий внутри Foundry.

Границы v0.1

Разрешено Не разрешено
Просматривать Page Library и экземпляры Applications Менять канонический Page Library или дизайн-компоненты
Создавать application instance Удалять application instance через MCP
Изменять название, slug и описание instance Создавать свободный canvas или произвольный React-интерфейс
Добавлять повторные instances зарегистрированной страницы Вызывать Engine, provider API или произвольный внешний endpoint из Foundry MCP
Создавать/обновлять provider-neutral map pin bindings Хранить provider tokens, transport payload или credentials в manifest
Создавать/обновлять versioned provider-neutral map.style_profile Хардкодить provider-specific statuses, цвета или размеры в renderer
Задавать text label/order binding и сохранять полный Map view/window layout по bindingId Использовать редактируемое имя как machine identity или превращать empty selection в all
Связывать versioned data product с approved Map entity-stream slot Хранить provider ID, tenant/connection, endpoint или credential в data binding
Управлять server-owned consumer только для persisted approved binding Передавать reader capability, EDP URL или raw provider payload через MCP

Каждая запись требует idempotencyKey. Операция сохраняется в persistent runtime volume и повторный вызов с тем же ключом и тем же входом вернёт исходный результат без дублирования. Повтор ключа с иным входом завершается конфликтом.

MCP endpoint

POST /api/mcp

Поддерживается MCP protocol 2025-06-18. Endpoint принимает JSON-RPC initialize, tools/list, tools/call и ping.

Обязательные HTTP-заголовки каждого вызова:

Authorization: Bearer <Foundry capability or Foundry Agent credential>
MCP-Protocol-Version: 2025-06-18

Endpoint поддерживает два существующих контура входа, не смешивая их:

  • AI Workspace получает короткоживущую Foundry capability после штатной entitlement-проверки. Она подписана существующим внутренним service credential, привязана к actorId и ownerKey, живёт не более 10 минут и не раскрывает platform credential worker-у;
  • внешний Codex получает отдельный durable Foundry Agent credential через настройки текущего пользователя Foundry. Credential действует до явного revoke и не является AI Workspace capability.

Заголовки с actor/owner от клиента не принимаются: пользовательский контекст извлекается только из проверенной capability или server-side записи Agent.

Доступные инструменты:

  • foundry_status
  • foundry_list_applications
  • foundry_get_application
  • foundry_create_application
  • foundry_update_application_metadata
  • foundry_add_page_instance
  • foundry_upsert_map_pin_binding
  • foundry_remove_map_pin_binding
  • foundry_upsert_map_presentation_profile
  • foundry_update_map_page_settings
  • foundry_save_map_page_view_state
  • foundry_upsert_map_data_product_binding
  • foundry_plan_map_data_product_consumer
  • foundry_apply_map_data_product_consumer
  • foundry_get_map_data_product_consumer_status
  • foundry_accept_map_data_product_consumer
  • foundry_rollback_map_data_product_consumer

foundry_upsert_map_pin_binding хранит только визуальную, provider-neutral привязку elevated-spike: стабильный id, subject, координаты, semantic status и ссылку на источник сущности. Поток живых данных не передаётся в MCP по одной позиции.

foundry_remove_map_pin_binding удаляет одну устаревшую визуальную привязку по стабильному bindingId. Операция не затрагивает data-product consumer, его последний безопасный snapshot или канонический шаблон Page Library.

foundry_upsert_map_presentation_profile управляет отдельным версионированным map.style_profile страницы. Профиль содержит renderer-neutral геометрию таргета, label contract, semantic styles, правила классов, facet filters, counters и sort order. Условия классов могут ссылаться только на объявленные provider-neutral facet fields. Data Product binding выбирает профиль через presentationProfileId; binding также может задавать пользовательский displayName и порядок строки в dropdown Объекты. Provider raw status, endpoint и credential в профиль не допускаются. Полная machine-readable JSON Schema профиля публикуется прямо в tools/list: label использует mode, fields, плашку, fontWeight и sizePx, а target, facets, styles, classes и sort имеют закрытые наборы полей. Поэтому новый MCP клиент может создать профиль для другого spatial domain без чтения исходников Foundry и без неописанного generic JSON.

foundry_update_map_page_settings меняет подложку, terrain, здания, атмосферу, освещение и сетку только у указанного экземпляра Map внутри Application. Операция не пишет в Page Library, принимает закрытый typed patch и поэтому может безопасно воспроизводить визуальное окружение через MCP.

foundry_save_map_page_view_state является MCP-эквивалентом одной Application кнопки Сохранить. Операция атомарно сохраняет camera/map height, typed patch base settings, exact visibility/facet selections и все canonical binding windows (open, rect, maximized, zIndex). Состояние keyed только по bindingId: editable displayName не участвует в identity. Missing facet не ограничивает выборку, явно пустой список остаётся empty после reopen и не мигрирует обратно в Все.

foundry_upsert_map_data_product_binding сохраняет только декларацию data product → Map entity-stream slot: versioned data product id, semantic types, допустимую field projection и необязательную ссылку на существующий presentation profile. Endpoint, provider, tenant, connection, token, credential и raw payload валидатор отклоняет. Page runtime получает scoped snapshot/patch поток через Platform, но не через Foundry MCP.

Пять consumer-инструментов управляют тем же runtime, который обслуживает Map Page. plan проверяет persisted binding, Data Product version/delivery, versioned policy и безопасно показывает readerGrantAction=ensure|reuse. Если grant отсутствует, EDP сам разрешает единственный active writer scope по Data Product id; provider/tenant/connection в Foundry не возвращаются. apply принимает exact planId, подписанно передаёт EDP только SHA-256 digest нового target-scoped token, коммитит snapshot и включает consumer; status возвращает только safe cursor, счётчики subject/status/reconnect и количество viewers/upstream streams; accept берёт bounded diagnostic lease; rollback останавливает consumer, но сохраняет последний safe snapshot. Capability value, grant path, internal URL и fact attributes в MCP diagnostics не возвращаются.

Reader grant версионируется поколениями. При замене Data Product Foundry не перезаписывает immutable request generation и не ослабляет EDP conflict fence: он выпускает отдельный successor token/generation, проверяет им новый catalog и коммитит новый snapshot. Только после успешного snapshot predecessor generation отзывается exact revoke. Если bootstrap не удался, persisted predecessor snapshot и его grant остаются рабочими. Если временно не удался только revoke, следующий exact plan получает action finalize-reader-grant-rotation и завершает отзыв без повторного bootstrap.

Runtime data-product boundary

Для Map Page Foundry предоставляет только same-origin runtime routes:

GET /api/applications/:applicationId/pages/:pageId/data-bindings/:bindingId/snapshot
GET /api/applications/:applicationId/pages/:pageId/data-bindings/:bindingId/history?from=:iso&to=:iso&resolutionMs=:ms&sourceIds=:ids&limit=:n&cursor=:opaque
GET /api/applications/:applicationId/pages/:pageId/data-bindings/:bindingId/stream?after=:cursor

Маршрут разрешает persisted binding, а затем server-owned consumer находит opaque EDP reader grant по sha256(applicationId/pageId/bindingId) и persisted active generation. Generation 1 сохраняет совместимый legacy filename, а successor capabilities хранятся отдельными root-owned immutable files. Нормальный grant создаётся внутри persistent private runtime Foundry; runner монтирует отдельный Ed25519 private key только для подписи digest-only provisioner request, а EDP получает только public trust. Старый root-owned read-only grant directory остаётся fallback для уже выданных grant. Сам token передаётся только как Authorization во внутренний External Data Plane и никогда не попадает в browser, manifest, MCP или лог. В browser отдаётся только safe Foundry projection канонического data-product envelope: snapshot, bounded nodedc.data-product.history/v1, safe nodedc.data-product.patch/v1 upserts и cursor. History query проходит через тот же exact binding/read grant и не создаёт L2 execution на каждого viewer. Last-Event-ID авторитетнее старого after при автоматическом SSE reconnect.

Consumer state хранится отдельно от application manifest в persistent runtime volume. Snapshot atomically заменяет subject projection, включая явный empty snapshot. Patch применяется только от exact previous cursor; новый cursor и semantic state сначала сохраняются, затем fan-out отправляется browsers. Повторный cursor идемпотентно игнорируется, gap вызывает snapshot rebase. Несколько viewers одного binding делят один upstream EDP stream; закрытие последнего viewer закрывает subscription, но не влияет на независимый producer в Engine L2.

Freshness и remove принадлежат versioned provider-neutral policy из registry/data-product-consumer-policies.json. Stale вычисляется по observedAt; terminal statuses сохраняются. Временная transport/credential ошибка не удаляет subject. Удаление допустимо только при отсутствии в авторитетном snapshot rebase или по canonical tombstone/revoked operation. Renderer получает стабильный sourceId + semanticType, persisted coordinates и Foundry-owned presentationStatus. Операционные visual classes, фильтры, счётчики и сортировка разрешаются отдельно из orthogonal state facets и page-owned map.style_profile; provider identity на стиль и lifecycle не влияет.

Внешний Codex: Foundry + отдельная Ontology MCP

В профиле Foundry раздел Настройки → Codex Agent API создаёт Agent текущего пользователя и выдаёт одноразовую setup-команду. Команда устанавливает в Codex две независимые MCP-конфигурации:

nodedc_module_foundry → POST /api/mcp
nodedc_ontology       → POST /api/ontology-mcp

Это одна операция установки, но не одна MCP. Foundry credential принимается только /api/mcp, Ontology credential — только /api/ontology-mcp; их перекрёстное использование отклоняется. Ontology route после своей проверки проксирует MCP JSON-RPC во внутренний Ontology Core /mcp с уже существующим server-only NODEDC_INTERNAL_ACCESS_TOKEN. Foundry не импортирует ontology tools в свой список и не становится маршрутизатором платформенных MCP.

Agent credentials не имеют календарного срока жизни: они валидны до явного отзыва Agent. Setup code одноразовый и живёт 15 минут. Raw credentials возвращаются только в момент redeem; в Foundry runtime сохраняются только SHA-256 digests, device metadata и bounded audit. Один revoke закрывает обе credentials конкретного Agent.

Первый срез намеренно ограничен текущим аутентифицированным пользователем: Codex может создавать и настраивать его Applications средствами Foundry MCP и читать общую Ontology через отдельную read-only MCP. Sharing, роли, межпользовательская видимость, release/publication и новые product ACL в этот срез не входят.

Установщик идемпотентно обновляет только блоки nodedc_module_foundry и nodedc_ontology в ~/.codex/config.toml, сохраняет остальные MCP (включая Engine и Ops), делает backup конфигурации и устанавливает skill foundry-context. Перед успешным завершением он выполняет initialize и tools/list для обеих MCP. После установки Codex Desktop нужно полностью перезапустить.

Entitlement для AI Workspace

POST /api/ai-workspace/entitlements

Endpoint предназначен для уже существующего AI Workspace generic adapter. Он принимает его штатный request-контракт и выдаёт динамический MCP grant только если совпала одна из политик:

  • FOUNDRY_ALLOW_ALL_AUTHENTICATED=true — временно разрешает всем уже аутентифицированным пользователям;
  • FOUNDRY_ALLOWED_OWNER_IDS — список дополнительных разрешённых user id / owner key;
  • FOUNDRY_ALLOWED_OWNER_GROUPS — список дополнительных разрешённых групп.

По умолчанию доступ получает user_root (dcctouch@gmail.com) и техническая группа Authentik nodedc:module-foundry:access. В Launcher эта группа выдаёт роль сервиса member; все остальные пользователи получают «нет доступа» до явной выдачи права администратором Hub. Многопользовательское владение Applications в v0.1 не вводится: это общий внутренний контур с аудитом actor id.

Для production предпочтительны явно заданные users/groups; режим ALLOW_ALL_AUTHENTICATED допустим только для внутренней песочницы.

Штатный запрос adapter выглядит так:

{
  "schemaVersion": "ai-workspace.entitlement-request.v1",
  "appId": "module-foundry",
  "owner": {
    "userId": "authenticated-user-id",
    "key": "owner-or-workspace-key",
    "groups": ["optional-group"]
  },
  "activeContext": {},
  "runContext": {},
  "requestedAt": "2026-07-13T00:00:00.000Z"
}

Server-side configuration

Все значения находятся только в server environment. В browser bundle не передаются tokens, entitlement keys и runtime volume paths.

FOUNDRY_PUBLIC_URL=https://<future-foundry-domain>
FOUNDRY_MCP_URL=https://<future-foundry-domain>/api/mcp
# Existing NODE.DC server-to-server credential; never send it to a browser or worker.
NODEDC_INTERNAL_ACCESS_TOKEN=<existing-platform-service-value>
# Internal-only Ontology Core address; never expose the Core directly.
NODEDC_ONTOLOGY_CORE_URL=http://ontology-core:8080
FOUNDRY_MCP_CAPABILITY_TTL_MS=600000
# Optional exceptional identities, not the default dcctouch superadmin grant.
FOUNDRY_ALLOWED_OWNER_IDS=<comma-separated-extra-ids>
FOUNDRY_ALLOWED_OWNER_GROUPS=<comma-separated-extra-groups>
FOUNDRY_ALLOW_ALL_AUTHENTICATED=false
FOUNDRY_MCP_ALLOWED_ORIGINS=https://<ai-workspace-domain>

Foundry не требует у пользователя вводить platform secret. NODEDC_INTERNAL_ACCESS_TOKEN — уже существующий server-to-server credential платформы: он находится только в server environment и используется для Launcher/Authentiк handoff, внутренней entitlement-проверки и server-side Ontology Core proxy. AI Workspace worker получает лишь короткоживущую capability; внешний Codex — отдельные Agent credentials, но не internal token.

После появления домена в deployment AI Workspace добавляется только штатная настройка adapter; новый worker, отдельная оркестрация или специальный bridge не нужны:

AI_WORKSPACE_ENTITLEMENT_ADAPTERS_JSON='{"module-foundry":{"url":"https://<future-foundry-domain>/api/ai-workspace/entitlements","required":false}}'

Если в переменной уже есть другие adapters, module-foundry добавляется в тот же JSON-объект — существующие ops, engine, launcher и другие grants не заменяются.

Deployment package

Foundry подготовлен к отдельному deployment как stateless server + один named persistent volume. Runtime volume содержит Applications, Design Profiles, idempotency audit, data-product consumer state/cursors, media uploads, Agent/device records, setup-code digests и Agent audit. Он является единственным источником изменяемого Foundry state; Git не используется для runtime-данных и tile cache. Raw Foundry/Ontology Agent credentials и EDP reader capabilities в volume не записываются.

cp .env.example .env
# использовать уже имеющийся NODEDC_INTERNAL_ACCESS_TOKEN из server environment
docker compose --env-file .env -f infra/docker-compose.module-foundry.yml up -d --build
curl http://127.0.0.1:9920/healthz

Внешний маршрут подготовлен: https://foundry.nodedc.ru172.22.0.222:9920 → container :3333. Compose на NAS должен слушать именно 172.22.0.222:9920, а не все интерфейсы. До запуска Foundry proxy закономерно возвращает 502; это проверяемый признак, что DNS/TLS/proxy уже доходят до внутреннего destination. Публикация допустима только после проверки Launcher's handoff, server-side session revalidation, синхронизации Authentik-группы и выдачи Hub access. Только после этого добавляется Foundry entitlement adapter в platform AI Workspace deployment.

Следующие слои

  1. Онтологический contract приложения, страницы, page instance, Map Page и visual entities.
  2. Расширение runtime adapter с points на targets, zones, routes и tracks.
  3. Управляемая публикация и повторное использование presentation profiles между Applications.
  4. Access/Hub registration и публикация module route.
  5. Отдельная пустая программируемая страница — только после того, как готовые page templates и их MCP-контуры отработаны.