From 2219d1249b970bca335cdf34414bce639251aedd Mon Sep 17 00:00:00 2001 From: "a.tolmachev" Date: Sun, 29 Mar 2026 21:11:04 +0300 Subject: [PATCH] docs: redesign architecture around workspaces and agents --- README.md | 97 ++-- TASKS.md | 15 +- docs/admin-api.md | 566 ++++++----------------- docs/architecture.md | 644 ++++++++------------------ docs/as-is-to-be.md | 264 +++++++++++ docs/data-model.md | 843 ++++++++--------------------------- docs/database-schema.md | 594 +++++++++++------------- docs/diagrams.md | 376 +++------------- docs/implementation-plan.md | 406 +++-------------- docs/mcp-interface.md | 107 ++--- docs/module-decomposition.md | 679 +++------------------------- 11 files changed, 1321 insertions(+), 3270 deletions(-) create mode 100644 docs/as-is-to-be.md diff --git a/README.md b/README.md index 74e4dad..06d0dae 100644 --- a/README.md +++ b/README.md @@ -2,9 +2,7 @@ ![Crank](./Crank.png) -Crank - это low-code платформа для публикации внешних API в виде MCP tools без написания нового backend-обработчика под каждую интеграцию. Система предоставляет единый административный UI, в котором оператор может подключать REST, GraphQL и gRPC операции, настраивать маппинг входных и выходных данных, выполнять тестовый вызов и публиковать результат как MCP tool. - -На текущем этапе репозиторий содержит проектную документацию и архитектурные решения, которые задают границы MVP и подход к реализации. +Crank - платформа для публикации внешних API в виде MCP tools без написания отдельного backend-кода под каждую интеграцию. Целевая модель проекта строится вокруг связки `workspace -> agent -> operations`. ## Цели @@ -12,75 +10,78 @@ Crank - это low-code платформа для публикации внеш - Поддержать динамическое добавление интеграций через UI или конфигурацию. - Обеспечить единый сценарий работы оператора для REST, GraphQL и gRPC. - Нормализовать внешние протоколы в единую внутреннюю модель операции. -- Избежать генерации и деплоя нового backend-кода при добавлении каждого нового инструмента. +- Ограничивать набор tools на уровне конкретного агента, а не отдавать один глобальный каталог. +- Поддержать workspace-изоляцию, platform access и observability. -## Состав MVP +## Целевая модель продукта +- `Workspace` как tenant boundary. +- `Operation` как интеграционный контракт. +- `Agent` как curated MCP surface для LLM. - Поддержка REST для `GET`, `POST`, `PUT`, `PATCH` и `DELETE`. -- Поддержка GraphQL для `query` и `mutation` на основе шаблонов и переменных. +- Поддержка GraphQL для `query` и `mutation`. - Поддержка только unary-методов gRPC. -- Загрузка примеров `JSON` для ускоренного создания схем и чернового маппинга. -- Загрузка `.proto` файлов или descriptor set для обнаружения схемы gRPC. -- Импорт и экспорт конфигураций операций в `YAML`. -- Использование `JSONPath` для точечного маппинга вложенных параметров и ответа. -- Настройка маппинга запроса и ответа через UI. -- Публикация tools в MCP без пересборки backend. +- Platform API keys и membership layer. +- Observability: invocation logs, usage aggregates, latency/error metrics. +- Импорт и экспорт operation-конфигураций в `YAML`. +- Использование `JSONPath` для точечного маппинга. ## Структура документации -- `docs/architecture.md` - архитектура системы, модули, потоки данных и стек. -- `docs/module-decomposition.md` - детальная декомпозиция crates и внутренних модулей. -- `docs/data-model.md` - формальная модель данных и JSON-структуры сущностей. -- `docs/database-schema.md` - схема БД, связи и versioning конфигураций. -- `docs/admin-api.md` - HTTP-контракты административного API. -- `docs/diagrams.md` - структурные диаграммы компонентов, сущностей, БД и потоков. -- `docs/mcp-interface.md` - транспорт и контракт публикации MCP tools. -- `docs/testing-strategy.md` - стратегия тестирования до и во время разработки. -- `docs/runtime-config.md` - конфигурация окружения, storage и секретов. -- `docs/deployment.md` - контейнерный деплой, reverse proxy и CI/CD. -- `docs/demo-runbook.md` - пошаговый сценарий локального запуска и воспроизводимого демо. -- `docs/rust-design.md` - распределение методов, `impl`, `trait` и service-слоя без god-struct. -- `docs/development-rules.md` - правила разработки, TDD-процесс и git workflow. -- `docs/rust-code-rules.md` - Rust-specific правила кода, toolchain и linting. -- `docs/implementation-plan.md` - последовательность модулей и фич по этапам реализации. -- `docs/protocols/rest.md` - функциональные требования и ограничения для REST. -- `docs/protocols/graphql.md` - функциональные требования и ограничения для GraphQL. -- `docs/protocols/grpc.md` - функциональные требования и ограничения для gRPC. +- `docs/architecture.md` - целевая архитектура системы. +- `docs/as-is-to-be.md` - переход `as is -> to be`, page-by-page gap analysis и архитектурные конфликты. +- `docs/module-decomposition.md` - декомпозиция crates и модулей. +- `docs/data-model.md` - целевая модель данных. +- `docs/database-schema.md` - целевая схема БД. +- `docs/admin-api.md` - целевые HTTP-контракты административного API. +- `docs/diagrams.md` - диаграммы компонентов, сущностей и БД. +- `docs/mcp-interface.md` - модель MCP transport и agent-scoped publishing. +- `docs/testing-strategy.md` - стратегия тестирования. +- `docs/runtime-config.md` - конфигурация окружения. +- `docs/deployment.md` - деплой, reverse proxy и CI/CD. +- `docs/demo-runbook.md` - демонстрационный сценарий. +- `docs/rust-design.md` - правила распределения поведения в Rust. +- `docs/development-rules.md` - правила разработки и workflow. +- `docs/rust-code-rules.md` - Rust-specific coding rules. +- `docs/implementation-plan.md` - порядок перехода от текущего состояния к целевой модели. +- `docs/protocols/rest.md` - требования и ограничения для REST. +- `docs/protocols/graphql.md` - требования и ограничения для GraphQL. +- `docs/protocols/grpc.md` - требования и ограничения для gRPC. ## Ключевая идея продукта -Система строится вокруг унифицированной сущности `Operation`. Каждая операция описывает: +Система строится вокруг трех уровней: -- внешний протокол, -- целевой endpoint или метод, -- входную схему, -- правила маппинга входных данных, -- параметры выполнения, -- правила маппинга выходных данных, +- `Workspace` - граница данных и доступа команды. +- `Agent` - curated MCP endpoint для конкретного сценария LLM. +- `Operation` - низкоуровневый интеграционный контракт. + +`Operation` описывает: + +- внешний протокол; +- целевой endpoint или метод; +- входную схему; +- правила маппинга входных данных; +- параметры выполнения; +- правила маппинга выходных данных; - метаданные MCP tool. -За счет этого MCP runtime работает с единой внутренней моделью, а протокольные адаптеры уже выполняют конкретные вызовы REST, GraphQL или gRPC. - -Для GraphQL это означает, что в MCP публикуется не "универсальный GraphQL endpoint", а конкретная операция с фиксированным шаблоном запроса, фиксированным набором входных параметров и предсказуемой структурой ответа. - -Для упрощения настройки оператор может загружать примеры входного и выходного `JSON`, а для gRPC - `.proto` или descriptor set. На основе этих артефактов система строит черновую схему и стартовый маппинг, который затем вручную уточняется через `JSONPath`. - -Конфигурации операций должны импортироваться и экспортироваться в `YAML`, чтобы их можно было переносить между окружениями, хранить в git и редактировать вне UI. +`Agent` собирает ограниченный набор опубликованных операций в одну MCP-поверхность. Именно это решает проблему, когда один агент теряется в слишком большом наборе tools. ## CI/CD статус В репозитории настроены: -- `CI` для Rust, UI и deployment artifacts; +- `CI` для Rust, UI container и deployment artifacts; - `CD`, который запускается после успешного `CI` на `main` или вручную; -- containerized production-like deployment через `docker compose`. +- containerized deployment через `docker compose`. ## Поддерживаемые протоколы -В MVP платформа ориентируется на три основных протокольных сценария интеграции: +В целевой модели платформа ориентируется на: - REST - GraphQL - gRPC -`SOAP` сознательно не входит в MVP. Он остается актуальным для части корпоративных и государственных интеграций, но требует отдельного адаптера с поддержкой WSDL, XML Schema, SOAP envelope, namespaces и XML-oriented mapping. Для первой версии это слишком большой отдельный пласт сложности. +`SOAP` сознательно не входит в текущий scope. diff --git a/TASKS.md b/TASKS.md index 9f75cba..34298d1 100644 --- a/TASKS.md +++ b/TASKS.md @@ -2,21 +2,26 @@ ## Current -### `feat/remove-legacy-ui` +### `feat/as-is-to-be-docs` Status: completed DoD: -- текущая React/Vite UI-кодовая база удалена -- `apps/ui` сохранен как статический placeholder для будущей замены -- compose, deploy и CI не ломаются после удаления legacy UI +- `as is -> to be` зафиксирован в документации +- разобраны page-by-page backend gaps для `test-ui` +- workspace/agent/access/observability модель синхронизирована в архитектурных документах ## Next -- `feat/alpine-ui` +- `feat/backend-gap-plan` ## Backlog +- `feat/backend-gap-plan` +- `feat/workspace-foundation` +- `feat/agent-publishing` +- `feat/platform-access` +- `feat/observability-api` - `feat/alpine-ui` - `feat/demo-assets` diff --git a/docs/admin-api.md b/docs/admin-api.md index 5266fb3..bead6f3 100644 --- a/docs/admin-api.md +++ b/docs/admin-api.md @@ -2,16 +2,15 @@ ## 1. Назначение документа -Этот документ фиксирует HTTP-контракты административного API, через которое UI управляет операциями, загружает артефакты, тестирует вызовы и выполняет YAML import/export. - -Документ задает логический контракт. Конкретные детали `axum` handlers, auth middleware и response envelope могут уточняться при реализации. +Этот документ фиксирует целевые HTTP-контракты административного API, через которое UI управляет workspace, operations, agents, platform access и observability. ## 2. Общие правила API - все payload по умолчанию в `JSON`; -- import/export конфигурации используют `YAML` как payload или файл; -- версии operation адресуются явно; -- published операция - это ссылка на конкретную version; +- import/export конфигурации используют `YAML`; +- все основные ресурсы являются `workspace-scoped`; +- версии operation и agent адресуются явно; +- published operation и published agent - ссылки на конкретные version; - ошибки валидации возвращаются отдельно от transport errors. Базовый префикс: @@ -22,431 +21,154 @@ ## 3. Основные ресурсы +- `workspaces` +- `memberships` +- `invitations` - `operations` -- `versions` +- `auth-profiles` +- `agents` +- `platform-api-keys` +- `logs` +- `usage` - `samples` - `descriptors` -- `auth-profiles` -- `test-runs` - `config import/export` -## 4. CRUD операций +## 4. Workspace-scoped routing -### `GET /api/admin/operations` +Канонический префикс для UI-driven сценариев: -Назначение: - -- список операций для UI. - -Параметры: - -- `protocol` -- `status` -- `search` - -Ответ: - -```json -{ - "items": [ - { - "id": "op_01", - "name": "crm_create_lead", - "display_name": "Create Lead", - "protocol": "rest", - "status": "draft", - "current_draft_version": 3, - "latest_published_version": 2, - "updated_at": "2026-03-25T09:00:00Z" - } - ] -} +```text +/api/admin/workspaces/{workspace_id} ``` -### `POST /api/admin/operations` - -Назначение: - -- создание новой операции и версии `1`. - -Тело: - -```json -{ - "name": "crm_create_lead", - "display_name": "Create Lead", - "protocol": "rest", - "target": { - "kind": "rest", - "base_url": "https://api.example.com", - "method": "POST", - "path_template": "/v1/leads" - }, - "input_schema": { "type": "object", "fields": {} }, - "output_schema": { "type": "object", "fields": {} }, - "input_mapping": { "rules": [] }, - "output_mapping": { "rules": [] }, - "execution_config": { - "timeout_ms": 10000 - }, - "tool_description": { - "title": "Create CRM lead", - "description": "Creates a new lead." - } -} -``` - -Ответ: - -```json -{ - "operation_id": "op_01", - "version": 1, - "status": "draft" -} -``` - -### `GET /api/admin/operations/{operation_id}` - -Назначение: - -- получить метаданные operation и ссылки на draft/published версии. - -### `GET /api/admin/operations/{operation_id}/versions/{version}` - -Назначение: - -- получить полную конфигурацию конкретной версии. - -### `POST /api/admin/operations/{operation_id}/versions` - -Назначение: - -- создать новую draft-версию на основе текущего payload. - -Тело: - -- полная конфигурация operation; -- опционально `change_note`. - -Ответ: - -```json -{ - "operation_id": "op_01", - "version": 4, - "status": "draft" -} -``` - -## 5. Публикация - -### `POST /api/admin/operations/{operation_id}/publish` - -Назначение: - -- опубликовать текущую draft-версию. - -Тело: - -```json -{ - "version": 4 -} -``` - -Ответ: - -```json -{ - "operation_id": "op_01", - "published_version": 4, - "published_at": "2026-03-25T10:00:00Z" -} -``` - -### `POST /api/admin/operations/{operation_id}/archive` - -Назначение: - -- перевести operation в archived status. - -## 6. Samples и schema artifacts - -### `POST /api/admin/operations/{operation_id}/samples/input-json` - -Назначение: - -- загрузить sample входного JSON. - -Тип: - -- `multipart/form-data` или raw `application/json`. - -Ответ: - -```json -{ - "sample_id": "file_01", - "sample_kind": "input_json" -} -``` - -### `POST /api/admin/operations/{operation_id}/samples/output-json` - -Назначение: - -- загрузить sample выходного JSON. - -`admin-api v1` реализует именно JSON samples, потому что они нужны для REST сценария и draft generation уже на первом этапе. - -### `POST /api/admin/operations/{operation_id}/drafts/generate` - -Назначение: - -- построить черновую схему и mappings из сохраненных JSON samples. - -В `admin-api v1` endpoint возвращает: - -- `generated_draft` -- сгенерированные `input_schema` -- сгенерированные `output_schema` -- сгенерированные `input_mapping` -- сгенерированные `output_mapping` - -## 7. gRPC descriptor endpoints - -Следующие endpoints относятся к фазе `grpc-support` и реализованы как часть gRPC vertical slice: - -### `POST /api/admin/operations/{operation_id}/descriptors/proto` - -Назначение: - -- загрузить `.proto`. - -### `POST /api/admin/operations/{operation_id}/descriptors/descriptor-set` - -Назначение: - -- загрузить `descriptor set`. - -Ответ: - -```json -{ - "descriptor_id": "desc_01", - "version": 1 -} -``` - -### `GET /api/admin/operations/{operation_id}/grpc/services` - -Назначение: - -- получить discovery summary по services и methods. - -Ответ: - -```json -{ - "services": [ - { - "package": "crm.v1", - "service": "LeadService", - "methods": [ - { - "name": "CreateLead", - "kind": "unary", - "input_schema": { "type": "object", "fields": {} }, - "output_schema": { "type": "object", "fields": {} } - } - ] - } - ] -} -``` - -Discovery endpoint используется для выбора unary метода и для построения UI-формы входа/выхода до публикации операции. - -## 8. Тестовый запуск - -### `POST /api/admin/operations/{operation_id}/test-runs` - -Назначение: - -- выполнить тестовый вызов draft-конфигурации. - -Тело: - -```json -{ - "version": 4, - "input": { - "name": "Alice", - "email": "alice@example.com" - } -} -``` - -Ответ: - -```json -{ - "ok": true, - "request_preview": { - "body": { - "name": "Alice", - "email": "alice@example.com" - } - }, - "response_preview": { - "id": "lead_123", - "status": "created" - }, - "errors": [] -} -``` - -## 9. Auth profiles - -### `GET /api/admin/auth-profiles` - -Назначение: - -- список доступных профилей аутентификации. - -### `POST /api/admin/auth-profiles` - -Назначение: - -- создать новый auth profile. - -Тело: - -```json -{ - "name": "crm-prod-bearer", - "kind": "bearer", - "config": { - "header_name": "Authorization", - "secret_ref": "secret://auth/crm-prod-token" - } -} -``` - -### `GET /api/admin/auth-profiles/{auth_profile_id}` - -Назначение: - -- получить metadata auth profile без раскрытия секрета. - -## 10. YAML export - -### `GET /api/admin/operations/{operation_id}/export` - -Назначение: - -- экспортировать конфигурацию операции в `YAML`. - -Параметры: - -- `version` - опционально, если нужно экспортировать не current draft; -- `mode=portable|bundle` - -Ответ: - -- `Content-Type: application/yaml` -- тело ответа - YAML document - -### Пример YAML response - -```yaml -format_version: "1" -kind: operation -operation: - name: crm_create_lead - protocol: rest - status: draft -``` - -## 11. YAML import - -### `POST /api/admin/operations/import` - -Назначение: - -- импортировать operation из YAML. - -Тип: - -- `application/yaml` -- или `multipart/form-data` с YAML файлом - -Параметры: - -- `mode=create|upsert` - -Ответ: - -```json -{ - "operation_id": "op_01", - "version": 5, - "import_mode": "upsert", - "warnings": [] -} -``` - -## 12. Ошибки - -Рекомендуемые классы ошибок: - -- `validation_error` -- `mapping_error` -- `schema_error` -- `descriptor_error` -- `auth_profile_error` -- `yaml_import_error` -- `runtime_test_error` -- `not_found` -- `conflict` - -Пример: - -```json -{ - "error": { - "code": "validation_error", - "message": "Invalid JSONPath in input_mapping rule 2", - "details": { - "field": "input_mapping.rules[1].source" - } - } -} -``` - -## 13. Что важно не допустить - -- смешивание CRUD и publish semantics в одном endpoint; -- обновление draft "поверх" существующей версии без создания новой version; -- YAML import как скрытый апдейт без явного режима `create|upsert`; -- возврат открытых секретов из auth-profile endpoints; -- привязку runtime к admin DTO; -- endpoints, возвращающие разные формы одной и той же сущности без причины. - -## 14. Практический итог - -Минимальный рабочий набор admin API для MVP: - -- список и чтение operations; -- создание новой version; -- publish; -- upload JSON samples; -- auth profiles; -- generate draft; +## 5. Группы endpoints + +### 5.1. Workspaces and members + +- `GET /api/admin/workspaces` +- `POST /api/admin/workspaces` +- `GET /api/admin/workspaces/{workspace_id}` +- `PATCH /api/admin/workspaces/{workspace_id}` +- `GET /api/admin/workspaces/{workspace_id}/members` +- `POST /api/admin/workspaces/{workspace_id}/invitations` +- `DELETE /api/admin/workspaces/{workspace_id}/invitations/{invitation_id}` + +### 5.2. Operations + +- `GET /api/admin/workspaces/{workspace_id}/operations` +- `POST /api/admin/workspaces/{workspace_id}/operations` +- `GET /api/admin/workspaces/{workspace_id}/operations/{operation_id}` +- `PATCH /api/admin/workspaces/{workspace_id}/operations/{operation_id}` +- `DELETE /api/admin/workspaces/{workspace_id}/operations/{operation_id}` +- `POST /api/admin/workspaces/{workspace_id}/operations/{operation_id}/versions` +- `GET /api/admin/workspaces/{workspace_id}/operations/{operation_id}/versions/{version}` +- `POST /api/admin/workspaces/{workspace_id}/operations/{operation_id}/publish` +- `POST /api/admin/workspaces/{workspace_id}/operations/{operation_id}/archive` +- `POST /api/admin/workspaces/{workspace_id}/operations/{operation_id}/test-runs` +- `GET /api/admin/workspaces/{workspace_id}/operations/{operation_id}/export` +- `POST /api/admin/workspaces/{workspace_id}/operations/import` + +### 5.3. Samples and descriptors + +- `POST /api/admin/workspaces/{workspace_id}/operations/{operation_id}/samples/input-json` +- `POST /api/admin/workspaces/{workspace_id}/operations/{operation_id}/samples/output-json` +- `POST /api/admin/workspaces/{workspace_id}/operations/{operation_id}/drafts/generate` +- `POST /api/admin/workspaces/{workspace_id}/operations/{operation_id}/descriptors/proto` +- `POST /api/admin/workspaces/{workspace_id}/operations/{operation_id}/descriptors/descriptor-set` +- `GET /api/admin/workspaces/{workspace_id}/operations/{operation_id}/grpc/services` + +### 5.4. Upstream auth profiles + +- `GET /api/admin/workspaces/{workspace_id}/auth-profiles` +- `POST /api/admin/workspaces/{workspace_id}/auth-profiles` +- `GET /api/admin/workspaces/{workspace_id}/auth-profiles/{auth_profile_id}` +- `PATCH /api/admin/workspaces/{workspace_id}/auth-profiles/{auth_profile_id}` +- `DELETE /api/admin/workspaces/{workspace_id}/auth-profiles/{auth_profile_id}` + +### 5.5. Agents + +- `GET /api/admin/workspaces/{workspace_id}/agents` +- `POST /api/admin/workspaces/{workspace_id}/agents` +- `GET /api/admin/workspaces/{workspace_id}/agents/{agent_id}` +- `PATCH /api/admin/workspaces/{workspace_id}/agents/{agent_id}` +- `DELETE /api/admin/workspaces/{workspace_id}/agents/{agent_id}` +- `POST /api/admin/workspaces/{workspace_id}/agents/{agent_id}/versions` +- `GET /api/admin/workspaces/{workspace_id}/agents/{agent_id}/versions/{version}` +- `POST /api/admin/workspaces/{workspace_id}/agents/{agent_id}/publish` +- `POST /api/admin/workspaces/{workspace_id}/agents/{agent_id}/bindings` +- `DELETE /api/admin/workspaces/{workspace_id}/agents/{agent_id}/bindings/{operation_id}` + +### 5.6. Platform API keys + +- `GET /api/admin/workspaces/{workspace_id}/platform-api-keys` +- `POST /api/admin/workspaces/{workspace_id}/platform-api-keys` +- `POST /api/admin/workspaces/{workspace_id}/platform-api-keys/{key_id}/revoke` +- `DELETE /api/admin/workspaces/{workspace_id}/platform-api-keys/{key_id}` + +### 5.7. Observability + +- `GET /api/admin/workspaces/{workspace_id}/logs` +- `GET /api/admin/workspaces/{workspace_id}/logs/{log_id}` +- `GET /api/admin/workspaces/{workspace_id}/usage` +- `GET /api/admin/workspaces/{workspace_id}/usage/operations/{operation_id}` +- `GET /api/admin/workspaces/{workspace_id}/usage/agents/{agent_id}` + +## 6. Page-to-endpoint mapping + +### Operations catalog + +Нужны: + +- список операций; +- удаление операции; +- edit/open operation; +- publish/archive; +- usage summary для карточек и фильтров. + +### Wizard + +Нужны: + +- create/update version; - test run; -- YAML import/export. +- samples; +- draft generation; +- gRPC descriptor upload и discovery. -Этого достаточно, чтобы UI полностью управлял жизненным циклом operation без ручного редактирования кода backend. +### Agents -`gRPC descriptor` endpoints добавляются отдельным этапом вместе с `grpc-support`. +Нужны: + +- CRUD агентов; +- bindings к operations; +- publish agent; +- выдача MCP endpoint metadata. + +### API Keys + +Нужны: + +- list/create/revoke/delete platform API keys; +- one-time reveal значения ключа при создании. + +### Logs + +Нужны: + +- list logs с фильтрами; +- log detail; +- polling или live refresh strategy. + +### Usage + +Нужны: + +- агрегаты по периодам; +- breakdown по operation; +- breakdown по agent; +- CSV export. + +## 7. Принцип совместимости + +Если UI расходится с текущим backend, приоритет отдается целевой продуктовой модели, но конфликт должен быть явно разобран в `docs/as-is-to-be.md` до начала реализации. diff --git a/docs/architecture.md b/docs/architecture.md index 51afb4b..c74ce3e 100644 --- a/docs/architecture.md +++ b/docs/architecture.md @@ -2,78 +2,161 @@ ## 1. Назначение проекта -Проект представляет собой платформу для динамической публикации внешних API в виде MCP tools. Пользователь конфигурирует операцию через административный UI вместо написания отдельного backend-обработчика. Платформа сохраняет конфигурацию, валидирует ее, позволяет выполнить тестовый вызов и публикует операцию для использования LLM через MCP. +Crank - платформа для публикации внешних API в виде MCP tools без написания отдельного backend-кода под каждую интеграцию. Пользователь конфигурирует интеграции через UI, а система: -Главная инженерная цель проекта - представить разные протоколы как единый набор операций с точки зрения MCP-слоя. +- хранит и версионирует операции; +- группирует их по workspace; +- публикует их в составе конкретных agents; +- выдает LLM не глобальный каталог tools, а curated toolset на один agent; +- собирает продуктовые логи и usage по workspace, agent и operation. -## 2. Ключевой принцип проектирования +## 2. Переход `As Is -> To Be` -Центральная абстракция системы - `Operation`. +### 2.1. As Is -Каждая операция описывает один вызываемый элемент независимо от протокола: +Текущее ядро системы построено вокруг: -- `name` - внутреннее уникальное имя. -- `display_name` - имя, отображаемое в UI. -- `protocol` - `rest`, `graphql` или `grpc`. -- `target` - хост и протокол-специфичное описание назначения. -- `input_schema` - нормализованный входной контракт. -- `input_mapping` - правила отображения MCP-входа в поля целевого запроса. -- `execution_config` - auth-профиль, таймауты, заголовки и протокол-специфичные параметры. -- `output_mapping` - правила отображения ответа внешней системы в нормализованный выход. -- `tool_description` - метаданные для MCP и LLM. -- `status` - draft, testing, published, archived. +- глобальной сущности `Operation`; +- registry версий операций; +- runtime adapters `REST / GraphQL / unary gRPC`; +- `admin-api` для CRUD и тестовых вызовов; +- `mcp-server`, который публикует tools из published operations. -MCP server должен понимать только нормализованный контракт. Протокольные адаптеры должны преобразовывать нормализованную модель в конкретный REST, GraphQL или gRPC вызов и затем возвращать ответ обратно в нормализованный JSON. +### 2.2. To Be -## 3. Границы продукта +Целевая архитектура расширяет текущее ядро до модели: -### Входит в MVP +- `Workspace` - tenant boundary; +- `Operation` - интеграционный контракт; +- `Agent` - curated MCP surface; +- `Platform API key` и `Membership` - доступ к самой платформе; +- `Invocation log` и `Usage rollup` - observability слой. -- Административный UI для создания и редактирования операций. -- Динамический реестр операций. -- Runtime-выполнение REST операций. -- Runtime-выполнение GraphQL операций. -- Runtime-выполнение unary gRPC методов. -- Загрузка примеров `JSON` для ускоренного создания схем и mappings. -- Импорт и экспорт конфигураций в `YAML`. -- Тестирование операций до публикации. -- Публикация MCP tools на основе данных из реестра. -- Hot reload опубликованных операций без изменения backend-кода. +`Operation` остается фундаментом интеграции, но tools больше не публикуются глобально. MCP публикует tools в контексте конкретного `workspace` и конкретного `agent`. -### Не входит в MVP +## 3. Ключевые сущности и их роль + +### `Workspace` + +Изолирует: + +- операции; +- auth profiles; +- agents; +- platform API keys; +- logs и usage; +- пользователей и роли. + +### `Operation` + +Описывает один вызываемый элемент независимо от протокола: + +- `name` +- `display_name` +- `protocol` +- `target` +- `input_schema` +- `input_mapping` +- `execution_config` +- `output_mapping` +- `tool_description` +- `status` + +### `Agent` + +Является пользовательской MCP-поверхностью для LLM. + +`Agent`: + +- принадлежит одному workspace; +- имеет `slug`, `display_name`, `description`, `status`; +- ссылается на ограниченный набор published operations; +- формирует отдельный MCP endpoint; +- решает проблему "одному агенту нельзя отдавать 100 tools сразу". + +### `Platform access` + +Отдельный слой, не связанный с upstream auth: + +- `User` +- `Membership` +- `Invitation` +- `PlatformApiKey` + +### `Observability` + +Отдельный продуктовый слой: + +- `InvocationLog` +- `InvocationEvent` +- `UsageRollup` +- `LatencyStats` + +## 4. Главный принцип проектирования + +Система строится в три слоя: + +1. `Operation` как низкоуровневый интеграционный контракт. +2. `Agent` как curated набор published operations. +3. `Workspace` как граница данных, доступа и observability. + +Это позволяет: + +- переиспользовать одну operation в нескольких agents; +- ограничивать tool catalog для конкретного LLM-сценария; +- изолировать данные команд; +- строить logs и usage не глобально, а по tenant boundary. + +## 5. Границы целевого MVP + +### Входит + +- `Workspace` как tenant boundary. +- Операции `REST`, `GraphQL`, `unary gRPC`. +- `Agent` и привязка операций к агенту. +- Agent-scoped MCP endpoints. +- Platform API keys. +- Workspace-scoped auth profiles для upstream access. +- Product logs и usage aggregates. +- Импорт и экспорт operation-конфигураций в `YAML`. +- Hot reload опубликованных agents и operations. + +### Не входит - gRPC streaming. -- Полноценный импорт OpenAPI с автоматической генерацией маппинга. -- Полноценный визуальный конструктор GraphQL-запросов. - SOAP. -- Выполнение произвольного кода внутри mapping-правил. -- Оркестрация нескольких операций в виде workflow. -- Мультитенантность и биллинг. +- Оркестрация workflow. +- Биллинг. +- Full RBAC policy engine. +- Traffic splitting и deployment orchestration. -## 4. Пользовательский сценарий +## 6. Пользовательские сценарии -Сценарий работы оператора должен быть одинаковым для всех протоколов: +### Оператор операций -1. Выбрать протокол. -2. Указать целевой хост или сервер. -3. Выбрать или описать внешнюю операцию. -4. Определить MCP-входные параметры. -5. Сопоставить MCP-вход с внешним запросом. -6. Сопоставить внешний ответ с MCP-выходом. -7. Добавить описание для MCP и LLM. -8. Выполнить тестовый вызов. -9. Опубликовать операцию. +1. Выбирает workspace. +2. Создает или редактирует operation. +3. Выполняет test run. +4. Публикует operation version. +5. Привязывает operation к одному или нескольким agents. -UI должен максимально скрывать протокольную сложность. REST endpoint, GraphQL operation и gRPC method должны отображаться для оператора как "операция с входными и выходными параметрами". +### Оператор агентов -## 5. Стратегия по протоколам +1. Создает agent. +2. Выбирает набор published operations. +3. Публикует agent. +4. Получает MCP endpoint вида `/mcp/v1/{workspace}/{agent}`. + +### Администратор workspace + +1. Управляет API keys платформы. +2. Управляет пользователями и ролями. +3. Смотрит logs и usage. + +## 7. Стратегия по протоколам ### REST -REST-адаптер является базовым и должен реализовываться первым. - -Поддержка в MVP: - - `GET` - `POST` - `PUT` @@ -84,22 +167,9 @@ REST-адаптер является базовым и должен реализ - headers - JSON request body - JSON response body -- аутентификация `Bearer`, `Basic` и API key - -Пользователь настраивает: - -- base URL, -- HTTP method, -- path template, -- request mapping, -- response mapping. ### GraphQL -Поддержка GraphQL в MVP должна быть намеренно упрощена. - -Поддержка в MVP: - - `query` - `mutation` - endpoint URL @@ -108,56 +178,16 @@ REST-адаптер является базовым и должен реализ - variables mapping - извлечение результата из `data` -Пользователь настраивает: - -- GraphQL endpoint, -- шаблон операции, -- схему переменных, -- маппинг переменных, -- путь к нужным данным в ответе. - -Introspection может быть добавлен позже как вспомогательная функция UI, но первая рабочая версия системы не должна от него зависеть. - -Ключевое ограничение GraphQL в проекте: одна MCP operation должна соответствовать одному конкретному GraphQL-запросу или mutation с заранее определенным selection set. Платформа не должна пытаться передавать LLM всю гибкость GraphQL, потому что LLM не должен формировать произвольный набор полей и произвольную структуру параметров для одного и того же tool. - -С точки зрения MCP GraphQL в этой системе намеренно превращается в более жесткий интерфейс: - -- один tool; -- один шаблон `query` или `mutation`; -- фиксированный набор входных параметров; -- один предсказуемый формат ответа. - -Фактически на слое MCP "универсальность" GraphQL сознательно сужается до модели, близкой к RPC или REST operation. Это делается для того, чтобы tool оставался понятным для LLM, валидируемым, предсказуемым по структуре ответа и пригодным для явного mapping. +GraphQL в MCP публикуется как фиксированная операция с предсказуемой структурой ответа. ### gRPC -gRPC - наиболее сложный протокол в этом проекте, поэтому его нужно ограничить на раннем этапе. +- только unary RPC; +- `.proto` и `descriptor set`; +- JSON-oriented schema model поверх protobuf; +- без streaming. -Поддержка в MVP: - -- только unary RPC, -- загрузка `.proto`, -- загрузка descriptor set, -- опционально server reflection на более позднем этапе, -- преобразование между нормализованным JSON и protobuf-сообщениями. - -Рекомендуемый путь реализации: - -1. Принимать descriptor set как основной машинно-читаемый источник схемы. -2. Опционально принимать `.proto` для удобства оператора. -3. Парсить descriptor во внутреннюю модель схемы, удобную для UI. -4. Показывать services, methods, входные поля и выходные поля в виде структурированной формы. -5. Позволять пользователю настраивать input и output mapping. - -Такой подход превращает gRPC для оператора в тот же опыт, что и REST: выбрать метод, посмотреть параметры, сопоставить поля, протестировать, опубликовать. - -Streaming gRPC сознательно не входит в рамки проекта. Платформа ориентирована на MCP tool invocation, а MCP tool в этой системе моделируется как сценарий `запрос -> один ответ`. LLM не работает с долгоживущими транспортными сессиями и не нуждается в обработке потока сообщений для такого типа интеграции. Поэтому `server streaming`, `client streaming` и `bidirectional streaming` исключаются как архитектурно избыточные для выбранной модели взаимодействия. - -Тот же принцип применяется и к GraphQL: даже если внешний GraphQL endpoint допускает очень гибкий способ получения данных, в MCP публикуются только заранее зафиксированные операции с контролируемым входом и контролируемым ответом. - -## 6. Работа с файлами и автогенерация черновика - -Для упрощения конфигурирования система должна поддерживать загрузку файлов и примеров данных, из которых можно собрать стартовую конфигурацию operation. +## 8. Работа с файлами и автогенерация черновика Поддерживаемые источники: @@ -168,368 +198,62 @@ Streaming gRPC сознательно не входит в рамки проек Ожидаемый сценарий: -1. Оператор загружает пример входных данных и пример ответа. -2. Система строит черновую схему входа и выхода. -3. Система предлагает стартовый mapping по совпадающим или близким по структуре полям. -4. Оператор вручную корректирует результат. -5. Для точечной настройки используется `JSONPath`. -6. Готовую конфигурацию можно экспортировать в `YAML` или импортировать обратно. +1. оператор загружает артефакты; +2. система строит черновую схему и mapping; +3. оператор вручную корректирует результат; +4. готовую конфигурацию можно экспортировать в `YAML`. -Для gRPC источником структуры является не пример JSON-сообщения, а `.proto` или descriptor set. Однако после преобразования protobuf-схемы во внутреннюю JSON-ориентированную модель пользовательский опыт должен оставаться тем же: видим структуру полей, получаем стартовый mapping, затем уточняем его вручную. +## 9. Внутренняя модель данных -`YAML` используется как человекочитаемое представление конфигурации operation для: +Базовые сущности: -- переноса между окружениями; -- резервного копирования; -- хранения в git; -- редактирования вне UI; -- пакетного импорта нескольких operation. +- `Workspace` +- `Operation` +- `OperationVersion` +- `Agent` +- `AgentVersion` +- `AgentOperationBinding` +- `AuthProfile` +- `PlatformApiKey` +- `InvocationLog` +- `UsageRollup` -Storage backend для sample-файлов, `.proto`, `descriptor set` и YAML import payload в MVP должен быть локальным файловым хранилищем приложения с явным `storage_ref`. В дальнейшем этот слой можно заменить на S3-compatible storage без изменения доменной модели. +## 10. MCP publishing model -## 7. Внутренняя модель данных +Публикация tools строится так: -Система должна приводить все данные к JSON-ориентированным структурам, чтобы UI, registry и MCP runtime работали с единым контрактом. +1. `Operation` проходит versioning и publish. +2. `Agent` собирает curated набор published operations. +3. `MCP server` читает published view конкретного agent. +4. `tools/list` и `tools/call` работают в контексте `workspace + agent`. -### Operation +## 11. Observability -- `id` -- `name` -- `display_name` -- `protocol` +На каждый вызов tool сохраняются: + +- `workspace_id` +- `agent_id` +- `operation_id` +- `request_id` +- `timestamp` - `status` -- `target` -- `input_schema` -- `output_schema` -- `input_mapping` -- `output_mapping` -- `execution_config` -- `tool_description` -- `created_at` -- `updated_at` +- `duration_ms` +- `error_kind` +- `request_preview` +- `response_preview` -### Target +Сверху строятся: -REST target: +- logs page; +- usage page; +- периодические rollups; +- latency and error aggregates. -- `base_url` -- `method` -- `path_template` +## 12. Модель маппинга -GraphQL target: +Платформе нужен отдельный слой маппинга: -- `endpoint` -- `operation_type` -- `operation_name` -- `query_template` - -gRPC target: - -- `server_addr` -- `package` -- `service` -- `method` -- `descriptor_ref` -- `descriptor_set_b64` - -### Schema - -Нормализованный формат схемы должен поддерживать: - -- скалярные поля, -- вложенные объекты, -- массивы, -- enum, -- nullable-поля, -- `oneof` для схем, пришедших из protobuf. - -Транспортный формат между внутренними компонентами должен оставаться JSON, даже если конкретный адаптер под капотом работает с protobuf. - -## 8. Модель маппинга - -Платформе нужен отдельный слой маппинга, потому что MCP-facing параметры не совпадают напрямую с payload внешнего API. - -Начальная версия mapping-системы должна оставаться простой, но при этом достаточно выразительной для работы со вложенными структурами: - -- сопоставление поле-в-поле по `JSONPath`, -- константы, -- значения по умолчанию, +- сопоставление поле-в-поле по `JSONPath`; +- константы; +- значения по умолчанию; - извлечение вложенных полей из ответа. - -Примеры: - -- `$.mcp.user_id -> $.request.path.userId` -- `$.mcp.limit -> $.request.query.limit` -- `$.response.data.user.name -> $.output.name` -- `$.response.user.email -> $.output.email` - -`JSONPath` используется как единый способ адресации вложенных значений в input/output mapping. Это позволяет управлять структурой и вложенностью без написания пользовательского кода. - -Для MVP mapping engine не должен поддерживать произвольные скрипты. Достаточно единого движка `JSONPath`, констант, defaults и ограниченного набора встроенных преобразований. - -Черновой mapping может генерироваться автоматически на основе загруженных примеров данных, но итоговая конфигурация всегда остается явной и редактируемой оператором. - -Для MVP допустима простая стратегия draft generation: - -- искать уникальные leaf-поля с одинаковыми именами; -- предлагать только однозначные соответствия; -- не пытаться автоматически разрешать конфликты и неоднозначности. - -Каноническая логическая модель остается общей для runtime и БД, но система должна уметь сериализовать и десериализовать ее также в `YAML`. - -## 9. Основные компоненты - -### `crank-core` - -Ответственность: - -- общие доменные типы, -- идентификаторы, -- статусы и базовые protocol-specific target types, -- общие ошибки. - -### `crank-schema` - -Ответственность: - -- нормализованные схемы входа и выхода, -- представление типов и полей для UI и runtime, -- валидация JSON относительно внутренней схемы. - -### `crank-mapping` - -Ответственность: - -- модель mapping-правил, -- `JSONPath` parser и validator, -- применение input/output mapping, -- генерация чернового mapping по sample-данным и схемам. - -### `crank-proto` - -Ответственность: - -- загрузка `.proto` и `descriptor set`, -- protobuf discovery, -- извлечение services, methods и message schemas, -- преобразование protobuf metadata в нормализованные схемы. - -### `crank-registry` - -Ответственность: - -- постоянное хранение операций, -- CRUD для draft и published операций, -- выдача списка активных tools, -- инвалидация кэша и сигналы на reload. - -### `crank-runtime` - -Ответственность: - -- выполнение нормализованных операций, -- выбор нужного протокольного адаптера, -- применение input mapping, -- применение output mapping, -- единообразные runtime-ошибки. - -### `crank-adapter-rest` - -Ответственность: - -- сборка HTTP-запроса из нормализованного входа, -- отправка запроса через `reqwest`, -- нормализация HTTP-ответа в JSON. - -### `crank-adapter-graphql` - -Ответственность: - -- формирование GraphQL payload, -- подстановка переменных, -- отправка запроса, -- извлечение `data` и ошибок из GraphQL-ответа. - -### `crank-adapter-grpc` - -Ответственность: - -- сборка protobuf request message из нормализованного JSON, -- вызов unary RPC метода, -- преобразование protobuf response обратно в нормализованный JSON. - -### `crank-admin-api` - -Ответственность: - -- CRUD endpoints для UI, -- создание и управление version snapshots, -- import/export конфигураций в `YAML`, -- загрузка sample JSON, -- загрузка `.proto` и descriptor set, -- endpoints для тестового выполнения операций, -- discovery endpoints для gRPC metadata. - -### `crank-mcp-server` - -Ответственность: - -- список доступных MCP tools из registry, -- валидация входа tool по нормализованной схеме, -- делегирование выполнения в runtime, -- возврат нормализованного результата MCP-клиенту. - -### `crank-ui` - -Ответственность: - -- wizard создания сервиса и операции, -- editor для mapping, -- загрузка sample-файлов и schema artifacts, -- экран тестового вызова, -- браузер gRPC схемы, -- import/export конфигураций, -- workflow публикации и отображение статуса. - -### Deployment layer - -Ответственность: - -- контейнерная упаковка приложений; -- orchestration через `docker-compose`; -- reverse proxy routing; -- healthchecks и delivery pipeline. - -Этот слой не должен влиять на доменную модель и application contracts. - -## 10. Предлагаемая структура репозитория - -Для реализации рекомендуется workspace-структура: - -```text -crank/ - apps/ - admin-api/ - mcp-server/ - ui/ - crates/ - crank-core/ - crank-schema/ - crank-mapping/ - crank-proto/ - crank-registry/ - crank-runtime/ - crank-adapter-rest/ - crank-adapter-graphql/ - crank-adapter-grpc/ - docs/ -``` - -Такая структура позволяет держать протокольные адаптеры независимыми и отдельно тестируемыми. - -## 11. Технологический стек - -### Backend - -- Rust -- `tokio` как async runtime -- `axum` для HTTP API -- `serde` и `serde_json` -- `sqlx` для PostgreSQL -- `reqwest` для REST и GraphQL транспорта -- `tonic` и `prost` для работы с gRPC -- `tower` для middleware -- `tracing` для логирования и диагностики - -### Frontend - -- TypeScript -- React -- Vite -- React Router -- TanStack Query -- React Hook Form -- Zod - -Этот стек прагматичен для внутреннего административного UI: быстрая итерация, удобная работа с формами, понятная интеграция с API и отсутствие лишней сложности. - -## 12. Почему React + Vite для UI - -Frontend в этом проекте - это операторская консоль, а не контентный сайт. Server-side rendering здесь не требуется. Основные требования: - -- динамические формы, -- schema-driven рендеринг, -- экраны тестирования и предпросмотра, -- адаптивные административные страницы, -- высокая скорость локальной разработки. - -`React + TypeScript + Vite` хорошо подходит под эти условия, потому что позволяет развивать frontend независимо от Rust-сервисов и быстро собирать сложные формы вроде mapping editor и gRPC method inspector. - -## 13. Почему Axum для backend - -`axum` выбран как основной backend-фреймворк по следующим причинам: - -- он построен поверх `tower` и хорошо согласуется с современным async-стеком Rust, -- он естественно интегрируется с `tokio`, `hyper` и middleware-композицией, -- он лучше подходит для модульной структуры с несколькими сервисами, -- он удобен для typed handlers, shared state и собственных extractors, -- он лучше сочетается с `tonic`, который используется для gRPC. - -Детальная декомпозиция crates и модулей вынесена в `docs/module-decomposition.md`. -Формальная модель данных вынесена в `docs/data-model.md`. -Схема БД и versioning описаны в `docs/database-schema.md`. -HTTP-контракты административного API описаны в `docs/admin-api.md`. -Диаграммы компонентов, сущностей и потоков вынесены в `docs/diagrams.md`. -MCP transport и способ публикации tools описаны в `docs/mcp-interface.md`. -Стратегия тестирования описана в `docs/testing-strategy.md`, а runtime-конфигурация и storage assumptions - в `docs/runtime-config.md`. -Rust-oriented распределение методов, `impl`, `trait` и service-слоя описано в `docs/rust-design.md`. -Правила разработки и TDD-процесс описаны в `docs/development-rules.md`, а последовательность модулей и фич - в `docs/implementation-plan.md`. -Rust-specific правила кода, linting и toolchain описаны в `docs/rust-code-rules.md`. -Требования и ограничения по конкретным протоколам вынесены в `docs/protocols/rest.md`, `docs/protocols/graphql.md` и `docs/protocols/grpc.md`. - -## 14. Runtime-поток - -### Создание операции - -1. UI отправляет draft операции в admin API. -2. Admin API валидирует схему и mappings. -3. Registry сохраняет draft. -4. UI запускает тестовый вызов через runtime. -5. Оператор публикует операцию. -6. Registry помечает операцию как active. -7. MCP server перезагружает активные операции. - -### Выполнение tool - -1. MCP client вызывает tool. -2. MCP server берет определение tool из памяти. -3. Runtime валидирует вход относительно нормализованной схемы. -4. Runtime применяет input mapping. -5. Runtime вызывает нужный протокольный адаптер. -6. Runtime применяет output mapping. -7. MCP server возвращает нормализованный результат. - -Эта последовательность соответствует модели `один запрос -> один ответ`. Именно поэтому поддержка streaming-протоколов не рассматривается как часть MVP: она не соответствует целевой модели вызова tools со стороны LLM. -По этой же причине GraphQL tools должны быть заранее специализированы под конкретный сценарий вызова, а не представлять собой общий конструктор запросов для LLM. - -## 15. Нефункциональные требования - -- Новые операции должны добавляться без изменения backend-кода. -- Опубликованные операции должны становиться видимыми для MCP-клиентов без пересборки сервиса. -- Runtime-ошибки должны быть наблюдаемыми и различимыми по этапам. -- Система должна оставаться детерминированной и пригодной для аудита. -- Протокольные адаптеры должны тестироваться независимо. -- Все опубликованные операции должны укладываться в модель синхронного или квазисинхронного вызова `запрос -> ответ`. -- Все опубликованные GraphQL operations должны иметь фиксированный шаблон запроса и фиксированную структуру ожидаемого результата. - -## 16. Основные риски - -- Динамическая работа с protobuf заметно сложнее, чем REST и GraphQL. -- UX для маппинга может стать слишком тяжелым, если не ограничить его заранее. -- Нормализация схем может стать непоследовательной без строгой внутренней модели. -- Попытка поддержать слишком много возможностей протоколов замедлит реализацию. -- Попытка сохранить всю динамическую гибкость GraphQL на уровне MCP приведет к слишком широким и плохо управляемым tools. - -Поэтому проект должен в первую очередь реализовать один чистый end-to-end сценарий, а не широкий, но поверхностный охват возможностей. - -`SOAP` в этой версии проекта сознательно отложен. Это не забытый протокол, а отдельное направление развития, которое потребует самостоятельного XML/WSDL слоя, отдельной схемной модели и отдельного адаптера. diff --git a/docs/as-is-to-be.md b/docs/as-is-to-be.md new file mode 100644 index 0000000..ada15dd --- /dev/null +++ b/docs/as-is-to-be.md @@ -0,0 +1,264 @@ +# As Is -> To Be + +## 1. Назначение документа + +Этот документ фиксирует переход от текущего состояния проекта к целевой продуктовой модели, которую задает `test-ui`. + +## 2. As Is + +Сейчас проект умеет: + +- хранить и версионировать `Operation`; +- выполнять `REST`, `GraphQL`, `unary gRPC`; +- выполнять test run; +- публиковать operations в MCP; +- импортировать и экспортировать operation-конфигурации; +- загружать samples и gRPC descriptors. + +Сейчас проект не умеет как first-class product features: + +- `Workspace` +- `Agent` +- platform API keys +- members / invitations +- logs API +- usage API +- agent-scoped MCP toolsets + +## 3. To Be + +Целевая система должна работать так: + +- каждая команда работает в своем `Workspace`; +- операции создаются и тестируются внутри workspace; +- опубликованные операции привязываются к `Agent`; +- один `Agent` отдает LLM ограниченный набор tools; +- доступ к платформе контролируется через memberships и platform API keys; +- все вызовы попадают в logs и usage. + +## 4. Page-by-page gap analysis + +### 4.1. Operations + +Что хочет UI: + +- список операций; +- фильтры; +- edit/delete; +- publish/archive; +- верхние метрики; +- фильтр по agent. + +Что уже есть: + +- list/create/version/publish/test/export/import; +- samples и draft generation. + +Чего не хватает: + +- delete operation; +- archive operation; +- usage summary для карточек; +- связь operation с agent; +- workspace scoping. + +### 4.2. Wizard + +Что хочет UI: + +- create/edit operation; +- сохранить draft; +- тестировать и потом публиковать; +- работать с `REST / GraphQL / gRPC`; +- descriptors и schema-driven setup. + +Что уже есть: + +- почти весь operation lifecycle; +- samples; +- draft generation; +- gRPC descriptor workflow. + +Чего не хватает: + +- нормальный update flow без local storage; +- workspace-scoped endpoints; +- единый backend contract под final wizard shape. + +### 4.3. Agents + +Что хочет UI: + +- каталог агентов; +- create/edit agent; +- выбрать список operations; +- получить MCP endpoint агента. + +Что уже есть: + +- ничего как отдельный product layer. + +Чего не хватает: + +- сущность `Agent`; +- `AgentVersion`; +- `AgentOperationBinding`; +- publish agent; +- agent-scoped MCP runtime. + +### 4.4. API Keys + +Что хочет UI: + +- list/create/revoke/delete platform API keys; +- scopes; +- one-time reveal. + +Что уже есть: + +- только upstream `auth_profiles`. + +Чего не хватает: + +- отдельная сущность `PlatformApiKey`; +- hashing/secrets; +- scopes model; +- endpoints и audit. + +### 4.5. Logs + +Что хочет UI: + +- список логов; +- detail view; +- filters; +- live mode. + +Что уже есть: + +- только application logging. + +Чего не хватает: + +- продуктовая сущность `InvocationLog`; +- storage; +- list/detail API; +- polling/live refresh strategy. + +### 4.6. Usage + +Что хочет UI: + +- usage dashboard; +- p50/p95/p99; +- error rate; +- per-operation breakdown; +- CSV export. + +Что уже есть: + +- продуктового usage слоя нет. + +Чего не хватает: + +- `UsageRollup`; +- aggregation jobs; +- reporting API; +- export endpoint. + +### 4.7. Workspace / Settings + +Что хочет UI: + +- create/edit workspace; +- members and invitations; +- settings. + +Что уже есть: + +- ничего как backend model. + +Чего не хватает: + +- `Workspace`; +- `User`; +- `Membership`; +- `Invitation`; +- workspace-scoped routing. + +### 4.8. Login + +Что хочет UI: + +- platform sign-in flow. + +Что уже есть: + +- внешний `Basic Auth` на уровне `nginx`. + +Чего не хватает: + +- либо собственный auth/session backend; +- либо временный согласованный bridge, если login screen оставляем как demo flow. + +## 5. Архитектурные конфликты, которые нужно разобрать отдельно + +### Конфликт 1. Global operations vs workspace model + +Решение: + +- ввести `workspace_id` во все продуктовые сущности. + +### Конфликт 2. Published operations vs agents + +Решение: + +- MCP публикует tools не напрямую из operations, а из `published agent`. + +### Конфликт 3. Upstream auth vs platform API keys + +Решение: + +- оставить `AuthProfile` только для upstream; +- ввести отдельную сущность `PlatformApiKey`. + +### Конфликт 4. Application logs vs product logs + +Решение: + +- ввести `InvocationLog` и `UsageRollup`. + +### Конфликт 5. Basic Auth vs login page + +Решение: + +- зафиксировать временную и целевую auth model отдельно. + +## 6. Приоритет реализации + +### Wave 1 + +- Workspace foundation +- Operations + Wizard integration +- Agents +- Agent-scoped MCP + +### Wave 2 + +- Platform API keys +- Logs +- Usage + +### Wave 3 + +- Members / invitations +- Login / session layer + +## 7. Короткий итог + +Целевой UI не требует выбросить текущее ядро. Он требует добавить сверху: + +- tenant layer; +- curated agent layer; +- observability layer; +- platform access layer. diff --git a/docs/data-model.md b/docs/data-model.md index 0638682..2a82bdd 100644 --- a/docs/data-model.md +++ b/docs/data-model.md @@ -2,159 +2,209 @@ ## 1. Назначение документа -Этот документ фиксирует формальную модель данных платформы. Его цель - определить такие структуры, которые можно без существенных изменений перенести в: +Этот документ фиксирует целевую формальную модель данных платформы. Его цель - определить такие структуры, которые можно без существенных изменений перенести в: -- Rust domain types, -- HTTP DTO, -- структуру таблиц БД, -- runtime-представление operation, +- Rust domain types; +- HTTP DTO; +- структуру таблиц БД; +- runtime-представление операций и агентов; - UI-формы и конфигурационные экраны. -Документ не привязан к конкретной СУБД, но задает каноническую JSON-модель сущностей. - ## 2. Общие принципы модели -### 2.1. Одна операция - один tool +### 2.1. Одна операция - один интеграционный контракт -Каждая `Operation` соответствует одному MCP tool. Это особенно важно для: +Каждая `Operation` соответствует одному интеграционному контракту: -- GraphQL, где одна operation соответствует одному конкретному `query` или `mutation`; -- gRPC, где одна operation соответствует одному unary-методу; -- REST, где одна operation соответствует одному endpoint-сценарию. +- GraphQL -> один конкретный `query` или `mutation`; +- gRPC -> один unary method; +- REST -> один endpoint-сценарий. + +Однако MCP tool публикуется не напрямую из operation, а через `AgentOperationBinding` внутри конкретного `Agent`. ### 2.2. Внутренний транспортный формат - JSON -Независимо от внешнего протокола внутри системы данные должны быть представлены в JSON-ориентированном виде. Даже если внешний вызов работает с protobuf, runtime, mapping и UI опираются на нормализованный JSON. +Независимо от внешнего протокола внутри системы данные представлены в JSON-ориентированном виде. ### 2.3. Mapping всегда явный -Даже если система умеет строить черновой mapping по примерам данных, итоговая конфигурация mapping должна быть явно сохранена в operation. Нельзя полагаться на неявную "магию" сопоставления во время выполнения. +Даже если система умеет строить черновой mapping по примерам данных, итоговая конфигурация mapping сохраняется явно. ### 2.4. JSONPath как единый язык адресации -Для input и output mapping используется `JSONPath`. Это позволяет единообразно ссылаться на вложенные поля во входе, промежуточном представлении запроса и нормализованном ответе. +Для input и output mapping используется `JSONPath`. -### 2.5. YAML как формат обмена конфигурацией +### 2.5. Workspace - обязательная граница данных -Помимо канонической JSON-модели система должна поддерживать импорт и экспорт конфигураций в `YAML`. Это внешний формат обмена, а не отдельная доменная модель. +Все продуктовые сущности принадлежат одному `Workspace`. -## 3. Корневая сущность `Operation` +Минимальный набор workspace-scoped сущностей: -`Operation` - основная конфигурационная сущность платформы. +- `Operation` +- `OperationVersion` +- `AuthProfile` +- `Agent` +- `PlatformApiKey` +- `InvocationLog` +- `UsageRollup` -### Поля +### 2.6. YAML как формат обмена конфигурацией -- `id` - уникальный идентификатор операции. -- `name` - стабильное внутреннее имя. -- `display_name` - отображаемое имя в UI. -- `protocol` - `rest`, `graphql`, `grpc`. -- `status` - `draft`, `testing`, `published`, `archived`. -- `version` - версия конфигурации операции. -- `target` - описание внешней операции. -- `input_schema` - схема MCP-входа. -- `output_schema` - схема MCP-выхода. -- `input_mapping` - правила подготовки внешнего запроса. -- `output_mapping` - правила формирования MCP-ответа. -- `execution_config` - auth, headers, timeout, retries и protocol-specific execution settings. -- `tool_description` - описание tool для MCP и LLM. -- `samples` - загруженные образцы JSON и schema artifacts. -- `generated_draft` - автоматически построенный черновик схем и mappings. -- `config_export` - опциональные метаданные экспортируемой конфигурации. +Помимо канонической JSON-модели система поддерживает импорт и экспорт конфигураций в `YAML`. + +## 3. Корневые сущности + +### 3.1. `Workspace` + +Поля: + +- `id` +- `slug` +- `display_name` +- `status` +- `settings` +- `created_at` +- `updated_at` + +Назначение: + +- логическая изоляция команд; +- scoping для операций, агентов, ключей и логов; +- основа для multi-tenant MCP endpoints. + +### 3.2. `Operation` + +Поля: + +- `id` +- `workspace_id` +- `name` +- `display_name` +- `protocol` +- `status` +- `version` +- `target` +- `input_schema` +- `output_schema` +- `input_mapping` +- `output_mapping` +- `execution_config` +- `tool_description` +- `samples` +- `generated_draft` +- `config_export` - `created_at` - `updated_at` - `published_at` -### Пример +### 3.3. `Agent` -```json -{ - "id": "op_01hr7w0m6p8x9z4n7s2k3q5t6u", - "name": "crm_create_lead", - "display_name": "Create Lead", - "protocol": "rest", - "status": "draft", - "version": 3, - "target": { - "kind": "rest", - "base_url": "https://api.example.com", - "method": "POST", - "path_template": "/v1/leads" - }, - "input_schema": { - "type": "object", - "fields": { - "name": { - "type": "string", - "required": true - }, - "email": { - "type": "string", - "required": true - } - } - }, - "output_schema": { - "type": "object", - "fields": { - "id": { - "type": "string", - "required": true - }, - "status": { - "type": "string", - "required": true - } - } - }, - "input_mapping": { - "rules": [ - { - "source": "$.mcp.name", - "target": "$.request.body.name" - }, - { - "source": "$.mcp.email", - "target": "$.request.body.email" - } - ] - }, - "output_mapping": { - "rules": [ - { - "source": "$.response.body.id", - "target": "$.output.id" - }, - { - "source": "$.response.body.status", - "target": "$.output.status" - } - ] - }, - "execution_config": { - "timeout_ms": 10000, - "auth_profile_ref": "auth_01hr7x8rj2d8nq8v0c4m4t1r9e" - }, - "tool_description": { - "title": "Create CRM lead", - "description": "Creates a new lead in CRM by name and email." - }, - "samples": { - "input_json_sample_ref": "file_01hr7xj7z4k1t0qm0cxsw6f1wx", - "output_json_sample_ref": "file_01hr7xkvt3m1p6ms3m7r2dr8wz" - }, - "generated_draft": { - "status": "available", - "source_types": ["input_json_sample", "output_json_sample"] - }, - "config_export": { - "format_version": "1", - "export_mode": "portable" - }, - "created_at": "2026-03-25T08:00:00Z", - "updated_at": "2026-03-25T08:10:00Z", - "published_at": null -} -``` +`Agent` - пользовательская MCP-поверхность, которая собирает ограниченный набор published operations. + +Поля: + +- `id` +- `workspace_id` +- `slug` +- `display_name` +- `description` +- `status` +- `current_draft_version` +- `latest_published_version` +- `created_at` +- `updated_at` +- `published_at` + +### 3.4. `AgentVersion` + +Снимок конфигурации агента. + +Поля: + +- `agent_id` +- `version` +- `status` +- `instructions` +- `tool_selection_policy` +- `bindings` +- `created_at` + +### 3.5. `AgentOperationBinding` + +Связь published operation с agent version. + +Поля: + +- `operation_id` +- `operation_version` +- `tool_name` +- `tool_title` +- `tool_description_override` +- `enabled` + +### 3.6. `AuthProfile` + +Используется только для доступа к внешним системам. + +Поля: + +- `id` +- `workspace_id` +- `name` +- `kind` +- `config` + +### 3.7. `PlatformApiKey` + +Отдельная сущность для доступа к самой платформе. + +Поля: + +- `id` +- `workspace_id` +- `name` +- `prefix` +- `scopes` +- `status` +- `created_at` +- `last_used_at` + +### 3.8. `InvocationLog` + +Продуктовая запись о вызове tool. + +Поля: + +- `id` +- `workspace_id` +- `agent_id` +- `operation_id` +- `request_id` +- `level` +- `status` +- `duration_ms` +- `error_kind` +- `request_preview` +- `response_preview` +- `created_at` + +### 3.9. `UsageRollup` + +Агрегированная статистика по периоду. + +Поля: + +- `workspace_id` +- `agent_id` +- `operation_id` +- `period_kind` +- `period_start` +- `calls_total` +- `calls_ok` +- `calls_error` +- `p50_ms` +- `p95_ms` +- `p99_ms` ## 4. `Target` @@ -162,20 +212,6 @@ ### 4.1. `RestTarget` -```json -{ - "kind": "rest", - "base_url": "https://api.example.com", - "method": "PATCH", - "path_template": "/v1/users/{userId}", - "static_headers": { - "X-App-Source": "crank" - } -} -``` - -Поля: - - `kind` - `base_url` - `method` @@ -184,19 +220,6 @@ ### 4.2. `GraphqlTarget` -```json -{ - "kind": "graphql", - "endpoint": "https://api.example.com/graphql", - "operation_type": "mutation", - "operation_name": "CreateLead", - "query_template": "mutation CreateLead($input: LeadInput!) { createLead(input: $input) { id status } }", - "response_path": "$.response.body.data.createLead" -} -``` - -Поля: - - `kind` - `endpoint` - `operation_type` @@ -206,20 +229,6 @@ ### 4.3. `GrpcTarget` -```json -{ - "kind": "grpc", - "server_addr": "https://grpc.example.com:443", - "package": "crm.v1", - "service": "LeadService", - "method": "CreateLead", - "descriptor_ref": "desc_01hr7yn4d6g1x6vwt7h9n0e7ab", - "descriptor_set_b64": "" -} -``` - -Поля: - - `kind` - `server_addr` - `package` @@ -228,499 +237,19 @@ - `descriptor_ref` - `descriptor_set_b64` -`descriptor_ref` остается ссылкой на загруженный descriptor artifact в storage и registry. - -`descriptor_set_b64` - runtime-ready snapshot descriptor set, который используется unary gRPC adapter для динамического вызова метода без генерации Rust-кода. - ## 5. `Schema` -`Schema` - нормализованное описание входа или выхода. Это не JSON Schema в полном объеме, а внутренняя структурная модель, удобная для UI и runtime. +`Schema` - нормализованное описание входа или выхода. -### Базовая форма +Поддерживаются: -```json -{ - "type": "object", - "description": "Lead input", - "fields": { - "name": { - "type": "string", - "required": true, - "description": "Lead full name" - }, - "tags": { - "type": "array", - "required": false, - "items": { - "type": "string" - } - } - } -} -``` +- скалярные поля; +- вложенные объекты; +- массивы; +- enum; +- nullable-поля; +- `oneof` для protobuf. -### Поддерживаемые типы +## 6. Принцип совместимости -- `object` -- `array` -- `string` -- `integer` -- `number` -- `boolean` -- `enum` -- `null` -- `oneof` - -### Модель поля - -```json -{ - "type": "string", - "required": true, - "nullable": false, - "description": "User email", - "default": null -} -``` - -Тип объекта: - -```json -{ - "type": "object", - "required": true, - "fields": { - "email": { - "type": "string", - "required": true - } - } -} -``` - -Тип массива: - -```json -{ - "type": "array", - "required": false, - "items": { - "type": "object", - "fields": { - "id": { - "type": "string", - "required": true - } - } - } -} -``` - -### Нормализация protobuf-структур - -Для protobuf -> schema bridge дополнительно фиксируются такие правила: - -- `repeated` поле преобразуется в `array`; -- `enum` преобразуется в `type: enum` со списком `enum_values`; -- `map` преобразуется в `array` объектов `{ key, value }`; -- `oneof` преобразуется в `type: oneof`, где каждый вариант представлен объектом с одним допустимым полем. - -## 6. `MappingSet` и `MappingRule` - -`MappingSet` - набор правил преобразования между внутренним MCP input/output и protocol-specific request/response model. - -### `MappingSet` - -```json -{ - "rules": [ - { - "source": "$.mcp.user_id", - "target": "$.request.path.userId" - } - ] -} -``` - -### `MappingRule` - -Поля: - -- `source` - `JSONPath` в исходном контексте. -- `target` - `JSONPath` в целевом контексте. -- `required` - обязательно ли правило для корректного вызова. -- `default_value` - значение по умолчанию. -- `transform` - встроенное преобразование. -- `condition` - условие применения правила. -- `notes` - служебное описание для UI. - -Пример: - -```json -{ - "source": "$.mcp.profile.email", - "target": "$.request.body.contact.email", - "required": true, - "default_value": null, - "transform": { - "kind": "identity" - }, - "condition": null, - "notes": "Map email to CRM contact payload" -} -``` - -### Контексты `source` и `target` - -Для input mapping: - -- `$.mcp.*` -- `$.request.path.*` -- `$.request.query.*` -- `$.request.headers.*` -- `$.request.body.*` -- `$.request.variables.*` -- `$.request.grpc.*` - -Для output mapping: - -- `$.response.body.*` -- `$.response.data.*` -- `$.response.grpc.*` -- `$.output.*` - -Для MVP достаточно управляемого подмножества `JSONPath`: - -- путь всегда начинается с фиксированного root context; -- дальше используются dot-separated поля; -- для массивов допускаются numeric indexes вида `[0]`; -- quoted selectors, filter expressions и произвольные функции не поддерживаются. - -### `Transform` - -Для MVP transformations должны быть ограничены: - -- `identity` -- `to_string` -- `to_number` -- `to_boolean` -- `join` -- `split` -- `wrap_array` -- `unwrap_singleton` - -Пример: - -```json -{ - "kind": "to_string" -} -``` - -### Черновая генерация mapping - -Для MVP generation draft mapping может опираться на простое правило: - -- система сопоставляет уникальные leaf-поля с одинаковыми именами в source sample и target sample; -- неоднозначные совпадения автоматически не связываются; -- результат всегда остается черновиком и требует ручной проверки оператором. - -## 7. `ExecutionConfig` - -`ExecutionConfig` задает параметры выполнения operation. - -```json -{ - "timeout_ms": 10000, - "retry_policy": { - "max_attempts": 1 - }, - "auth_profile_ref": "auth_01hr7x8rj2d8nq8v0c4m4t1r9e", - "headers": { - "X-Client": "crank" - }, - "protocol_options": { - "rest": null, - "graphql": null, - "grpc": { - "use_tls": true - } - } -} -``` - -Поля: - -- `timeout_ms` -- `retry_policy` -- `auth_profile_ref` -- `headers` -- `protocol_options` - -Важно: - -- здесь хранятся execution settings, а не описание бизнес-схемы; -- `protocol_options` должны оставаться узкими и протокол-специфичными; -- секреты не хранятся внутри operation, только ссылки на secret store или auth profile. - -## 8. `AuthProfile` - -`AuthProfile` - переиспользуемая конфигурация аутентификации для внешних вызовов. - -Operation ссылается на auth profile через `auth_profile_ref`, а не хранит секреты внутри себя. - -### Базовая форма - -```json -{ - "id": "auth_01hr7x8rj2d8nq8v0c4m4t1r9e", - "name": "crm-prod-bearer", - "kind": "bearer", - "config": { - "header_name": "Authorization", - "secret_ref": "secret://auth/crm-prod-token" - }, - "created_at": "2026-03-25T08:00:00Z", - "updated_at": "2026-03-25T08:10:00Z" -} -``` - -### Поддерживаемые виды - -- `bearer` -- `basic` -- `api_key_header` -- `api_key_query` - -### MVP-решение по секретам - -Для MVP секреты должны храниться не в open text внутри operation version, а в отдельном secret storage слое. - -Рекомендуемое решение: - -- логическая ссылка в формате `secret://...`; -- реальное значение хранится в приложении либо в зашифрованном хранилище, либо в env-backed secret store; -- в документации и YAML export секреты всегда представляются только через `secret_ref`. - -## 9. `ToolDescription` - -`ToolDescription` задает MCP-представление operation. - -```json -{ - "title": "Get customer profile", - "description": "Returns a customer profile by external customer identifier.", - "tags": ["crm", "customer"], - "examples": [ - { - "input": { - "customer_id": "123" - } - } - ] -} -``` - -Поля: - -- `title` -- `description` -- `tags` -- `examples` - -## 10. `Samples` - -`Samples` связывает operation с загруженными артефактами, на основе которых может быть построен черновик. - -```json -{ - "input_json_sample_ref": "file_01hr7xj7z4k1t0qm0cxsw6f1wx", - "output_json_sample_ref": "file_01hr7xkvt3m1p6ms3m7r2dr8wz", - "proto_file_ref": null, - "descriptor_ref": null -} -``` - -Поля: - -- `input_json_sample_ref` -- `output_json_sample_ref` -- `proto_file_ref` -- `descriptor_ref` - -Примечания: - -- для REST чаще всего используются входной и выходной JSON samples; -- для GraphQL чаще всего полезен sample ответа; -- для gRPC основным artifact остается `.proto` или descriptor set, но input/output JSON samples тоже могут использоваться для MCP-facing модели. - -## 11. `GeneratedDraft` - -`GeneratedDraft` хранит результат автоматической генерации схем и mappings. - -```json -{ - "status": "available", - "source_types": ["input_json_sample", "output_json_sample"], - "generated_at": "2026-03-25T08:05:00Z", - "input_schema_generated": true, - "output_schema_generated": true, - "input_mapping_generated": true, - "output_mapping_generated": true, - "warnings": [ - "Field $.response.body.meta was not mapped automatically" - ] -} -``` - -Поля: - -- `status` - `none`, `available`, `stale`, `failed` -- `source_types` -- `generated_at` -- `input_schema_generated` -- `output_schema_generated` -- `input_mapping_generated` -- `output_mapping_generated` -- `warnings` - -Важно: - -- generated draft - это не runtime-источник истины; -- runtime использует только сохраненные `input_schema`, `output_schema`, `input_mapping`, `output_mapping`; -- generated draft нужен как вспомогательный слой для UI и ускорения конфигурирования. - -## 12. Runtime view - -Для исполнения operation должно существовать упрощенное runtime-представление без UI-специфики. - -```json -{ - "id": "op_01hr7w0m6p8x9z4n7s2k3q5t6u", - "protocol": "rest", - "target": { - "kind": "rest", - "base_url": "https://api.example.com", - "method": "POST", - "path_template": "/v1/leads" - }, - "input_schema": { "type": "object", "fields": {} }, - "output_schema": { "type": "object", "fields": {} }, - "input_mapping": { "rules": [] }, - "output_mapping": { "rules": [] }, - "execution_config": { - "timeout_ms": 10000 - } -} -``` - -Runtime view должно: - -- не содержать UI draft metadata; -- не зависеть от raw uploaded files; -- быть готовым к немедленному исполнению адаптером. - -## 13. YAML-конфигурация - -Для импорта и экспорта система должна поддерживать `YAML`-представление operation. - -Принцип: - -- внутренняя доменная модель одна; -- `JSON` и `YAML` - это два способа сериализации одной и той же конфигурации; -- runtime не зависит от конкретного формата файла; -- `YAML` нужен для переносимости и ручного редактирования. - -### Базовая структура YAML - -```yaml -format_version: "1" -kind: operation -operation: - id: op_01hr7w0m6p8x9z4n7s2k3q5t6u - name: crm_create_lead - display_name: Create Lead - protocol: rest - status: draft - version: 3 - target: - kind: rest - base_url: https://api.example.com - method: POST - path_template: /v1/leads - input_schema: - type: object - fields: - name: - type: string - required: true - email: - type: string - required: true - output_schema: - type: object - fields: - id: - type: string - required: true - status: - type: string - required: true - input_mapping: - rules: - - source: $.mcp.name - target: $.request.body.name - - source: $.mcp.email - target: $.request.body.email - output_mapping: - rules: - - source: $.response.body.id - target: $.output.id - - source: $.response.body.status - target: $.output.status - execution_config: - timeout_ms: 10000 - auth_profile_ref: auth_01hr7x8rj2d8nq8v0c4m4t1r9e - tool_description: - title: Create CRM lead - description: Creates a new lead in CRM by name and email. -``` - -### Требования к YAML import/export - -- формат должен быть детерминированным; -- структура должна быть человекочитаемой; -- импорт должен валидировать схему, mapping и protocol-specific target; -- экспорт не должен включать секреты в открытом виде; -- ссылки на внешние артефакты допустимы, но режим экспорта должен быть явным. - -### Режимы экспорта - -Минимально стоит предусмотреть два режима: - -- `portable` - экспорт только конфигурации operation и ссылок на внешние артефакты; -- `bundle` - экспорт конфигурации operation вместе с вложенными sample metadata и descriptor metadata, если это допустимо. - -Для MVP можно начать только с `portable`. - -## 14. Что важно не допустить - -- одну гигантскую `Operation`, в которой protocol-specific поля лежат вперемешку; -- неявный mapping, который не сохраняется после генерации черновика; -- смешивание uploaded artifacts и runtime-ready configuration; -- хранение секретов внутри operation; -- хранение реальных auth credentials внутри YAML export; -- произвольные пользовательские скрипты в mapping; -- YAML-экспорт, который становится отдельной несовместимой моделью по отношению к доменной структуре. - -## 15. Практический итог - -Эта модель задает основу для: - -- Rust structs в `crank-core`, `crank-schema`, `crank-mapping`; -- DTO для `admin-api`; -- таблиц `operations`, `operation_versions`, `operation_samples`, `operation_descriptors`; -- import/export layer для `YAML` конфигураций; -- runtime view, который будет передаваться в `crank-runtime`. - -Следующим логическим шагом после этого документа должна стать схема БД, в которой эти сущности будут разложены по таблицам и связям. +Если UI требует сущность, которой нет в текущем backend, эта сущность должна быть сначала явно добавлена в эту модель данных, а уже потом в код и БД. diff --git a/docs/database-schema.md b/docs/database-schema.md index 067a4d2..b425bb0 100644 --- a/docs/database-schema.md +++ b/docs/database-schema.md @@ -2,364 +2,288 @@ ## 1. Назначение документа -Этот документ фиксирует структуру хранения конфигураций, версий операций, загруженных артефактов и published runtime-view. Его цель - дать основу для SQL-миграций и для реализации `crank-registry`. - -В документе предполагается реляционная модель, ориентированная на `PostgreSQL`. Канонической считается схема, совместимая с `PostgreSQL`. +Этот документ фиксирует целевую структуру хранения workspace-scoped конфигураций, агентов, ключей доступа и observability-данных. Базовая СУБД - `PostgreSQL`. ## 2. Общие принципы хранения ### 2.1. Версионирование обязательно -Конфигурация operation не должна храниться только в одной "живой" записи. Каждое существенное изменение должно приводить к появлению новой версии конфигурации. - -Для MVP в registry version snapshot хранит protocol-specific конфигурацию, схемы, mapping и execution settings. Поля identity и listing view (`name`, `display_name`, `protocol`) считаются стабильными и хранятся в `operations`. +Конфигурация operation и agent не хранится только в одной "живой" записи. Каждое существенное изменение создает новую версию. ### 2.2. Published и draft разделяются логически - `draft` может меняться; -- `published` должна ссылаться на конкретную зафиксированную версию; -- runtime читает только опубликованные версии. +- `published` всегда указывает на конкретную version; +- runtime читает только опубликованные представления. -### 2.3. Артефакты и конфигурация не смешиваются +### 2.3. Workspace scoping обязателен -`.proto`, descriptor set, sample JSON и YAML import payload не должны храниться в той же структуре, что и runtime-ready configuration. +Все продуктовые таблицы должны ссылаться на `workspaces`. -### 2.4. Секреты не хранятся внутри operation +### 2.4. Артефакты и конфигурация не смешиваются -В БД operation должны храниться только ссылки на auth profiles или secret references. +`.proto`, descriptor set, sample JSON и YAML payload не хранятся в тех же строках, что runtime-ready configuration. -Для MVP рекомендуется отдельная таблица `auth_profiles`, где metadata и secret refs отделены от operation versions. +### 2.5. Секреты не хранятся в открытом виде -### 2.5. Тестовая изоляция - -Integration tests для registry должны выполняться на реальной `PostgreSQL`, но без влияния на runtime-данные. Предпочтительный способ: - -- отдельная test database; -- либо отдельная временная schema на время теста; -- обязательная очистка после завершения тестов. +- upstream secrets живут за `secret_ref`; +- platform API keys хранятся как hash. ## 3. Основные таблицы -Минимальный набор таблиц: - +- `workspaces` +- `users` +- `memberships` +- `invitation_tokens` - `operations` - `operation_versions` - `published_operations` - `operation_samples` - `descriptors` - `auth_profiles` +- `agents` +- `agent_versions` +- `agent_operation_bindings` +- `published_agents` +- `platform_api_keys` +- `invocation_logs` +- `usage_rollups` - `yaml_import_jobs` -Опционально позже: - -- `operation_test_runs` -- `audit_log` - -## 4. Таблица `operations` - -Хранит стабильную сущность операции, не зависящую от конкретной версии. - -### Поля - -- `id` `text primary key` -- `name` `text not null unique` -- `display_name` `text not null` -- `protocol` `text not null` -- `status` `text not null` -- `current_draft_version` `integer not null default 1` -- `latest_published_version` `integer null` -- `created_at` `timestamptz not null` -- `updated_at` `timestamptz not null` -- `published_at` `timestamptz null` - -### Назначение - -- быстрый список операций; -- стабильный идентификатор для UI и MCP; -- привязка к актуальному draft и опубликованной версии. - -## 5. Таблица `operation_versions` - -Хранит полную сериализованную конфигурацию конкретной версии operation. - -### Поля - -- `operation_id` `text not null` -- `version` `integer not null` -- `status` `text not null` -- `target_json` `jsonb not null` -- `input_schema_json` `jsonb not null` -- `output_schema_json` `jsonb not null` -- `input_mapping_json` `jsonb not null` -- `output_mapping_json` `jsonb not null` -- `execution_config_json` `jsonb not null` -- `tool_description_json` `jsonb not null` -- `samples_json` `jsonb null` -- `generated_draft_json` `jsonb null` -- `config_export_json` `jsonb null` -- `change_note` `text null` -- `created_at` `timestamptz not null` -- `created_by` `text null` - -### Ключи - -- primary key: `(operation_id, version)` -- foreign key: `operation_id -> operations(id)` -- рекомендованный composite foreign key для связанных таблиц: `(operation_id, version)` - -### Почему так - -Для MVP выгоднее хранить version snapshot целиком, а не дробить по десятку связанных таблиц. Это: - -- упрощает versioning; -- упрощает откат; -- упрощает YAML export; -- хорошо сочетается с JSON-oriented доменной моделью. - -## 6. Таблица `published_operations` - -Хранит явную published-привязку, которую читает runtime. - -### Поля - -- `operation_id` `text primary key` -- `version` `integer not null` -- `published_at` `timestamptz not null` -- `published_by` `text null` - -### Назначение - -- быстрый доступ к published runtime-view; -- отсутствие двусмысленности, какая именно версия сейчас активна; -- простой invalidation для runtime cache. - -### Рекомендуемая целостность - -- `operation_id -> operations(id)` -- `(operation_id, version) -> operation_versions(operation_id, version)` - -## 7. Таблица `operation_samples` - -Хранит метаданные и ссылки на sample artifacts. - -### Поля - -- `id` `text primary key` -- `operation_id` `text not null` -- `version` `integer not null` -- `sample_kind` `text not null` -- `storage_ref` `text not null` -- `content_type` `text not null` -- `file_name` `text null` -- `created_at` `timestamptz not null` - -### Варианты `sample_kind` - -- `input_json` -- `output_json` -- `yaml_import_source` - -### Назначение - -- не класть большие sample payload в основные version records; -- иметь возможность переиспользовать или пересобирать draft mapping; -- отслеживать, из каких sample-данных строился черновик. - -### Рекомендуемая целостность - -- `operation_id -> operations(id)` -- `(operation_id, version) -> operation_versions(operation_id, version)` - -## 8. Таблица `descriptors` - -Хранит gRPC schema artifacts. - -### Поля - -- `id` `text primary key` -- `operation_id` `text null` -- `version` `integer null` -- `descriptor_kind` `text not null` -- `storage_ref` `text not null` -- `source_name` `text null` -- `package_index_json` `jsonb null` -- `created_at` `timestamptz not null` - -### Варианты `descriptor_kind` - -- `proto_upload` -- `descriptor_set` -- `reflection_snapshot` - -### Назначение - -- связывать gRPC operation с конкретной схемой; -- не хранить binary descriptor внутри основной operation version; -- иметь отдельную точку для discovery metadata. - -### Рекомендуемая целостность - -- если descriptor привязан к version, то `(operation_id, version) -> operation_versions(operation_id, version)` - -## 9. Таблица `yaml_import_jobs` - -Для MVP можно импортировать YAML синхронно, но таблицу под журнал импорта лучше предусмотреть сразу. - -### Поля - -- `id` `text primary key` -- `source_sample_id` `text null` -- `status` `text not null` -- `format_version` `text not null` -- `mode` `text not null` -- `result_operation_id` `text null` -- `result_version` `integer null` -- `error_text` `text null` -- `created_at` `timestamptz not null` -- `finished_at` `timestamptz null` - -### Назначение - -- аудит импортов; -- разбор ошибок валидации; -- поддержка будущего async import pipeline. - -## 10. Таблица `auth_profiles` - -Хранит переиспользуемые профили аутентификации для внешних вызовов. - -### Поля - -- `id` `text primary key` -- `name` `text not null unique` -- `kind` `text not null` -- `config_json` `jsonb not null` -- `created_at` `timestamptz not null` -- `updated_at` `timestamptz not null` - -### Варианты `kind` - -- `bearer` -- `basic` -- `api_key_header` -- `api_key_query` - -### Правило - -`config_json` должен содержать только `secret_ref`, а не открытые секреты. - -## 11. Предлагаемая SQL-форма - -```sql -create table operations ( - id text primary key, - name text not null unique, - display_name text not null, - protocol text not null, - status text not null, - current_draft_version integer not null default 1, - latest_published_version integer null, - created_at timestamptz not null, - updated_at timestamptz not null, - published_at timestamptz null -); - -create table operation_versions ( - operation_id text not null references operations(id), - version integer not null, - status text not null, - target_json jsonb not null, - input_schema_json jsonb not null, - output_schema_json jsonb not null, - input_mapping_json jsonb not null, - output_mapping_json jsonb not null, - execution_config_json jsonb not null, - tool_description_json jsonb not null, - samples_json jsonb null, - generated_draft_json jsonb null, - config_export_json jsonb null, - change_note text null, - created_at timestamptz not null, - created_by text null, - primary key (operation_id, version) -); - -create table published_operations ( - operation_id text primary key references operations(id), - version integer not null, - published_at timestamptz not null, - published_by text null, - foreign key (operation_id, version) - references operation_versions(operation_id, version) -); - -create table auth_profiles ( - id text primary key, - name text not null unique, - kind text not null, - config_json jsonb not null, - created_at timestamptz not null, - updated_at timestamptz not null -); -``` - -## 12. Индексы - -Минимально нужны: - -- index on `operations(protocol)` -- index on `operations(status)` -- index on `operation_versions(operation_id, created_at desc)` -- index on `published_operations(version)` -- index on `operation_samples(operation_id, version)` -- index on `descriptors(operation_id, version)` -- index on `auth_profiles(kind)` - -## 13. Versioning flow - -### Создание операции - -1. Создается запись в `operations`. -2. Создается версия `1` в `operation_versions`. -3. `current_draft_version = 1`. - -### Изменение draft - -1. Читается текущий draft. -2. Создается новая версия `n + 1`. -3. В `operations.current_draft_version` пишется новая версия. -4. Published версия не меняется. - -### Публикация - -1. Берется текущий draft version. -2. В `published_operations` upsert-ится ссылка на эту версию. -3. В `operations.latest_published_version` пишется та же версия. -4. Runtime cache получает сигнал на reload. - -### Импорт YAML - -1. YAML валидируется. -2. Определяется create или update сценарий. -3. Создается новая запись в `operation_versions`. -4. При необходимости создается запись в `yaml_import_jobs`. - -## 14. Что не должно храниться в БД в таком виде - -- секреты в открытом виде; -- runtime cache; -- скомпилированные adapter clients; -- невалидированные черновики, не приводимые к доменной модели. - -## 15. Практический итог - -Для MVP рекомендован такой подход: - -- `operations` - стабильная идентичность; -- `operation_versions` - полные version snapshots; -- `published_operations` - текущая активная версия; -- `operation_samples` и `descriptors` - внешние артефакты; -- `auth_profiles` - переиспользуемая внешняя аутентификация; -- `yaml_import_jobs` - журнал импортов. - -Эта схема хорошо ложится на `sqlx`, не требует избыточной нормализации и соответствует JSON-oriented модели домена. +## 4. Operations + +### `operations` + +- `id` +- `workspace_id` +- `name` +- `display_name` +- `protocol` +- `status` +- `current_draft_version` +- `latest_published_version` +- `created_at` +- `updated_at` +- `published_at` + +Ограничение: + +- `unique (workspace_id, name)` + +### `operation_versions` + +- `operation_id` +- `version` +- `status` +- `target_json` +- `input_schema_json` +- `output_schema_json` +- `input_mapping_json` +- `output_mapping_json` +- `execution_config_json` +- `tool_description_json` +- `samples_json` +- `generated_draft_json` +- `config_export_json` +- `change_note` +- `created_at` +- `created_by` + +### `published_operations` + +- `operation_id` +- `version` +- `published_at` +- `published_by` + +## 5. Operation artifacts + +### `operation_samples` + +- `id` +- `operation_id` +- `version` +- `sample_kind` +- `storage_ref` +- `content_type` +- `file_name` +- `created_at` + +### `descriptors` + +- `id` +- `operation_id` +- `version` +- `descriptor_kind` +- `storage_ref` +- `source_name` +- `package_index_json` +- `created_at` + +### `yaml_import_jobs` + +- `id` +- `source_sample_id` +- `status` +- `format_version` +- `mode` +- `result_operation_id` +- `result_version` +- `error_text` +- `created_at` +- `finished_at` + +## 6. Upstream auth + +### `auth_profiles` + +- `id` +- `workspace_id` +- `name` +- `kind` +- `config_json` +- `created_at` +- `updated_at` + +Ограничение: + +- `unique (workspace_id, name)` + +## 7. Workspaces and access layer + +### `workspaces` + +- `id` +- `slug` +- `display_name` +- `status` +- `settings_json` +- `created_at` +- `updated_at` + +### `users` + +- `id` +- `email` +- `display_name` +- `status` +- `created_at` + +### `memberships` + +- `workspace_id` +- `user_id` +- `role` +- `created_at` + +### `invitation_tokens` + +- `id` +- `workspace_id` +- `email` +- `role` +- `status` +- `token_hash` +- `expires_at` +- `created_at` + +## 8. Agents + +### `agents` + +- `id` +- `workspace_id` +- `slug` +- `display_name` +- `description` +- `status` +- `current_draft_version` +- `latest_published_version` +- `created_at` +- `updated_at` +- `published_at` + +Ограничение: + +- `unique (workspace_id, slug)` + +### `agent_versions` + +- `agent_id` +- `version` +- `status` +- `instructions_json` +- `tool_selection_policy_json` +- `created_at` + +### `agent_operation_bindings` + +- `agent_id` +- `agent_version` +- `operation_id` +- `operation_version` +- `tool_name` +- `tool_title` +- `tool_description_override` +- `enabled` + +### `published_agents` + +- `agent_id` +- `version` +- `published_at` +- `published_by` + +## 9. Platform access and observability + +### `platform_api_keys` + +- `id` +- `workspace_id` +- `name` +- `prefix` +- `secret_hash` +- `scopes_json` +- `status` +- `created_at` +- `last_used_at` + +### `invocation_logs` + +- `id` +- `workspace_id` +- `agent_id` +- `operation_id` +- `request_id` +- `level` +- `status` +- `duration_ms` +- `error_kind` +- `request_preview_json` +- `response_preview_json` +- `created_at` + +### `usage_rollups` + +- `workspace_id` +- `agent_id` +- `operation_id` +- `period_kind` +- `period_start` +- `calls_total` +- `calls_ok` +- `calls_error` +- `p50_ms` +- `p95_ms` +- `p99_ms` + +## 10. Migration strategy + +Переход от текущей схемы к целевой идет так: + +1. добавить `workspaces` и заполнить default workspace; +2. добавить `workspace_id` в `operations` и `auth_profiles`; +3. добавить `agents` и `published_agents`; +4. внедрить `platform_api_keys`; +5. добавить `invocation_logs` и `usage_rollups`; +6. перевести MCP runtime на `published_agents`, а не на глобальный список operations. diff --git a/docs/diagrams.md b/docs/diagrams.md index 45001a4..f74c114 100644 --- a/docs/diagrams.md +++ b/docs/diagrams.md @@ -2,14 +2,11 @@ ## 1. Назначение документа -Этот документ собирает диаграммы, которые фиксируют проект до начала разработки: +Этот документ собирает диаграммы целевой модели проекта: - компонентную структуру; - связи между доменными сущностями; -- хранение данных в БД; -- основные runtime и admin-потоки. - -Диаграммы даны в формате `Mermaid`, чтобы их можно было хранить прямо в репозитории и рендерить в Markdown-compatible tooling. +- хранение данных в БД. ## 2. Компонентная диаграмма @@ -29,6 +26,7 @@ flowchart LR GRPC[adapter-grpc] DB[(PostgreSQL)] STORE[(Artifact Storage)] + OBS[(Usage and Logs)] UI --> ADMIN MCP --> REG @@ -36,352 +34,118 @@ flowchart LR ADMIN --> REG ADMIN --> RUN ADMIN --> PROTO - REG --> DB REG --> CORE REG --> SCHEMA REG --> MAP - RUN --> CORE RUN --> SCHEMA RUN --> MAP RUN --> REST RUN --> GQL RUN --> GRPC - GRPC --> PROTO PROTO --> STORE ADMIN --> STORE + REG --> OBS + ADMIN --> OBS ``` -## 3. Диаграмма зависимостей crates - -```mermaid -flowchart TD - CORE[crank-core] - SCHEMA[crank-schema] - MAP[crank-mapping] - PROTO[crank-proto] - REG[crank-registry] - RUN[crank-runtime] - REST[crank-adapter-rest] - GQL[crank-adapter-graphql] - GRPC[crank-adapter-grpc] - ADMIN[apps/admin-api] - MCP[apps/mcp-server] - - SCHEMA --> CORE - MAP --> CORE - PROTO --> CORE - PROTO --> SCHEMA - REG --> CORE - REG --> SCHEMA - REG --> MAP - REST --> CORE - GQL --> CORE - GRPC --> CORE - GRPC --> PROTO - RUN --> CORE - RUN --> SCHEMA - RUN --> MAP - RUN --> REST - RUN --> GQL - RUN --> GRPC - ADMIN --> CORE - ADMIN --> SCHEMA - ADMIN --> MAP - ADMIN --> PROTO - ADMIN --> REG - ADMIN --> RUN - MCP --> CORE - MCP --> REG - MCP --> RUN -``` - -## 4. Структурная диаграмма доменной модели +## 3. Структурная диаграмма доменной модели ```mermaid classDiagram + class Workspace { + +id + +slug + +display_name + } + class Operation { +id + +workspace_id +name +display_name +protocol +status - +version - +target - +input_schema - +output_schema - +input_mapping - +output_mapping - +execution_config - +tool_description - +samples - +generated_draft - +config_export } - class RestTarget { - +base_url - +method - +path_template - +static_headers - } - - class GraphqlTarget { - +endpoint - +operation_type - +operation_name - +query_template - +response_path - } - - class GrpcTarget { - +server_addr - +package - +service - +method - +descriptor_ref - +descriptor_set_b64 - } - - class Schema { - +type - +description - +fields - } - - class MappingSet { - +rules[] - } - - class MappingRule { - +source - +target - +required - +default_value - +transform - +condition - } - - class ExecutionConfig { - +timeout_ms - +retry_policy - +auth_profile_ref - +headers - +protocol_options - } - - class AuthProfile { + class Agent { +id - +name - +kind - +config - } - - class ToolDescription { - +title - +description - +tags - +examples - } - - class Samples { - +input_json_sample_ref - +output_json_sample_ref - +proto_file_ref - +descriptor_ref - } - - class GeneratedDraft { + +workspace_id + +slug + +display_name +status - +source_types - +generated_at - +warnings } - Operation --> RestTarget : target - Operation --> GraphqlTarget : target - Operation --> GrpcTarget : target - Operation --> Schema : input_schema - Operation --> Schema : output_schema - Operation --> MappingSet : input_mapping - Operation --> MappingSet : output_mapping - Operation --> ExecutionConfig : execution_config - Operation --> ToolDescription : tool_description - Operation --> Samples : samples - Operation --> GeneratedDraft : generated_draft - ExecutionConfig --> AuthProfile : auth_profile_ref - MappingSet --> MappingRule : contains + class AgentBinding { + +operation_id + +operation_version + +tool_name + +enabled + } + + class PlatformApiKey { + +id + +workspace_id + +name + +prefix + +scopes + +status + } + + class InvocationLog { + +workspace_id + +agent_id + +operation_id + +status + +duration_ms + } + + Workspace --> Operation : owns + Workspace --> Agent : owns + Workspace --> PlatformApiKey : owns + Agent --> AgentBinding : contains + AgentBinding --> Operation : references + InvocationLog --> Workspace : belongs_to + InvocationLog --> Agent : belongs_to + InvocationLog --> Operation : belongs_to ``` -## 5. ER-диаграмма БД +## 4. ER-диаграмма БД ```mermaid erDiagram + WORKSPACES ||--o{ OPERATIONS : owns + WORKSPACES ||--o{ AGENTS : owns + WORKSPACES ||--o{ AUTH_PROFILES : owns + WORKSPACES ||--o{ PLATFORM_API_KEYS : owns + WORKSPACES ||--o{ INVOCATION_LOGS : owns OPERATIONS ||--o{ OPERATION_VERSIONS : has OPERATIONS ||--o| PUBLISHED_OPERATIONS : publishes - OPERATIONS ||--o{ OPERATION_SAMPLES : owns - OPERATIONS ||--o{ DESCRIPTORS : may_use - OPERATIONS ||--o{ YAML_IMPORT_JOBS : may_create - AUTH_PROFILES ||--o{ OPERATION_VERSIONS : referenced_by + AGENTS ||--o{ AGENT_VERSIONS : has + AGENTS ||--o| PUBLISHED_AGENTS : publishes + AGENT_VERSIONS ||--o{ AGENT_OPERATION_BINDINGS : contains + OPERATIONS ||--o{ AGENT_OPERATION_BINDINGS : exposed_by + WORKSPACES { + text id PK + text slug + text display_name + } OPERATIONS { text id PK + text workspace_id FK text name text display_name text protocol text status - int current_draft_version - int latest_published_version - timestamptz created_at - timestamptz updated_at - timestamptz published_at } - - OPERATION_VERSIONS { - text operation_id FK - int version + AGENTS { + text id PK + text workspace_id FK + text slug + text display_name text status - jsonb target_json - jsonb input_schema_json - jsonb output_schema_json - jsonb input_mapping_json - jsonb output_mapping_json - jsonb execution_config_json - jsonb tool_description_json - jsonb samples_json - jsonb generated_draft_json - jsonb config_export_json - timestamptz created_at - } - - PUBLISHED_OPERATIONS { - text operation_id PK - int version - timestamptz published_at - text published_by - } - - OPERATION_SAMPLES { - text id PK - text operation_id FK - int version - text sample_kind - text storage_ref - text content_type - text file_name - timestamptz created_at - } - - DESCRIPTORS { - text id PK - text operation_id FK - int version - text descriptor_kind - text storage_ref - jsonb package_index_json - timestamptz created_at - } - - YAML_IMPORT_JOBS { - text id PK - text source_sample_id - text status - text format_version - text mode - text result_operation_id - int result_version - text error_text - timestamptz created_at - timestamptz finished_at - } - - AUTH_PROFILES { - text id PK - text name - text kind - jsonb config_json - timestamptz created_at - timestamptz updated_at } ``` - -## 6. Sequence: создание и публикация operation - -```mermaid -sequenceDiagram - participant UI - participant API as admin-api - participant REG as registry - participant RUN as runtime - participant MCP as mcp-server - - UI->>API: POST /operations - API->>REG: create operation v1 - REG-->>API: created - API-->>UI: operation_id, version - - UI->>API: upload samples / descriptors - API-->>UI: artifact refs - - UI->>API: POST /drafts/generate - API->>REG: save generated draft metadata - API-->>UI: generated draft - - UI->>API: POST /test-runs - API->>RUN: execute draft version - RUN-->>API: request_preview + response_preview - API-->>UI: test result - - UI->>API: POST /publish - API->>REG: mark version as published - REG-->>API: published - API-->>MCP: reload signal - API-->>UI: published_version -``` - -## 7. Sequence: MCP tool execution - -```mermaid -sequenceDiagram - participant Client as MCP Client - participant MCP as mcp-server - participant REG as registry/cache - participant RUN as runtime - participant ADP as protocol adapter - - Client->>MCP: call tool(name, input) - MCP->>REG: resolve published runtime view - REG-->>MCP: runtime operation - MCP->>RUN: execute(operation, input) - RUN->>RUN: validate input schema - RUN->>RUN: apply input mapping - RUN->>ADP: execute prepared request - ADP-->>RUN: normalized response - RUN->>RUN: apply output mapping - RUN-->>MCP: output - MCP-->>Client: tool result -``` - -## 8. Sequence: YAML import - -```mermaid -sequenceDiagram - participant UI - participant API as admin-api - participant REG as registry - - UI->>API: POST /operations/import (YAML) - API->>API: parse YAML - API->>API: validate schema, target, mapping - API->>REG: create or upsert new version - REG-->>API: operation_id, version - API-->>UI: import result -``` - -## 9. Что важно помнить - -- Диаграммы фиксируют целевую архитектуру, а не точную реализацию каждого файла. -- Если меняется модель данных или поток исполнения, сначала нужно обновлять документы, потом код. -- Для старта разработки этого набора достаточно: компоненты, сущности, БД и ключевые sequence flows уже описаны. diff --git a/docs/implementation-plan.md b/docs/implementation-plan.md index e965ebf..4234251 100644 --- a/docs/implementation-plan.md +++ b/docs/implementation-plan.md @@ -2,425 +2,121 @@ ## 1. Назначение документа -Этот документ фиксирует порядок реализации модулей и фич. Он нужен затем, чтобы разработка шла последовательно, а не параллельно во все стороны сразу. +Этот документ фиксирует порядок перехода от текущего состояния проекта к целевой модели, заданной `test-ui`. Принцип: -- сначала фундамент; -- потом минимальный end-to-end сценарий; -- потом расширение протоколов; +- сначала перепроектирование `as is -> to be`; +- потом foundation под workspace/agent model; +- потом возврат к end-to-end UI сценариям; +- потом observability и access layer; - потом polish и demo readiness. -## 2. Этап 0. Scaffold проекта +## 2. Этап 1. Перепроектирование `As Is -> To Be` Цель: -- создать `cargo workspace`; -- создать приложения и crates; -- подключить базовый CI/test workflow; -- зафиксировать структуру каталогов. - -Состав: - -- `apps/admin-api` -- `apps/mcp-server` -- `apps/ui` -- `crates/crank-core` -- `crates/crank-schema` -- `crates/crank-mapping` -- `crates/crank-proto` -- `crates/crank-registry` -- `crates/crank-runtime` -- `crates/crank-adapter-rest` -- `crates/crank-adapter-graphql` -- `crates/crank-adapter-grpc` - -Результат: - -- проект собирается; -- тестовый pipeline запускается; -- есть пустые crate boundaries. +- зафиксировать новую доменную модель и page-driven backend contract. DoD: -- создан `cargo workspace`; -- все crates и apps объявлены в workspace; -- проект собирается без бизнес-логики; -- базовые test targets запускаются; -- сделан атомарный commit со scaffold. +- зафиксирован `as is -> to be` план; +- page-by-page gap analysis покрывает все целевые экраны; +- разобраны все архитектурные конфликты UI vs current backend; +- документы `architecture`, `data-model`, `database-schema`, `admin-api`, `mcp-interface` синхронизированы. -## 3. Этап 1. Базовая доменная модель +## 3. Этап 2. Workspace foundation Цель: -- реализовать типы из `data-model`. - -Фичи: - -- `Operation` -- `Target` -- `Schema` -- `MappingSet` -- `ExecutionConfig` -- `ToolDescription` -- `AuthProfile` - -Параллельно: - -- unit tests на доменные типы; -- базовая сериализация `JSON`/`YAML`. - -Результат: - -- модель данных существует как код; -- нет инфраструктурных зависимостей внутри домена. +- перевести хранение и API на workspace-scoped модель. DoD: -- типы из `data-model` реализованы; -- базовая сериализация `JSON` и `YAML` проходит тесты; -- доменные `impl` не содержат инфраструктурной логики; -- unit tests на ключевые типы проходят; -- изменения зафиксированы через один или несколько `RGR + commit`. +- операции и auth profiles принадлежат workspace; +- registry умеет фильтровать данные по workspace; +- есть default workspace migration path. -## 4. Этап 2. Schema engine +## 4. Этап 3. Agent publishing foundation Цель: -- реализовать `crank-schema`. - -Фичи: - -- model полей и типов; -- schema validation; -- field traversal; -- нормализация JSON samples; -- protobuf -> schema bridge contracts. - -Результат: - -- можно описывать и валидировать вход/выход. +- ввести `Agent` и agent-scoped MCP publishing. DoD: -- реализована схема полей и типов; -- работает schema validation; -- JSON sample normalization покрыт тестами; -- контракты protobuf -> schema зафиксированы; -- нет смешивания schema logic с adapter logic. +- можно создать agent и привязать к нему published operations; +- `mcp-server` выдает tools в контексте конкретного agent; +- один agent видит только свой curated toolset. -## 5. Этап 3. Mapping engine +## 5. Этап 4. Operations and wizard integration Цель: -- реализовать `crank-mapping`. - -Фичи: - -- `JSONPath` parsing и validation; -- input mapping; -- output mapping; -- transforms; -- generation draft mapping из samples. - -Результат: - -- можно преобразовывать MCP input в request model и response в output model. +- посадить operations catalog и wizard на реальные backend contracts. DoD: -- `JSONPath` parsing и validation работают; -- input/output mapping проходят unit tests; -- generation draft mapping покрыта фикстурами; -- transforms ограничены и задокументированы; -- mapping engine не знает о конкретных protocol adapters. +- каталог операций и wizard работают без `localStorage` overrides; +- operation edit/delete/publish/test выполняются через backend; +- все протоколы работают в рамках одного UI flow. -## 6. Этап 4. Registry и БД +## 6. Этап 5. Agents UI and backend Цель: -- реализовать `crank-registry` и миграции. - -Фичи: - -- таблицы из `database-schema`; -- version snapshots; -- published operations; -- auth profiles; -- sample metadata; -- descriptor metadata; -- YAML import job log. - -Результат: - -- конфигурации можно хранить и версионировать. +- реализовать agent-centric слой. DoD: -- миграции создают таблицы из `database-schema`; -- version snapshots работают корректно; -- publish linkage реализован; -- auth profiles и artifact metadata сохраняются; -- integration tests на registry проходят на реальной БД. +- agent CRUD работает; +- binding operations к agent работает; +- published agent появляется в MCP runtime. -## 7. Этап 5. REST vertical slice +## 7. Этап 6. Platform access Цель: -- получить первый рабочий end-to-end сценарий. - -Фичи: - -- `crank-adapter-rest` -- `crank-runtime` для REST -- REST test run -- создание REST operation -- publish REST operation -- вызов published REST tool из MCP слоя - -Результат: - -- MVP работает хотя бы для REST. +- реализовать workspace access и platform API keys. DoD: -- REST operation можно создать, протестировать и опубликовать; -- runtime исполняет REST operation end-to-end; -- published REST tool вызывается через MCP слой; -- negative tests на mapping и external errors существуют; -- есть демонстрационный REST сценарий. +- UI screens `API Keys`, `Settings`, `Workspace` имеют backend-контракт; +- platform API keys не смешиваются с upstream auth profiles; +- tenant boundary выражен в access layer. -## 8. Этап 6. Admin API v1 +## 8. Этап 7. Observability Цель: -- дать UI полный backend-контракт для базового сценария. - -Фичи: - -- CRUD operations; -- create version; -- publish; -- upload input/output JSON samples; -- generate draft; -- test run; -- auth profiles CRUD; -- YAML import/export. - -Результат: - -- UI может полностью управлять REST operation без ручных правок кода. +- реализовать логи и usage. DoD: -- доступны CRUD, versioning, publish, samples, draft generation, test runs; -- доступны auth profiles и YAML import/export; -- API контракты соответствуют документации; -- integration tests на ключевые endpoints проходят; -- нет скрытой бизнес-логики в handlers. +- `Logs` page и `Usage` page работают на реальных данных; +- есть продуктовые endpoints, а не только application logs; +- rollups и detail views согласованы с UI. -## 9. Этап 7. UI v1 +## 9. Этап 8. Alpine UI integration Цель: -- собрать рабочую административную консоль. - -Фичи: - -- список операций; -- мастер создания операции; -- sample upload; -- schema viewer; -- mapping editor; -- test run screen; -- publish flow; -- YAML import/export screen. - -Результат: - -- есть демонстрируемый пользовательский интерфейс. +- перенести `test-ui` в `apps/ui` и подключить его к реальному backend. DoD: -- UI покрывает основной сценарий от создания operation до publish; -- sample upload и mapping editor работают; -- YAML import/export доступен из UI; -- нет блокирующих заглушек на критическом пути демо; -- основные пользовательские сценарии проверены вручную или integration tests. +- `apps/ui` содержит целевой Alpine.js UI; +- mock JSON больше не используется на критическом пути; +- UI, backend и docs синхронизированы. -## 10. Этап 8. MCP server +## 10. Этап 9. Hardening and demo readiness Цель: -- публиковать published operations как MCP tools. - -Фичи: - -- `Streamable HTTP`; -- list tools; -- call tool; -- reload published tools; -- error mapping MCP layer. - -Результат: - -- REST operation доступна как полноценный MCP tool. +- довести продукт до стабильного демо-сценария. DoD: -- `Streamable HTTP` transport работает; -- list tools и call tool реализованы; -- reload published tools работает без перезапуска; -- ошибки runtime корректно транслируются в MCP слой; -- есть end-to-end test или demo flow вызова published REST tool. - -## 11. Этап 9. GraphQL support - -Цель: - -- добавить второй протокол без разрушения архитектуры. - -Фичи: - -- `crank-adapter-graphql` -- GraphQL target support; -- variables mapping; -- `response_path`; -- GraphQL test runs; -- publish и вызов через MCP. - -Результат: - -- второй end-to-end сценарий. - -DoD: - -- GraphQL operation можно создать, протестировать и опубликовать; -- variables mapping и `response_path` работают; -- GraphQL `errors` корректно обрабатываются; -- published GraphQL tool вызывается через MCP слой; -- есть demo fixture или integration scenario. - -## 12. Этап 10. gRPC support - -Цель: - -- добавить unary gRPC. - -Фичи: - -- `crank-proto` -- descriptor loading; -- service/method discovery; -- protobuf normalization; -- `crank-adapter-grpc` -- unary test runs; -- publish и вызов через MCP. - -Результат: - -- третий end-to-end сценарий. - -DoD: - -- `.proto` или descriptor set можно загрузить; -- unary method discovery работает; -- JSON <-> protobuf conversion покрыта тестами; -- gRPC operation можно протестировать и опубликовать; -- published gRPC tool вызывается через MCP слой. - -## 13. Этап 11. Harden и demo readiness - -Цель: - -- довести проект до стабильного demo state. - -Фичи: - -- логирование и tracing; -- улучшение ошибок; -- фикстуры и demo scenarios; -- polish UI; -- документация по запуску; -- проверка YAML roundtrip; -- проверка publish/reload flow. - -DoD: - -- демонстрационные сценарии воспроизводимы; -- логирование и ошибки читаемы; -- YAML roundtrip проверен; -- publish/reload flow стабилен; -- документация по запуску достаточна для повторения демо. - -## 14. Этап 12. Deployment и CD - -Цель: - -- сделать повторяемый production-like запуск проекта. - -Фичи: - -- `Dockerfile` для приложений; -- `docker-compose.yml`; -- `.env.example`; -- reverse proxy examples; -- health endpoints; -- CD workflow для `main`. - -Результат: - -- проект можно развернуть на Linux-хосте без ручной сборки бинарей и без ad-hoc shell-скриптов. - -DoD: - -- backend приложения собираются в контейнеры; -- есть production-like compose конфигурация; -- reverse proxy examples задокументированы; -- CI и CD разделены; -- deployment проверяется healthchecks. - -## 15. Приоритеты по реализации - -Если времени не хватает, сохраняется такой приоритет: - -1. REST end-to-end -2. Registry + versioning -3. YAML import/export -4. MCP server -5. GraphQL -6. gRPC - -Причина: - -- диплом должен показать работающую платформу; -- лучше один полный вертикальный сценарий, чем три недоделанных адаптера. - -## 16. Разбиение по фичам - -Каждый этап желательно бить на маленькие фичи: - -- `schema-field-model` -- `schema-validator` -- `jsonpath-parser` -- `input-mapping-engine` -- `output-mapping-engine` -- `registry-create-version` -- `registry-publish` -- `rest-adapter-post-json` -- `yaml-export-portable` -- `mcp-list-tools` - -Для каждой такой фичи локальный `DoD` должен быть записан до начала реализации хотя бы в рабочем описании задачи или ветки. - -## 17. Практический итог - -Правильная последовательность для проекта: - -- сначала домен и фундамент; -- потом registry; -- потом один полный REST vertical slice; -- потом admin-ui и MCP слой; -- только после этого расширение на GraphQL и gRPC. - -Такой порядок минимизирует архитектурный риск и дает ранний рабочий результат. +- end-to-end demo воспроизводим; +- deployment и healthchecks стабильно зелёные; +- документация и продуктовый сценарий совпадают. diff --git a/docs/mcp-interface.md b/docs/mcp-interface.md index be33f93..df5a872 100644 --- a/docs/mcp-interface.md +++ b/docs/mcp-interface.md @@ -2,40 +2,34 @@ ## 1. Назначение документа -Этот документ фиксирует, как именно платформа публикует operations в виде MCP tools и какой transport используется в MVP. - -Главная цель - убрать неопределенность вокруг вопроса "каким именно будет MCP server" до начала реализации. +Этот документ фиксирует, как именно платформа публикует agents и operations в виде MCP tools и какой transport используется в целевой модели. ## 2. Архитектурное решение -Для MVP `mcp-server` должен публиковать tools через network-oriented MCP transport. +`mcp-server` публикует tools через network-oriented MCP transport. -Рекомендуемое решение: +Решение: - основной transport: `Streamable HTTP`; - отдельный `mcp-server` как сервис; -- `stdio` не является обязательной частью MVP. - -Причина: - -- проект задуман как `Crank`, а не как локальный single-process adapter; -- нужен удаленный доступ к опубликованным tools; -- published tools должны обновляться без пересборки и без локального обертывания каждого клиента. +- `stdio` не является обязательной частью текущего scope. ## 3. Модель публикации tools -Каждая published operation превращается в один MCP tool. +Каждая published operation превращается в один MCP tool внутри конкретного published agent. Соответствие: -- одна published version; -- один tool name; +- один published agent; +- набор `AgentOperationBinding`; +- один tool name на binding; - одна input schema; - один результат. Публикация tool основана на: -- `operation.name` +- `agent.slug` +- `operation.name` или binding-level `tool_name` - `tool_description` - `input_schema` - `published runtime view` @@ -44,7 +38,7 @@ `mcp-server` должен: -- загрузить published operations из registry; +- загрузить published agents и их bindings из registry; - преобразовать их в MCP tool definitions; - принимать вызовы tools от MCP clients; - валидировать вход; @@ -62,12 +56,12 @@ - заниматься protobuf discovery; - содержать бизнес-логику admin UI. -## 6. Published runtime view +## 6. Runtime view -`mcp-server` должен работать не с полной admin-конфигурацией, а с runtime-ready view. - -В published runtime view остаются: +В runtime view остаются: +- `workspace_id` +- `agent_id` - `operation_id` - `protocol` - `target` @@ -78,29 +72,30 @@ - `execution_config` - `tool_description` -В published runtime view не должны попадать: +В runtime view не попадают: - raw uploaded samples; - generated draft metadata; - YAML import metadata; - UI-specific helper fields. -## 7. Transport для MVP +## 7. MCP endpoint model -### Поддерживается +Канонический endpoint: -- `Streamable HTTP` +```text +/mcp/v1/{workspace_slug}/{agent_slug} +``` -### Не обязательно в MVP +Этот endpoint определяет: -- `stdio` -- дополнительные transport adapters - -Если позже понадобится локальная интеграция, `stdio` можно добавить как отдельный transport layer поверх того же runtime. +- tenant boundary; +- конкретный curated toolset; +- набор usage и log labels. ## 8. MCP lifecycle -MVP-контракт `mcp-server` строится вокруг JSON-RPC методов MCP: +Поддерживаемые JSON-RPC методы: - `initialize` - `notifications/initialized` @@ -108,37 +103,32 @@ MVP-контракт `mcp-server` строится вокруг JSON-RPC мет - `tools/list` - `tools/call` -Сессия создается на `initialize` и идентифицируется через `MCP-Session-Id`. -Согласованная версия протокола возвращается и читается через `MCP-Protocol-Version`. - -Пока сессии хранятся in-memory внутри `mcp-server`, чего достаточно для MVP и demo-сценариев. - ### Tool listing -После `initialize` и `notifications/initialized`: - 1. клиент вызывает `tools/list`; -2. `mcp-server` перечитывает published operations по refresh policy; -3. строит или обновляет in-memory catalog tools; -4. отдает список tools через MCP JSON-RPC result. +2. `mcp-server` извлекает `workspace_slug` и `agent_slug` из path; +3. перечитывает published agent по refresh policy; +4. строит или обновляет in-memory catalog tools только для этого agent; +5. отдает список tools через MCP JSON-RPC result. ### Tool call -1. MCP client вызывает tool. -2. `mcp-server` находит published runtime view. -3. Валидирует input относительно schema. -4. Делегирует вызов в `crank-runtime`. -5. Возвращает результат. +1. клиент вызывает tool; +2. `mcp-server` определяет `workspace` и `agent`; +3. находит binding нужной operation внутри published agent; +4. валидирует input относительно schema; +5. делегирует вызов в `crank-runtime`; +6. возвращает результат. ## 9. Обновление tools -После публикации новой версии: +После публикации новой operation version или agent version: -1. `admin-api` фиксирует published version в registry. -2. `registry` обновляет published_operations. -3. `mcp-server` не требует restart и не опирается на ручной reload signal. -4. `mcp-server` выполняет controlled refresh опубликованного каталога по interval-based policy. -5. Новый tool contract становится доступен MCP clients. +1. `admin-api` фиксирует published version в registry; +2. `registry` обновляет published operations или published agents; +3. `mcp-server` не требует restart; +4. выполняется controlled refresh опубликованного каталога; +5. новый tool contract становится доступен MCP clients. ## 10. Именование tools @@ -150,9 +140,9 @@ MVP-контракт `mcp-server` строится вокруг JSON-RPC мет Требования: -- имя уникально в пределах платформы; -- имя не зависит от внутреннего numeric version; -- rename operation должен считаться отдельным осознанным изменением. +- имя уникально в пределах одного agent; +- имя не зависит от numeric version; +- один и тот же operation может публиковаться под разными именами в разных agents. ## 11. Ошибки MCP слоя @@ -164,14 +154,11 @@ MVP-контракт `mcp-server` строится вокруг JSON-RPC мет - external service error; - internal runtime error. -`mcp-server` не должен терять стадию ошибки при трансляции ответа клиенту. - ## 12. Практический итог -Для MVP достаточно следующей фиксации: - - `mcp-server` - отдельный сервис; - transport - `Streamable HTTP`; -- одна published operation = один MCP tool; +- endpoint определяется парой `workspace + agent`; +- одна published operation = один MCP tool внутри agent; - reload published tools без пересборки сервиса; - никакой draft-логики или admin CRUD в MCP слое. diff --git a/docs/module-decomposition.md b/docs/module-decomposition.md index c6b9882..d998f73 100644 --- a/docs/module-decomposition.md +++ b/docs/module-decomposition.md @@ -2,44 +2,22 @@ ## 1. Цель документа -Этот документ фиксирует детальную структуру проекта до начала активной разработки. Его задача - заранее ограничить ответственность каждого компонента, избежать разрастания `crank-core`, не допустить появления "универсальных" структур на все случаи жизни и сохранить понятные границы между доменной логикой, runtime, адаптерами, API и UI. +Этот документ фиксирует детальную структуру проекта под целевую модель `workspace -> agent -> operations`. -Основной принцип: каждый crate отвечает за один слой системы. Внутри crate модули должны быть маленькими, тематическими и с минимальным количеством публичных сущностей. +Основной принцип: каждый crate отвечает за один слой системы. Внутри crate модули маленькие, тематические и с минимальным количеством публичных сущностей. ## 2. Общие архитектурные правила -### 2.1. Что считается правильной декомпозицией - - `core` содержит только базовую доменную модель, идентификаторы, типы ошибок и общие контракты. -- `registry` отвечает только за хранение и загрузку конфигурации операций. +- `registry` отвечает только за хранение и загрузку workspace-scoped конфигурации. - `runtime` исполняет операции, но не знает о способе их хранения. - адаптеры знают только свой протокол и общий контракт runtime. - `admin-api` оркестрирует use case для UI, но не содержит протокольной логики. -- `mcp-server` публикует tools и вызывает runtime, но не содержит бизнес-логики конфигурирования. -- `ui` не знает внутреннюю реализацию runtime и работает только через HTTP API. - -### 2.2. Что запрещено - -- помещать SQL, HTTP-клиенты или gRPC-клиенты в `crank-core`; -- хранить в `core` "общие утилиты", не относящиеся к доменной модели; -- делать `runtime`, который напрямую читает БД; -- писать mapping-логику внутри REST, GraphQL или gRPC адаптеров; -- дублировать доменные типы в `admin-api`, `mcp-server` и адаптерах; -- создавать большие структуры вида `AppState`, в которые складывается все подряд; -- создавать большие enum или config-объекты, содержащие поля всех протоколов одновременно без выделенных вложенных типов. - -### 2.3. Предпочтительный стиль - -- узкие интерфейсы; -- маленькие DTO; -- отдельные типы для draft, published и runtime-view сущностей; -- отдельные модули для чтения, записи, валидации и исполнения; -- композиция из небольших сервисов вместо одного глобального сервиса. +- `mcp-server` публикует agent-scoped tools и вызывает runtime. +- `ui` работает только через HTTP API. ## 3. Workspace-структура -Рекомендуемая структура: - ```text crank/ apps/ @@ -58,7 +36,11 @@ crank/ crank-proto/ ``` -Дополнительные crates `crank-mapping`, `crank-schema` и `crank-proto` нужны затем, чтобы не перегружать `crank-core`. +Поверх существующих crates должны появиться новые логические поддомены: + +- workspace/access domain; +- agent publishing domain; +- observability domain. ## 4. Детальная декомпозиция по crate @@ -68,58 +50,19 @@ crank/ - базовые доменные типы; - идентификаторы; -- метаданные операций; -- общие контракты и ошибки верхнего уровня. +- метаданные workspace, operation и agent; +- общие контракты и ошибки. -Что должно лежать в crate: +Внутренние модули: -```text -crank-core/ - src/ - lib.rs - ids.rs - protocol.rs - operation/ - mod.rs - model.rs - status.rs - target.rs - metadata.rs - auth/ - mod.rs - profile.rs - secret_ref.rs - errors/ - mod.rs - domain.rs - validation.rs - runtime.rs -``` - -Описание модулей: - -- `ids.rs` - типы `OperationId`, `DescriptorId`, `ToolId` и другие идентификаторы. -- `protocol.rs` - enum протоколов и общие protocol capability flags. -- `operation/model.rs` - основная доменная модель операции без технических деталей хранения. -- `operation/status.rs` - типы состояний операции. -- `operation/target.rs` - базовые protocol-specific target structs. -- `operation/metadata.rs` - описание tool, display name, version, tags. -- `auth/profile.rs` - типы auth-профилей без привязки к конкретному клиенту. -- `auth/secret_ref.rs` - ссылки на секреты, а не сами секреты. -- `errors/*` - типизированные ошибки доменного слоя. - -Что не должно лежать в crate: - -- JSON Schema реализация; -- mapping engine; -- SQL-модели; -- HTTP DTO; -- protobuf parsing; -- `reqwest`, `sqlx`, `tonic`, `axum`. - -Причина: - -`crank-core` должен быть максимально стабильным и независимым. Если положить туда все подряд, он станет точкой связности всей системы. +- `ids` +- `protocol` +- `workspace` +- `operation` +- `agent` +- `auth` +- `observability` +- `errors` ### 4.2. `crank-schema` @@ -127,107 +70,16 @@ crank-core/ - внутренняя модель схем; - нормализация входа и выхода; -- представление полей для UI и runtime; -- преобразование схем из разных источников в единый вид. - -Структура: - -```text -crank-schema/ - src/ - lib.rs - schema/ - mod.rs - model.rs - field.rs - scalar.rs - object.rs - collection.rs - oneof.rs - enums.rs - normalize/ - mod.rs - json.rs - graphql.rs - protobuf.rs - validate/ - mod.rs - input.rs - output.rs -``` - -Описание: - -- `schema/model.rs` - корневая структура схемы. -- `field.rs` - описание поля, nullable, required, description. -- `scalar.rs` - базовые scalar types. -- `object.rs` - вложенные объекты. -- `collection.rs` - массивы и map-подобные структуры. -- `oneof.rs` - представление protobuf `oneof`. -- `enums.rs` - enum-значения и метаданные. -- `normalize/*` - преобразование GraphQL и protobuf моделей в единую схему. -- `normalize/json.rs` - нормализация загруженных JSON-примеров во внутреннюю schema model. -- `validate/*` - проверка JSON относительно внутренней схемы. - -Почему отдельный crate: - -Схемы будут использоваться почти везде, но это не повод тащить их в `core`. Иначе `core` станет тяжелым и начнет менять версию при каждом изменении схемной логики. +- представление полей для UI и runtime. ### 4.3. `crank-mapping` Назначение: -- описание mapping DSL; -- компиляция mappings в runtime-представление; -- применение mappings к входу и выходу; -- автогенерация чернового mapping по загруженным примерам; -- трассировка ошибок маппинга. - -Структура: - -```text -crank-mapping/ - src/ - lib.rs - model/ - mod.rs - mapping.rs - source.rs - target.rs - transform.rs - parser/ - mod.rs - jsonpath.rs - compile/ - mod.rs - plan.rs - infer/ - mod.rs - from_samples.rs - from_schema.rs - execute/ - mod.rs - input.rs - output.rs - errors.rs -``` - -Описание: - -- `model/mapping.rs` - описание одного правила mapping. -- `model/source.rs` - откуда берем данные: `mcp`, `response`, `constant`. -- `model/target.rs` - куда кладем данные: `request.path`, `request.query`, `request.body`, `output`. -- `model/transform.rs` - ограниченный набор допустимых преобразований. -- `parser/jsonpath.rs` - единый parser и validator для `JSONPath` выражений. -- `compile/plan.rs` - предварительно скомпилированный план маппинга. -- `infer/from_samples.rs` - генерация чернового mapping по загруженным примерам JSON. -- `infer/from_schema.rs` - генерация чернового mapping по нормализованной схеме. -- `execute/input.rs` - применение mappings к запросу. -- `execute/output.rs` - применение mappings к ответу. - -Антипаттерн, которого нужно избежать: - -не помещать mapping-правила в строковые поля, которые потом интерпретируются каждым адаптером по-своему. Mapping должен быть единым движком. +- mapping DSL; +- `JSONPath` parsing; +- input/output mapping; +- draft inference из samples. ### 4.4. `crank-proto` @@ -237,472 +89,55 @@ crank-mapping/ - извлечение services, methods и message schemas; - преобразование protobuf metadata во внутренние типы. -Структура: - -```text -crank-proto/ - src/ - lib.rs - descriptor/ - mod.rs - loader.rs - source.rs - registry.rs - reflect/ - mod.rs - client.rs - model/ - mod.rs - service.rs - method.rs - message.rs - field.rs - convert/ - mod.rs - to_schema.rs - to_json.rs - from_json.rs - errors.rs -``` - -Описание: - -- `descriptor/loader.rs` - загрузка descriptor set. -- `descriptor/source.rs` - типы источников: upload, file, reflection. -- `descriptor/registry.rs` - индексирование описаний для поиска services/methods. -- `reflect/client.rs` - клиент server reflection, если будет добавлен. -- `model/*` - protobuf-ориентированная промежуточная модель. -- `convert/to_schema.rs` - перевод protobuf message в `crank-schema`. -- `convert/to_json.rs` и `from_json.rs` - преобразование runtime payload. - -Почему отдельный crate: - -protobuf-логика объемная и быстро начнет загрязнять gRPC adapter, если не отделить ее сразу. - ### 4.5. `crank-registry` Назначение: -- хранение операций, схем, descriptor links и статусов; -- выдача draft/published представлений; -- поиск активных операций для runtime и MCP server. - -Структура: - -```text -crank-registry/ - src/ - lib.rs - model/ - mod.rs - record.rs - draft.rs - published.rs - repo/ - mod.rs - operation_repo.rs - descriptor_repo.rs - service/ - mod.rs - create_operation.rs - update_operation.rs - publish_operation.rs - list_operations.rs - get_runtime_view.rs - storage/ - mod.rs - postgres.rs - cache/ - mod.rs - runtime_cache.rs - errors.rs -``` - -Описание: - -- `model/record.rs` - DB-aligned record model. -- `model/draft.rs` - модель черновика. -- `model/published.rs` - модель опубликованной операции. -- `repo/*` - контракты репозиториев. -- `service/*` - use case операции над реестром. -- `storage/*` - реализации репозиториев на `sqlx`. -- `cache/runtime_cache.rs` - кэш активных операций. - -Правило: - -`registry` не выполняет операции и не знает о `reqwest`/`tonic`. Он только хранит и отдает согласованные представления. +- хранение workspace-scoped operations и version snapshots; +- хранение agents и agent versions; +- auth profiles; +- platform API keys; +- logs и usage aggregates; +- metadata по sample artifacts и descriptors. ### 4.6. `crank-runtime` Назначение: -- исполнение операций; -- orchestration между схемой, mapping и адаптерами; -- выдача нормализованного результата. +- исполнение published operation; +- запись invocation events; +- возврат нормализованного результата. -Структура: +### 4.7. Protocol adapters -```text -crank-runtime/ - src/ - lib.rs - executor/ - mod.rs - operation_executor.rs - input_prepare.rs - output_finalize.rs - adapter/ - mod.rs - traits.rs - dispatch.rs - context/ - mod.rs - execution_context.rs - model/ - mod.rs - runtime_operation.rs - prepared_request.rs - adapter_response.rs - errors.rs -``` +- `crank-adapter-rest` +- `crank-adapter-graphql` +- `crank-adapter-grpc` -Описание: +Каждый adapter знает только свой протокол. -- `executor/operation_executor.rs` - основной orchestration use case. -- `executor/input_prepare.rs` - валидация входа и применение input mapping. -- `executor/output_finalize.rs` - обработка adapter response и output mapping. -- `adapter/traits.rs` - общий контракт для протокольных адаптеров. -- `adapter/dispatch.rs` - выбор адаптера по протоколу. -- `context/execution_context.rs` - correlation id, deadlines, tracing data. -- `model/runtime_operation.rs` - runtime-ready представление операции. +### 4.8. `apps/admin-api` -Правило: +Должен содержать сервисные группы: -`runtime` не должен знать, где хранится операция. Он получает уже готовую `runtime_operation`. +- `workspaces` +- `memberships` +- `operations` +- `auth_profiles` +- `agents` +- `platform_api_keys` +- `logs` +- `usage` -### 4.7. `crank-adapter-rest` +### 4.9. `apps/mcp-server` Назначение: -- построение и выполнение REST-вызовов. +- публикация published agent bindings как MCP tools; +- transport handling; +- JSON-RPC lifecycle; +- вызов runtime. -Структура: +Антипаттерн: -```text -crank-adapter-rest/ - src/ - lib.rs - client.rs - request/ - mod.rs - build.rs - path.rs - query.rs - headers.rs - body.rs - response/ - mod.rs - decode.rs - normalize.rs - errors.rs -``` - -Правило: - -REST adapter не валидирует MCP input и не знает о registry. Он получает уже подготовленный request contract. - -### 4.8. `crank-adapter-graphql` - -Назначение: - -- построение и выполнение GraphQL-вызовов. - -Структура: - -```text -crank-adapter-graphql/ - src/ - lib.rs - client.rs - request/ - mod.rs - build.rs - variables.rs - response/ - mod.rs - decode.rs - extract.rs - errors.rs -``` - -Правило: - -GraphQL adapter не занимается introspection по умолчанию и не содержит редактор схем. Он только исполняет подготовленный operation template. - -Дополнительное ограничение: - -один GraphQL tool соответствует одному заранее определенному `query` или `mutation`. Адаптер не должен принимать от LLM произвольный GraphQL-документ, потому что в MCP-модели операция должна оставаться узкой, предсказуемой и валидируемой по фиксированной схеме. - -### 4.9. `crank-adapter-grpc` - -Назначение: - -- выполнение unary gRPC-вызовов на основе уже выбранного метода и descriptor metadata. - -Структура: - -```text -crank-adapter-grpc/ - src/ - lib.rs - channel.rs - invoke/ - mod.rs - unary.rs - request/ - mod.rs - build.rs - response/ - mod.rs - decode.rs - errors.rs -``` - -Правило: - -gRPC adapter не должен сам парсить `.proto`. Этим занимается `crank-proto`. Иначе в адаптере смешаются discovery и execution. - -Дополнительное ограничение: - -adapter поддерживает только unary RPC. Streaming-вызовы не реализуются, потому что целевая модель MCP tool в проекте соответствует сценарию `запрос -> ответ`, а не долгоживущей сессии обмена сообщениями. - -### 4.10. `admin-api` - -Назначение: - -- HTTP API для UI; -- координация use case создания, редактирования, тестирования и публикации. - -Структура: - -```text -apps/admin-api/ - src/ - main.rs - app.rs - state.rs - router.rs - http/ - mod.rs - dto/ - mod.rs - operation.rs - mapping.rs - test_run.rs - handlers/ - mod.rs - create_operation.rs - update_operation.rs - publish_operation.rs - list_operations.rs - test_operation.rs - export_operation_yaml.rs - import_operation_yaml.rs - upload_json_samples.rs - upload_proto.rs - list_grpc_services.rs - response.rs - services/ - mod.rs - operation_service.rs - descriptor_service.rs - config_portability_service.rs - test_service.rs - errors.rs -``` - -Правила: - -- HTTP DTO не должны протекать в доменный слой; -- handlers должны быть тонкими; -- orchestration должна жить в `services/*`; -- `state.rs` не должен разрастаться в огромную структуру. Лучше использовать вложенные state-компоненты или отдельные service bundles. -- import/export конфигурации в `YAML` должен быть отдельным use case, а не побочным эффектом обычного CRUD. - -### 4.11. `mcp-server` - -Назначение: - -- публикация MCP tools; -- вызов runtime по имени tool; -- обновление активного списка tools. - -Структура: - -```text -apps/mcp-server/ - src/ - main.rs - app.rs - state.rs - tools/ - mod.rs - list.rs - call.rs - cache.rs - translate/ - mod.rs - to_mcp_tool.rs - from_mcp_input.rs - to_mcp_output.rs - errors.rs -``` - -Правило: - -`mcp-server` не должен реализовывать business rules публикации. Он читает уже опубликованные операции и транслирует их в MCP. - -### 4.12. `ui` - -Назначение: - -- интерфейс оператора. - -Предлагаемая frontend-структура: - -```text -apps/ui/ - src/ - main.tsx - app/ - router.tsx - providers.tsx - pages/ - operation-list/ - operation-create/ - operation-edit/ - operation-test/ - grpc-browser/ - features/ - operation-form/ - mapping-editor/ - sample-upload/ - grpc-method-picker/ - schema-viewer/ - publish-operation/ - entities/ - operation/ - descriptor/ - shared/ - api/ - lib/ - ui/ - config/ -``` - -Правило: - -UI должен декомпозироваться по пользовательским сценариям, а не по типам файлов уровня "все компоненты в одной папке". - -## 5. Правила зависимостей между crate - -Целевой граф зависимостей: - -```text -crank-core -crank-schema -> crank-core -crank-mapping -> crank-core -crank-proto -> crank-core, crank-schema -crank-registry -> crank-core, crank-schema, crank-mapping -crank-adapter-rest -> crank-core -crank-adapter-graphql -> crank-core -crank-adapter-grpc -> crank-core, crank-proto -crank-runtime -> crank-core, crank-schema, crank-mapping, adapters -admin-api -> crank-core, crank-schema, crank-mapping, crank-proto, crank-registry, crank-runtime -mcp-server -> crank-core, crank-registry, crank-runtime -``` - -Критические ограничения: - -- `crank-core` ни от кого не зависит; -- адаптеры не зависят от `registry`; -- `runtime` не зависит от `admin-api` и `mcp-server`; -- `registry` не зависит от адаптеров; -- `ui` зависит только от HTTP API. - -## 6. Границы публичных API модулей - -Чтобы структура не разъехалась, нужно заранее ограничить публичность. - -Рекомендуемое правило: - -- наружу экспортируются только корневые доменные типы, service-интерфейсы и ошибки; -- внутренние DTO, record-модели и промежуточные builder-структуры остаются `pub(crate)`; -- не реэкспортировать целые деревья модулей без необходимости; -- не делать `mod utils`, если можно назвать модуль по смыслу. - -Пример плохого решения: - -- `pub mod common;` -- `pub mod helpers;` -- `pub struct AppContext { ... 25 полей ... }` - -Пример правильного решения: - -- `pub struct OperationExecutor` -- `pub trait OperationRepository` -- `pub struct RuntimeOperation` - -## 7. Какие большие структуры точно не нужны - -Ниже список сущностей, которые легко превращаются в антипаттерн: - -- одна гигантская `Operation`, содержащая сразу все REST, GraphQL и gRPC поля; -- один `MappingConfig`, содержащий и input, и output, и transforms, и validation rules без разделения; -- единый `AppState` со всеми репозиториями, клиентами, кэшами и конфигами; -- один `ProtocolAdapter` с ветвлением `match protocol` внутри на сотни строк; -- один `SchemaField` без выделения object/array/enum/oneof вариантов. - -Правильный подход: - -- отдельные target-типы по протоколам; -- отдельные input/output mapping модели; -- отдельные bounded state-наборы для каждого приложения; -- отдельные adapter crates; -- выделенная иерархия schema types. - -## 8. Порядок реализации без архитектурного долга - -Рекомендуемый порядок разработки: - -1. `crank-core` -2. `crank-schema` -3. `crank-mapping` -4. `crank-registry` -5. `crank-adapter-rest` -6. `crank-runtime` -7. `admin-api` -8. `ui` -9. `crank-proto` -10. `crank-adapter-grpc` -11. `crank-adapter-graphql` -12. `mcp-server` - -Причина такого порядка: - -- сначала фиксируется доменная модель; -- затем схема и mapping как самые чувствительные части; -- затем реестр и базовое выполнение REST; -- после этого можно собирать UI и только потом наращивать сложные протоколы. - -## 9. Практический итог - -Если придерживаться этой декомпозиции, то: - -- `crank-core` останется маленьким и стабильным; -- schema и mapping не смешаются с transport-логикой; -- protobuf discovery не загрязнит gRPC runtime; -- `admin-api` и `mcp-server` останутся тонкими входными слоями; -- добавление нового протокола не потребует переписывать половину проекта. - -Это и есть целевая архитектурная дисциплина проекта: отдельные слои, отдельные модели, минимально необходимая публичность и отсутствие "универсальных" структур, в которые со временем начинает стекаться вся система. +не превращать `mcp-server` во второй `admin-api`.