docs: redesign architecture around workspaces and agents

This commit is contained in:
a.tolmachev
2026-03-29 21:11:04 +03:00
parent df2974bafa
commit 2219d1249b
11 changed files with 1321 additions and 3270 deletions
+49 -48
View File
@@ -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.
+10 -5
View File
@@ -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`
+144 -422
View File
@@ -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` до начала реализации.
+184 -460
View File
@@ -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 слоя, отдельной схемной модели и отдельного адаптера.
+264
View File
@@ -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.
+186 -657
View File
@@ -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": "<base64-encoded-descriptor-set>"
}
```
Поля:
- `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<K, V>` преобразуется в `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, эта сущность должна быть сначала явно добавлена в эту модель данных, а уже потом в код и БД.
+259 -335
View File
@@ -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.
+70 -306
View File
@@ -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 уже описаны.
+51 -355
View File
@@ -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 стабильно зелёные;
- документация и продуктовый сценарий совпадают.
+47 -60
View File
@@ -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 слое.
+57 -622
View File
@@ -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`.