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](./Crank.png)
Crank - это low-code платформа для публикации внешних API в виде MCP tools без написания нового backend-обработчика под каждую интеграцию. Система предоставляет единый административный UI, в котором оператор может подключать REST, GraphQL и gRPC операции, настраивать маппинг входных и выходных данных, выполнять тестовый вызов и публиковать результат как MCP tool. Crank - платформа для публикации внешних API в виде MCP tools без написания отдельного backend-кода под каждую интеграцию. Целевая модель проекта строится вокруг связки `workspace -> agent -> operations`.
На текущем этапе репозиторий содержит проектную документацию и архитектурные решения, которые задают границы MVP и подход к реализации.
## Цели ## Цели
@@ -12,75 +10,78 @@ Crank - это low-code платформа для публикации внеш
- Поддержать динамическое добавление интеграций через UI или конфигурацию. - Поддержать динамическое добавление интеграций через UI или конфигурацию.
- Обеспечить единый сценарий работы оператора для REST, GraphQL и gRPC. - Обеспечить единый сценарий работы оператора для 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`. - Поддержка REST для `GET`, `POST`, `PUT`, `PATCH` и `DELETE`.
- Поддержка GraphQL для `query` и `mutation` на основе шаблонов и переменных. - Поддержка GraphQL для `query` и `mutation`.
- Поддержка только unary-методов gRPC. - Поддержка только unary-методов gRPC.
- Загрузка примеров `JSON` для ускоренного создания схем и чернового маппинга. - Platform API keys и membership layer.
- Загрузка `.proto` файлов или descriptor set для обнаружения схемы gRPC. - Observability: invocation logs, usage aggregates, latency/error metrics.
- Импорт и экспорт конфигураций операций в `YAML`. - Импорт и экспорт operation-конфигураций в `YAML`.
- Использование `JSONPath` для точечного маппинга вложенных параметров и ответа. - Использование `JSONPath` для точечного маппинга.
- Настройка маппинга запроса и ответа через UI.
- Публикация tools в MCP без пересборки backend.
## Структура документации ## Структура документации
- `docs/architecture.md` - архитектура системы, модули, потоки данных и стек. - `docs/architecture.md` - целевая архитектура системы.
- `docs/module-decomposition.md` - детальная декомпозиция crates и внутренних модулей. - `docs/as-is-to-be.md` - переход `as is -> to be`, page-by-page gap analysis и архитектурные конфликты.
- `docs/data-model.md` - формальная модель данных и JSON-структуры сущностей. - `docs/module-decomposition.md` - декомпозиция crates и модулей.
- `docs/database-schema.md` - схема БД, связи и versioning конфигураций. - `docs/data-model.md` - целевая модель данных.
- `docs/admin-api.md` - HTTP-контракты административного API. - `docs/database-schema.md` - целевая схема БД.
- `docs/diagrams.md` - структурные диаграммы компонентов, сущностей, БД и потоков. - `docs/admin-api.md` - целевые HTTP-контракты административного API.
- `docs/mcp-interface.md` - транспорт и контракт публикации MCP tools. - `docs/diagrams.md` - диаграммы компонентов, сущностей и БД.
- `docs/testing-strategy.md` - стратегия тестирования до и во время разработки. - `docs/mcp-interface.md` - модель MCP transport и agent-scoped publishing.
- `docs/runtime-config.md` - конфигурация окружения, storage и секретов. - `docs/testing-strategy.md` - стратегия тестирования.
- `docs/deployment.md` - контейнерный деплой, reverse proxy и CI/CD. - `docs/runtime-config.md` - конфигурация окружения.
- `docs/demo-runbook.md` - пошаговый сценарий локального запуска и воспроизводимого демо. - `docs/deployment.md` - деплой, reverse proxy и CI/CD.
- `docs/rust-design.md` - распределение методов, `impl`, `trait` и service-слоя без god-struct. - `docs/demo-runbook.md` - демонстрационный сценарий.
- `docs/development-rules.md` - правила разработки, TDD-процесс и git workflow. - `docs/rust-design.md` - правила распределения поведения в Rust.
- `docs/rust-code-rules.md` - Rust-specific правила кода, toolchain и linting. - `docs/development-rules.md` - правила разработки и workflow.
- `docs/implementation-plan.md` - последовательность модулей и фич по этапам реализации. - `docs/rust-code-rules.md` - Rust-specific coding rules.
- `docs/protocols/rest.md` - функциональные требования и ограничения для REST. - `docs/implementation-plan.md` - порядок перехода от текущего состояния к целевой модели.
- `docs/protocols/graphql.md` - функциональные требования и ограничения для GraphQL. - `docs/protocols/rest.md` - требования и ограничения для REST.
- `docs/protocols/grpc.md` - функциональные требования и ограничения для gRPC. - `docs/protocols/graphql.md` - требования и ограничения для GraphQL.
- `docs/protocols/grpc.md` - требования и ограничения для gRPC.
## Ключевая идея продукта ## Ключевая идея продукта
Система строится вокруг унифицированной сущности `Operation`. Каждая операция описывает: Система строится вокруг трех уровней:
- внешний протокол, - `Workspace` - граница данных и доступа команды.
- целевой endpoint или метод, - `Agent` - curated MCP endpoint для конкретного сценария LLM.
- входную схему, - `Operation` - низкоуровневый интеграционный контракт.
- правила маппинга входных данных,
- параметры выполнения, `Operation` описывает:
- правила маппинга выходных данных,
- внешний протокол;
- целевой endpoint или метод;
- входную схему;
- правила маппинга входных данных;
- параметры выполнения;
- правила маппинга выходных данных;
- метаданные MCP tool. - метаданные MCP tool.
За счет этого MCP runtime работает с единой внутренней моделью, а протокольные адаптеры уже выполняют конкретные вызовы REST, GraphQL или gRPC. `Agent` собирает ограниченный набор опубликованных операций в одну MCP-поверхность. Именно это решает проблему, когда один агент теряется в слишком большом наборе tools.
Для GraphQL это означает, что в MCP публикуется не "универсальный GraphQL endpoint", а конкретная операция с фиксированным шаблоном запроса, фиксированным набором входных параметров и предсказуемой структурой ответа.
Для упрощения настройки оператор может загружать примеры входного и выходного `JSON`, а для gRPC - `.proto` или descriptor set. На основе этих артефактов система строит черновую схему и стартовый маппинг, который затем вручную уточняется через `JSONPath`.
Конфигурации операций должны импортироваться и экспортироваться в `YAML`, чтобы их можно было переносить между окружениями, хранить в git и редактировать вне UI.
## CI/CD статус ## CI/CD статус
В репозитории настроены: В репозитории настроены:
- `CI` для Rust, UI и deployment artifacts; - `CI` для Rust, UI container и deployment artifacts;
- `CD`, который запускается после успешного `CI` на `main` или вручную; - `CD`, который запускается после успешного `CI` на `main` или вручную;
- containerized production-like deployment через `docker compose`. - containerized deployment через `docker compose`.
## Поддерживаемые протоколы ## Поддерживаемые протоколы
В MVP платформа ориентируется на три основных протокольных сценария интеграции: В целевой модели платформа ориентируется на:
- REST - REST
- GraphQL - GraphQL
- gRPC - gRPC
`SOAP` сознательно не входит в MVP. Он остается актуальным для части корпоративных и государственных интеграций, но требует отдельного адаптера с поддержкой WSDL, XML Schema, SOAP envelope, namespaces и XML-oriented mapping. Для первой версии это слишком большой отдельный пласт сложности. `SOAP` сознательно не входит в текущий scope.
+10 -5
View File
@@ -2,21 +2,26 @@
## Current ## Current
### `feat/remove-legacy-ui` ### `feat/as-is-to-be-docs`
Status: completed Status: completed
DoD: DoD:
- текущая React/Vite UI-кодовая база удалена - `as is -> to be` зафиксирован в документации
- `apps/ui` сохранен как статический placeholder для будущей замены - разобраны page-by-page backend gaps для `test-ui`
- compose, deploy и CI не ломаются после удаления legacy UI - workspace/agent/access/observability модель синхронизирована в архитектурных документах
## Next ## Next
- `feat/alpine-ui` - `feat/backend-gap-plan`
## Backlog ## Backlog
- `feat/backend-gap-plan`
- `feat/workspace-foundation`
- `feat/agent-publishing`
- `feat/platform-access`
- `feat/observability-api`
- `feat/alpine-ui` - `feat/alpine-ui`
- `feat/demo-assets` - `feat/demo-assets`
+144 -422
View File
@@ -2,16 +2,15 @@
## 1. Назначение документа ## 1. Назначение документа
Этот документ фиксирует HTTP-контракты административного API, через которое UI управляет операциями, загружает артефакты, тестирует вызовы и выполняет YAML import/export. Этот документ фиксирует целевые HTTP-контракты административного API, через которое UI управляет workspace, operations, agents, platform access и observability.
Документ задает логический контракт. Конкретные детали `axum` handlers, auth middleware и response envelope могут уточняться при реализации.
## 2. Общие правила API ## 2. Общие правила API
- все payload по умолчанию в `JSON`; - все payload по умолчанию в `JSON`;
- import/export конфигурации используют `YAML` как payload или файл; - import/export конфигурации используют `YAML`;
- версии operation адресуются явно; - все основные ресурсы являются `workspace-scoped`;
- published операция - это ссылка на конкретную version; - версии operation и agent адресуются явно;
- published operation и published agent - ссылки на конкретные version;
- ошибки валидации возвращаются отдельно от transport errors. - ошибки валидации возвращаются отдельно от transport errors.
Базовый префикс: Базовый префикс:
@@ -22,431 +21,154 @@
## 3. Основные ресурсы ## 3. Основные ресурсы
- `workspaces`
- `memberships`
- `invitations`
- `operations` - `operations`
- `versions` - `auth-profiles`
- `agents`
- `platform-api-keys`
- `logs`
- `usage`
- `samples` - `samples`
- `descriptors` - `descriptors`
- `auth-profiles`
- `test-runs`
- `config import/export` - `config import/export`
## 4. CRUD операций ## 4. Workspace-scoped routing
### `GET /api/admin/operations` Канонический префикс для UI-driven сценариев:
Назначение: ```text
/api/admin/workspaces/{workspace_id}
- список операций для 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"
}
]
}
``` ```
### `POST /api/admin/operations` ## 5. Группы endpoints
Назначение: ### 5.1. Workspaces and members
- создание новой операции и версии `1`. - `GET /api/admin/workspaces`
- `POST /api/admin/workspaces`
Тело: - `GET /api/admin/workspaces/{workspace_id}`
- `PATCH /api/admin/workspaces/{workspace_id}`
```json - `GET /api/admin/workspaces/{workspace_id}/members`
{ - `POST /api/admin/workspaces/{workspace_id}/invitations`
"name": "crm_create_lead", - `DELETE /api/admin/workspaces/{workspace_id}/invitations/{invitation_id}`
"display_name": "Create Lead",
"protocol": "rest", ### 5.2. Operations
"target": {
"kind": "rest", - `GET /api/admin/workspaces/{workspace_id}/operations`
"base_url": "https://api.example.com", - `POST /api/admin/workspaces/{workspace_id}/operations`
"method": "POST", - `GET /api/admin/workspaces/{workspace_id}/operations/{operation_id}`
"path_template": "/v1/leads" - `PATCH /api/admin/workspaces/{workspace_id}/operations/{operation_id}`
}, - `DELETE /api/admin/workspaces/{workspace_id}/operations/{operation_id}`
"input_schema": { "type": "object", "fields": {} }, - `POST /api/admin/workspaces/{workspace_id}/operations/{operation_id}/versions`
"output_schema": { "type": "object", "fields": {} }, - `GET /api/admin/workspaces/{workspace_id}/operations/{operation_id}/versions/{version}`
"input_mapping": { "rules": [] }, - `POST /api/admin/workspaces/{workspace_id}/operations/{operation_id}/publish`
"output_mapping": { "rules": [] }, - `POST /api/admin/workspaces/{workspace_id}/operations/{operation_id}/archive`
"execution_config": { - `POST /api/admin/workspaces/{workspace_id}/operations/{operation_id}/test-runs`
"timeout_ms": 10000 - `GET /api/admin/workspaces/{workspace_id}/operations/{operation_id}/export`
}, - `POST /api/admin/workspaces/{workspace_id}/operations/import`
"tool_description": {
"title": "Create CRM lead", ### 5.3. Samples and descriptors
"description": "Creates a new lead."
} - `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`
```json
{ ### 5.4. Upstream auth profiles
"operation_id": "op_01",
"version": 1, - `GET /api/admin/workspaces/{workspace_id}/auth-profiles`
"status": "draft" - `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}`
### `GET /api/admin/operations/{operation_id}`
### 5.5. Agents
Назначение:
- `GET /api/admin/workspaces/{workspace_id}/agents`
- получить метаданные operation и ссылки на draft/published версии. - `POST /api/admin/workspaces/{workspace_id}/agents`
- `GET /api/admin/workspaces/{workspace_id}/agents/{agent_id}`
### `GET /api/admin/operations/{operation_id}/versions/{version}` - `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`
### `POST /api/admin/operations/{operation_id}/versions` - `DELETE /api/admin/workspaces/{workspace_id}/agents/{agent_id}/bindings/{operation_id}`
Назначение: ### 5.6. Platform API keys
- создать новую draft-версию на основе текущего payload. - `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}`
- полная конфигурация operation;
- опционально `change_note`. ### 5.7. Observability
Ответ: - `GET /api/admin/workspaces/{workspace_id}/logs`
- `GET /api/admin/workspaces/{workspace_id}/logs/{log_id}`
```json - `GET /api/admin/workspaces/{workspace_id}/usage`
{ - `GET /api/admin/workspaces/{workspace_id}/usage/operations/{operation_id}`
"operation_id": "op_01", - `GET /api/admin/workspaces/{workspace_id}/usage/agents/{agent_id}`
"version": 4,
"status": "draft" ## 6. Page-to-endpoint mapping
}
``` ### Operations catalog
## 5. Публикация Нужны:
### `POST /api/admin/operations/{operation_id}/publish` - список операций;
- удаление операции;
Назначение: - edit/open operation;
- publish/archive;
- опубликовать текущую draft-версию. - usage summary для карточек и фильтров.
Тело: ### Wizard
```json Нужны:
{
"version": 4 - create/update version;
}
```
Ответ:
```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;
- test run; - 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. Назначение проекта ## 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` - внутреннее уникальное имя. - глобальной сущности `Operation`;
- `display_name` - имя, отображаемое в UI. - registry версий операций;
- `protocol` - `rest`, `graphql` или `grpc`. - runtime adapters `REST / GraphQL / unary gRPC`;
- `target` - хост и протокол-специфичное описание назначения. - `admin-api` для CRUD и тестовых вызовов;
- `input_schema` - нормализованный входной контракт. - `mcp-server`, который публикует tools из published operations.
- `input_mapping` - правила отображения MCP-входа в поля целевого запроса.
- `execution_config` - auth-профиль, таймауты, заголовки и протокол-специфичные параметры.
- `output_mapping` - правила отображения ответа внешней системы в нормализованный выход.
- `tool_description` - метаданные для MCP и LLM.
- `status` - draft, testing, published, archived.
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 для создания и редактирования операций. `Operation` остается фундаментом интеграции, но tools больше не публикуются глобально. MCP публикует tools в контексте конкретного `workspace` и конкретного `agent`.
- Динамический реестр операций.
- Runtime-выполнение REST операций.
- Runtime-выполнение GraphQL операций.
- Runtime-выполнение unary gRPC методов.
- Загрузка примеров `JSON` для ускоренного создания схем и mappings.
- Импорт и экспорт конфигураций в `YAML`.
- Тестирование операций до публикации.
- Публикация MCP tools на основе данных из реестра.
- Hot reload опубликованных операций без изменения backend-кода.
### Не входит в 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. - gRPC streaming.
- Полноценный импорт OpenAPI с автоматической генерацией маппинга.
- Полноценный визуальный конструктор GraphQL-запросов.
- SOAP. - SOAP.
- Выполнение произвольного кода внутри mapping-правил. - Оркестрация workflow.
- Оркестрация нескольких операций в виде workflow. - Биллинг.
- Мультитенантность и биллинг. - Full RBAC policy engine.
- Traffic splitting и deployment orchestration.
## 4. Пользовательский сценарий ## 6. Пользовательские сценарии
Сценарий работы оператора должен быть одинаковым для всех протоколов: ### Оператор операций
1. Выбрать протокол. 1. Выбирает workspace.
2. Указать целевой хост или сервер. 2. Создает или редактирует operation.
3. Выбрать или описать внешнюю операцию. 3. Выполняет test run.
4. Определить MCP-входные параметры. 4. Публикует operation version.
5. Сопоставить MCP-вход с внешним запросом. 5. Привязывает operation к одному или нескольким agents.
6. Сопоставить внешний ответ с MCP-выходом.
7. Добавить описание для MCP и LLM.
8. Выполнить тестовый вызов.
9. Опубликовать операцию.
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
REST-адаптер является базовым и должен реализовываться первым.
Поддержка в MVP:
- `GET` - `GET`
- `POST` - `POST`
- `PUT` - `PUT`
@@ -84,22 +167,9 @@ REST-адаптер является базовым и должен реализ
- headers - headers
- JSON request body - JSON request body
- JSON response body - JSON response body
- аутентификация `Bearer`, `Basic` и API key
Пользователь настраивает:
- base URL,
- HTTP method,
- path template,
- request mapping,
- response mapping.
### GraphQL ### GraphQL
Поддержка GraphQL в MVP должна быть намеренно упрощена.
Поддержка в MVP:
- `query` - `query`
- `mutation` - `mutation`
- endpoint URL - endpoint URL
@@ -108,56 +178,16 @@ REST-адаптер является базовым и должен реализ
- variables mapping - variables mapping
- извлечение результата из `data` - извлечение результата из `data`
Пользователь настраивает: GraphQL в MCP публикуется как фиксированная операция с предсказуемой структурой ответа.
- 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.
### gRPC ### gRPC
gRPC - наиболее сложный протокол в этом проекте, поэтому его нужно ограничить на раннем этапе. - только unary RPC;
- `.proto` и `descriptor set`;
- JSON-oriented schema model поверх protobuf;
- без streaming.
Поддержка в MVP: ## 8. Работа с файлами и автогенерация черновика
- только 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.
Поддерживаемые источники: Поддерживаемые источники:
@@ -168,368 +198,62 @@ Streaming gRPC сознательно не входит в рамки проек
Ожидаемый сценарий: Ожидаемый сценарий:
1. Оператор загружает пример входных данных и пример ответа. 1. оператор загружает артефакты;
2. Система строит черновую схему входа и выхода. 2. система строит черновую схему и mapping;
3. Система предлагает стартовый mapping по совпадающим или близким по структуре полям. 3. оператор вручную корректирует результат;
4. Оператор вручную корректирует результат. 4. готовую конфигурацию можно экспортировать в `YAML`.
5. Для точечной настройки используется `JSONPath`.
6. Готовую конфигурацию можно экспортировать в `YAML` или импортировать обратно.
Для gRPC источником структуры является не пример JSON-сообщения, а `.proto` или descriptor set. Однако после преобразования protobuf-схемы во внутреннюю JSON-ориентированную модель пользовательский опыт должен оставаться тем же: видим структуру полей, получаем стартовый mapping, затем уточняем его вручную. ## 9. Внутренняя модель данных
`YAML` используется как человекочитаемое представление конфигурации operation для: Базовые сущности:
- переноса между окружениями; - `Workspace`
- резервного копирования; - `Operation`
- хранения в git; - `OperationVersion`
- редактирования вне UI; - `Agent`
- пакетного импорта нескольких operation. - `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` На каждый вызов tool сохраняются:
- `name`
- `display_name` - `workspace_id`
- `protocol` - `agent_id`
- `operation_id`
- `request_id`
- `timestamp`
- `status` - `status`
- `target` - `duration_ms`
- `input_schema` - `error_kind`
- `output_schema` - `request_preview`
- `input_mapping` - `response_preview`
- `output_mapping`
- `execution_config`
- `tool_description`
- `created_at`
- `updated_at`
### Target Сверху строятся:
REST target: - logs page;
- usage page;
- периодические rollups;
- latency and error aggregates.
- `base_url` ## 12. Модель маппинга
- `method`
- `path_template`
GraphQL target: Платформе нужен отдельный слой маппинга:
- `endpoint` - сопоставление поле-в-поле по `JSONPath`;
- `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`,
- константы,
- значения по умолчанию,
- извлечение вложенных полей из ответа. - извлечение вложенных полей из ответа.
Примеры:
- `$.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. Назначение документа ## 1. Назначение документа
Этот документ фиксирует формальную модель данных платформы. Его цель - определить такие структуры, которые можно без существенных изменений перенести в: Этот документ фиксирует целевую формальную модель данных платформы. Его цель - определить такие структуры, которые можно без существенных изменений перенести в:
- Rust domain types, - Rust domain types;
- HTTP DTO, - HTTP DTO;
- структуру таблиц БД, - структуру таблиц БД;
- runtime-представление operation, - runtime-представление операций и агентов;
- UI-формы и конфигурационные экраны. - UI-формы и конфигурационные экраны.
Документ не привязан к конкретной СУБД, но задает каноническую JSON-модель сущностей.
## 2. Общие принципы модели ## 2. Общие принципы модели
### 2.1. Одна операция - один tool ### 2.1. Одна операция - один интеграционный контракт
Каждая `Operation` соответствует одному MCP tool. Это особенно важно для: Каждая `Operation` соответствует одному интеграционному контракту:
- GraphQL, где одна operation соответствует одному конкретному `query` или `mutation`; - GraphQL -> один конкретный `query` или `mutation`;
- gRPC, где одна operation соответствует одному unary-методу; - gRPC -> один unary method;
- REST, где одна operation соответствует одному endpoint-сценарию. - REST -> один endpoint-сценарий.
Однако MCP tool публикуется не напрямую из operation, а через `AgentOperationBinding` внутри конкретного `Agent`.
### 2.2. Внутренний транспортный формат - JSON ### 2.2. Внутренний транспортный формат - JSON
Независимо от внешнего протокола внутри системы данные должны быть представлены в JSON-ориентированном виде. Даже если внешний вызов работает с protobuf, runtime, mapping и UI опираются на нормализованный JSON. Независимо от внешнего протокола внутри системы данные представлены в JSON-ориентированном виде.
### 2.3. Mapping всегда явный ### 2.3. Mapping всегда явный
Даже если система умеет строить черновой mapping по примерам данных, итоговая конфигурация mapping должна быть явно сохранена в operation. Нельзя полагаться на неявную "магию" сопоставления во время выполнения. Даже если система умеет строить черновой mapping по примерам данных, итоговая конфигурация mapping сохраняется явно.
### 2.4. JSONPath как единый язык адресации ### 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` - уникальный идентификатор операции. Помимо канонической JSON-модели система поддерживает импорт и экспорт конфигураций в `YAML`.
- `name` - стабильное внутреннее имя.
- `display_name` - отображаемое имя в UI. ## 3. Корневые сущности
- `protocol` - `rest`, `graphql`, `grpc`.
- `status` - `draft`, `testing`, `published`, `archived`. ### 3.1. `Workspace`
- `version` - версия конфигурации операции.
- `target` - описание внешней операции. Поля:
- `input_schema` - схема MCP-входа.
- `output_schema` - схема MCP-выхода. - `id`
- `input_mapping` - правила подготовки внешнего запроса. - `slug`
- `output_mapping` - правила формирования MCP-ответа. - `display_name`
- `execution_config` - auth, headers, timeout, retries и protocol-specific execution settings. - `status`
- `tool_description` - описание tool для MCP и LLM. - `settings`
- `samples` - загруженные образцы JSON и schema artifacts. - `created_at`
- `generated_draft` - автоматически построенный черновик схем и mappings. - `updated_at`
- `config_export` - опциональные метаданные экспортируемой конфигурации.
Назначение:
- логическая изоляция команд;
- 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` - `created_at`
- `updated_at` - `updated_at`
- `published_at` - `published_at`
### Пример ### 3.3. `Agent`
```json `Agent` - пользовательская MCP-поверхность, которая собирает ограниченный набор published operations.
{
"id": "op_01hr7w0m6p8x9z4n7s2k3q5t6u", Поля:
"name": "crm_create_lead",
"display_name": "Create Lead", - `id`
"protocol": "rest", - `workspace_id`
"status": "draft", - `slug`
"version": 3, - `display_name`
"target": { - `description`
"kind": "rest", - `status`
"base_url": "https://api.example.com", - `current_draft_version`
"method": "POST", - `latest_published_version`
"path_template": "/v1/leads" - `created_at`
}, - `updated_at`
"input_schema": { - `published_at`
"type": "object",
"fields": { ### 3.4. `AgentVersion`
"name": {
"type": "string", Снимок конфигурации агента.
"required": true
}, Поля:
"email": {
"type": "string", - `agent_id`
"required": true - `version`
} - `status`
} - `instructions`
}, - `tool_selection_policy`
"output_schema": { - `bindings`
"type": "object", - `created_at`
"fields": {
"id": { ### 3.5. `AgentOperationBinding`
"type": "string",
"required": true Связь published operation с agent version.
},
"status": { Поля:
"type": "string",
"required": true - `operation_id`
} - `operation_version`
} - `tool_name`
}, - `tool_title`
"input_mapping": { - `tool_description_override`
"rules": [ - `enabled`
{
"source": "$.mcp.name", ### 3.6. `AuthProfile`
"target": "$.request.body.name"
}, Используется только для доступа к внешним системам.
{
"source": "$.mcp.email", Поля:
"target": "$.request.body.email"
} - `id`
] - `workspace_id`
}, - `name`
"output_mapping": { - `kind`
"rules": [ - `config`
{
"source": "$.response.body.id", ### 3.7. `PlatformApiKey`
"target": "$.output.id"
}, Отдельная сущность для доступа к самой платформе.
{
"source": "$.response.body.status", Поля:
"target": "$.output.status"
} - `id`
] - `workspace_id`
}, - `name`
"execution_config": { - `prefix`
"timeout_ms": 10000, - `scopes`
"auth_profile_ref": "auth_01hr7x8rj2d8nq8v0c4m4t1r9e" - `status`
}, - `created_at`
"tool_description": { - `last_used_at`
"title": "Create CRM lead",
"description": "Creates a new lead in CRM by name and email." ### 3.8. `InvocationLog`
},
"samples": { Продуктовая запись о вызове tool.
"input_json_sample_ref": "file_01hr7xj7z4k1t0qm0cxsw6f1wx",
"output_json_sample_ref": "file_01hr7xkvt3m1p6ms3m7r2dr8wz" Поля:
},
"generated_draft": { - `id`
"status": "available", - `workspace_id`
"source_types": ["input_json_sample", "output_json_sample"] - `agent_id`
}, - `operation_id`
"config_export": { - `request_id`
"format_version": "1", - `level`
"export_mode": "portable" - `status`
}, - `duration_ms`
"created_at": "2026-03-25T08:00:00Z", - `error_kind`
"updated_at": "2026-03-25T08:10:00Z", - `request_preview`
"published_at": null - `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` ## 4. `Target`
@@ -162,20 +212,6 @@
### 4.1. `RestTarget` ### 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` - `kind`
- `base_url` - `base_url`
- `method` - `method`
@@ -184,19 +220,6 @@
### 4.2. `GraphqlTarget` ### 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` - `kind`
- `endpoint` - `endpoint`
- `operation_type` - `operation_type`
@@ -206,20 +229,6 @@
### 4.3. `GrpcTarget` ### 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` - `kind`
- `server_addr` - `server_addr`
- `package` - `package`
@@ -228,499 +237,19 @@
- `descriptor_ref` - `descriptor_ref`
- `descriptor_set_b64` - `descriptor_set_b64`
`descriptor_ref` остается ссылкой на загруженный descriptor artifact в storage и registry.
`descriptor_set_b64` - runtime-ready snapshot descriptor set, который используется unary gRPC adapter для динамического вызова метода без генерации Rust-кода.
## 5. `Schema` ## 5. `Schema`
`Schema` - нормализованное описание входа или выхода. Это не JSON Schema в полном объеме, а внутренняя структурная модель, удобная для UI и runtime. `Schema` - нормализованное описание входа или выхода.
### Базовая форма Поддерживаются:
```json - скалярные поля;
{ - вложенные объекты;
"type": "object", - массивы;
"description": "Lead input", - enum;
"fields": { - nullable-поля;
"name": { - `oneof` для protobuf.
"type": "string",
"required": true,
"description": "Lead full name"
},
"tags": {
"type": "array",
"required": false,
"items": {
"type": "string"
}
}
}
}
```
### Поддерживаемые типы ## 6. Принцип совместимости
- `object` Если UI требует сущность, которой нет в текущем backend, эта сущность должна быть сначала явно добавлена в эту модель данных, а уже потом в код и БД.
- `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`.
Следующим логическим шагом после этого документа должна стать схема БД, в которой эти сущности будут разложены по таблицам и связям.
+259 -335
View File
@@ -2,364 +2,288 @@
## 1. Назначение документа ## 1. Назначение документа
Этот документ фиксирует структуру хранения конфигураций, версий операций, загруженных артефактов и published runtime-view. Его цель - дать основу для SQL-миграций и для реализации `crank-registry`. Этот документ фиксирует целевую структуру хранения workspace-scoped конфигураций, агентов, ключей доступа и observability-данных. Базовая СУБД - `PostgreSQL`.
В документе предполагается реляционная модель, ориентированная на `PostgreSQL`. Канонической считается схема, совместимая с `PostgreSQL`.
## 2. Общие принципы хранения ## 2. Общие принципы хранения
### 2.1. Версионирование обязательно ### 2.1. Версионирование обязательно
Конфигурация operation не должна храниться только в одной "живой" записи. Каждое существенное изменение должно приводить к появлению новой версии конфигурации. Конфигурация operation и agent не хранится только в одной "живой" записи. Каждое существенное изменение создает новую версию.
Для MVP в registry version snapshot хранит protocol-specific конфигурацию, схемы, mapping и execution settings. Поля identity и listing view (`name`, `display_name`, `protocol`) считаются стабильными и хранятся в `operations`.
### 2.2. Published и draft разделяются логически ### 2.2. Published и draft разделяются логически
- `draft` может меняться; - `draft` может меняться;
- `published` должна ссылаться на конкретную зафиксированную версию; - `published` всегда указывает на конкретную version;
- runtime читает только опубликованные версии. - 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. Тестовая изоляция - upstream secrets живут за `secret_ref`;
- platform API keys хранятся как hash.
Integration tests для registry должны выполняться на реальной `PostgreSQL`, но без влияния на runtime-данные. Предпочтительный способ:
- отдельная test database;
- либо отдельная временная schema на время теста;
- обязательная очистка после завершения тестов.
## 3. Основные таблицы ## 3. Основные таблицы
Минимальный набор таблиц: - `workspaces`
- `users`
- `memberships`
- `invitation_tokens`
- `operations` - `operations`
- `operation_versions` - `operation_versions`
- `published_operations` - `published_operations`
- `operation_samples` - `operation_samples`
- `descriptors` - `descriptors`
- `auth_profiles` - `auth_profiles`
- `agents`
- `agent_versions`
- `agent_operation_bindings`
- `published_agents`
- `platform_api_keys`
- `invocation_logs`
- `usage_rollups`
- `yaml_import_jobs` - `yaml_import_jobs`
Опционально позже: ## 4. Operations
- `operation_test_runs` ### `operations`
- `audit_log`
- `id`
## 4. Таблица `operations` - `workspace_id`
- `name`
Хранит стабильную сущность операции, не зависящую от конкретной версии. - `display_name`
- `protocol`
### Поля - `status`
- `current_draft_version`
- `id` `text primary key` - `latest_published_version`
- `name` `text not null unique` - `created_at`
- `display_name` `text not null` - `updated_at`
- `protocol` `text not null` - `published_at`
- `status` `text not null`
- `current_draft_version` `integer not null default 1` Ограничение:
- `latest_published_version` `integer null`
- `created_at` `timestamptz not null` - `unique (workspace_id, name)`
- `updated_at` `timestamptz not null`
- `published_at` `timestamptz null` ### `operation_versions`
### Назначение - `operation_id`
- `version`
- быстрый список операций; - `status`
- стабильный идентификатор для UI и MCP; - `target_json`
- привязка к актуальному draft и опубликованной версии. - `input_schema_json`
- `output_schema_json`
## 5. Таблица `operation_versions` - `input_mapping_json`
- `output_mapping_json`
Хранит полную сериализованную конфигурацию конкретной версии operation. - `execution_config_json`
- `tool_description_json`
### Поля - `samples_json`
- `generated_draft_json`
- `operation_id` `text not null` - `config_export_json`
- `version` `integer not null` - `change_note`
- `status` `text not null` - `created_at`
- `target_json` `jsonb not null` - `created_by`
- `input_schema_json` `jsonb not null`
- `output_schema_json` `jsonb not null` ### `published_operations`
- `input_mapping_json` `jsonb not null`
- `output_mapping_json` `jsonb not null` - `operation_id`
- `execution_config_json` `jsonb not null` - `version`
- `tool_description_json` `jsonb not null` - `published_at`
- `samples_json` `jsonb null` - `published_by`
- `generated_draft_json` `jsonb null`
- `config_export_json` `jsonb null` ## 5. Operation artifacts
- `change_note` `text null`
- `created_at` `timestamptz not null` ### `operation_samples`
- `created_by` `text null`
- `id`
### Ключи - `operation_id`
- `version`
- primary key: `(operation_id, version)` - `sample_kind`
- foreign key: `operation_id -> operations(id)` - `storage_ref`
- рекомендованный composite foreign key для связанных таблиц: `(operation_id, version)` - `content_type`
- `file_name`
### Почему так - `created_at`
Для MVP выгоднее хранить version snapshot целиком, а не дробить по десятку связанных таблиц. Это: ### `descriptors`
- упрощает versioning; - `id`
- упрощает откат; - `operation_id`
- упрощает YAML export; - `version`
- хорошо сочетается с JSON-oriented доменной моделью. - `descriptor_kind`
- `storage_ref`
## 6. Таблица `published_operations` - `source_name`
- `package_index_json`
Хранит явную published-привязку, которую читает runtime. - `created_at`
### Поля ### `yaml_import_jobs`
- `operation_id` `text primary key` - `id`
- `version` `integer not null` - `source_sample_id`
- `published_at` `timestamptz not null` - `status`
- `published_by` `text null` - `format_version`
- `mode`
### Назначение - `result_operation_id`
- `result_version`
- быстрый доступ к published runtime-view; - `error_text`
- отсутствие двусмысленности, какая именно версия сейчас активна; - `created_at`
- простой invalidation для runtime cache. - `finished_at`
### Рекомендуемая целостность ## 6. Upstream auth
- `operation_id -> operations(id)` ### `auth_profiles`
- `(operation_id, version) -> operation_versions(operation_id, version)`
- `id`
## 7. Таблица `operation_samples` - `workspace_id`
- `name`
Хранит метаданные и ссылки на sample artifacts. - `kind`
- `config_json`
### Поля - `created_at`
- `updated_at`
- `id` `text primary key`
- `operation_id` `text not null` Ограничение:
- `version` `integer not null`
- `sample_kind` `text not null` - `unique (workspace_id, name)`
- `storage_ref` `text not null`
- `content_type` `text not null` ## 7. Workspaces and access layer
- `file_name` `text null`
- `created_at` `timestamptz not null` ### `workspaces`
### Варианты `sample_kind` - `id`
- `slug`
- `input_json` - `display_name`
- `output_json` - `status`
- `yaml_import_source` - `settings_json`
- `created_at`
### Назначение - `updated_at`
- не класть большие sample payload в основные version records; ### `users`
- иметь возможность переиспользовать или пересобирать draft mapping;
- отслеживать, из каких sample-данных строился черновик. - `id`
- `email`
### Рекомендуемая целостность - `display_name`
- `status`
- `operation_id -> operations(id)` - `created_at`
- `(operation_id, version) -> operation_versions(operation_id, version)`
### `memberships`
## 8. Таблица `descriptors`
- `workspace_id`
Хранит gRPC schema artifacts. - `user_id`
- `role`
### Поля - `created_at`
- `id` `text primary key` ### `invitation_tokens`
- `operation_id` `text null`
- `version` `integer null` - `id`
- `descriptor_kind` `text not null` - `workspace_id`
- `storage_ref` `text not null` - `email`
- `source_name` `text null` - `role`
- `package_index_json` `jsonb null` - `status`
- `created_at` `timestamptz not null` - `token_hash`
- `expires_at`
### Варианты `descriptor_kind` - `created_at`
- `proto_upload` ## 8. Agents
- `descriptor_set`
- `reflection_snapshot` ### `agents`
### Назначение - `id`
- `workspace_id`
- связывать gRPC operation с конкретной схемой; - `slug`
- не хранить binary descriptor внутри основной operation version; - `display_name`
- иметь отдельную точку для discovery metadata. - `description`
- `status`
### Рекомендуемая целостность - `current_draft_version`
- `latest_published_version`
- если descriptor привязан к version, то `(operation_id, version) -> operation_versions(operation_id, version)` - `created_at`
- `updated_at`
## 9. Таблица `yaml_import_jobs` - `published_at`
Для MVP можно импортировать YAML синхронно, но таблицу под журнал импорта лучше предусмотреть сразу. Ограничение:
### Поля - `unique (workspace_id, slug)`
- `id` `text primary key` ### `agent_versions`
- `source_sample_id` `text null`
- `status` `text not null` - `agent_id`
- `format_version` `text not null` - `version`
- `mode` `text not null` - `status`
- `result_operation_id` `text null` - `instructions_json`
- `result_version` `integer null` - `tool_selection_policy_json`
- `error_text` `text null` - `created_at`
- `created_at` `timestamptz not null`
- `finished_at` `timestamptz null` ### `agent_operation_bindings`
### Назначение - `agent_id`
- `agent_version`
- аудит импортов; - `operation_id`
- разбор ошибок валидации; - `operation_version`
- поддержка будущего async import pipeline. - `tool_name`
- `tool_title`
## 10. Таблица `auth_profiles` - `tool_description_override`
- `enabled`
Хранит переиспользуемые профили аутентификации для внешних вызовов.
### `published_agents`
### Поля
- `agent_id`
- `id` `text primary key` - `version`
- `name` `text not null unique` - `published_at`
- `kind` `text not null` - `published_by`
- `config_json` `jsonb not null`
- `created_at` `timestamptz not null` ## 9. Platform access and observability
- `updated_at` `timestamptz not null`
### `platform_api_keys`
### Варианты `kind`
- `id`
- `bearer` - `workspace_id`
- `basic` - `name`
- `api_key_header` - `prefix`
- `api_key_query` - `secret_hash`
- `scopes_json`
### Правило - `status`
- `created_at`
`config_json` должен содержать только `secret_ref`, а не открытые секреты. - `last_used_at`
## 11. Предлагаемая SQL-форма ### `invocation_logs`
```sql - `id`
create table operations ( - `workspace_id`
id text primary key, - `agent_id`
name text not null unique, - `operation_id`
display_name text not null, - `request_id`
protocol text not null, - `level`
status text not null, - `status`
current_draft_version integer not null default 1, - `duration_ms`
latest_published_version integer null, - `error_kind`
created_at timestamptz not null, - `request_preview_json`
updated_at timestamptz not null, - `response_preview_json`
published_at timestamptz null - `created_at`
);
### `usage_rollups`
create table operation_versions (
operation_id text not null references operations(id), - `workspace_id`
version integer not null, - `agent_id`
status text not null, - `operation_id`
target_json jsonb not null, - `period_kind`
input_schema_json jsonb not null, - `period_start`
output_schema_json jsonb not null, - `calls_total`
input_mapping_json jsonb not null, - `calls_ok`
output_mapping_json jsonb not null, - `calls_error`
execution_config_json jsonb not null, - `p50_ms`
tool_description_json jsonb not null, - `p95_ms`
samples_json jsonb null, - `p99_ms`
generated_draft_json jsonb null,
config_export_json jsonb null, ## 10. Migration strategy
change_note text null,
created_at timestamptz not null, Переход от текущей схемы к целевой идет так:
created_by text null,
primary key (operation_id, version) 1. добавить `workspaces` и заполнить default workspace;
); 2. добавить `workspace_id` в `operations` и `auth_profiles`;
3. добавить `agents` и `published_agents`;
create table published_operations ( 4. внедрить `platform_api_keys`;
operation_id text primary key references operations(id), 5. добавить `invocation_logs` и `usage_rollups`;
version integer not null, 6. перевести MCP runtime на `published_agents`, а не на глобальный список operations.
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 модели домена.
+70 -306
View File
@@ -2,14 +2,11 @@
## 1. Назначение документа ## 1. Назначение документа
Этот документ собирает диаграммы, которые фиксируют проект до начала разработки: Этот документ собирает диаграммы целевой модели проекта:
- компонентную структуру; - компонентную структуру;
- связи между доменными сущностями; - связи между доменными сущностями;
- хранение данных в БД; - хранение данных в БД.
- основные runtime и admin-потоки.
Диаграммы даны в формате `Mermaid`, чтобы их можно было хранить прямо в репозитории и рендерить в Markdown-compatible tooling.
## 2. Компонентная диаграмма ## 2. Компонентная диаграмма
@@ -29,6 +26,7 @@ flowchart LR
GRPC[adapter-grpc] GRPC[adapter-grpc]
DB[(PostgreSQL)] DB[(PostgreSQL)]
STORE[(Artifact Storage)] STORE[(Artifact Storage)]
OBS[(Usage and Logs)]
UI --> ADMIN UI --> ADMIN
MCP --> REG MCP --> REG
@@ -36,352 +34,118 @@ flowchart LR
ADMIN --> REG ADMIN --> REG
ADMIN --> RUN ADMIN --> RUN
ADMIN --> PROTO ADMIN --> PROTO
REG --> DB REG --> DB
REG --> CORE REG --> CORE
REG --> SCHEMA REG --> SCHEMA
REG --> MAP REG --> MAP
RUN --> CORE RUN --> CORE
RUN --> SCHEMA RUN --> SCHEMA
RUN --> MAP RUN --> MAP
RUN --> REST RUN --> REST
RUN --> GQL RUN --> GQL
RUN --> GRPC RUN --> GRPC
GRPC --> PROTO GRPC --> PROTO
PROTO --> STORE PROTO --> STORE
ADMIN --> STORE ADMIN --> STORE
REG --> OBS
ADMIN --> OBS
``` ```
## 3. Диаграмма зависимостей crates ## 3. Структурная диаграмма доменной модели
```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. Структурная диаграмма доменной модели
```mermaid ```mermaid
classDiagram classDiagram
class Workspace {
+id
+slug
+display_name
}
class Operation { class Operation {
+id +id
+workspace_id
+name +name
+display_name +display_name
+protocol +protocol
+status +status
+version
+target
+input_schema
+output_schema
+input_mapping
+output_mapping
+execution_config
+tool_description
+samples
+generated_draft
+config_export
} }
class RestTarget { class Agent {
+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 {
+id +id
+name +workspace_id
+kind +slug
+config +display_name
}
class ToolDescription {
+title
+description
+tags
+examples
}
class Samples {
+input_json_sample_ref
+output_json_sample_ref
+proto_file_ref
+descriptor_ref
}
class GeneratedDraft {
+status +status
+source_types
+generated_at
+warnings
} }
Operation --> RestTarget : target class AgentBinding {
Operation --> GraphqlTarget : target +operation_id
Operation --> GrpcTarget : target +operation_version
Operation --> Schema : input_schema +tool_name
Operation --> Schema : output_schema +enabled
Operation --> MappingSet : input_mapping }
Operation --> MappingSet : output_mapping
Operation --> ExecutionConfig : execution_config class PlatformApiKey {
Operation --> ToolDescription : tool_description +id
Operation --> Samples : samples +workspace_id
Operation --> GeneratedDraft : generated_draft +name
ExecutionConfig --> AuthProfile : auth_profile_ref +prefix
MappingSet --> MappingRule : contains +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 ```mermaid
erDiagram 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{ OPERATION_VERSIONS : has
OPERATIONS ||--o| PUBLISHED_OPERATIONS : publishes OPERATIONS ||--o| PUBLISHED_OPERATIONS : publishes
OPERATIONS ||--o{ OPERATION_SAMPLES : owns AGENTS ||--o{ AGENT_VERSIONS : has
OPERATIONS ||--o{ DESCRIPTORS : may_use AGENTS ||--o| PUBLISHED_AGENTS : publishes
OPERATIONS ||--o{ YAML_IMPORT_JOBS : may_create AGENT_VERSIONS ||--o{ AGENT_OPERATION_BINDINGS : contains
AUTH_PROFILES ||--o{ OPERATION_VERSIONS : referenced_by OPERATIONS ||--o{ AGENT_OPERATION_BINDINGS : exposed_by
WORKSPACES {
text id PK
text slug
text display_name
}
OPERATIONS { OPERATIONS {
text id PK text id PK
text workspace_id FK
text name text name
text display_name text display_name
text protocol text protocol
text status text status
int current_draft_version
int latest_published_version
timestamptz created_at
timestamptz updated_at
timestamptz published_at
} }
AGENTS {
OPERATION_VERSIONS { text id PK
text operation_id FK text workspace_id FK
int version text slug
text display_name
text status 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. Назначение документа ## 1. Назначение документа
Этот документ фиксирует порядок реализации модулей и фич. Он нужен затем, чтобы разработка шла последовательно, а не параллельно во все стороны сразу. Этот документ фиксирует порядок перехода от текущего состояния проекта к целевой модели, заданной `test-ui`.
Принцип: Принцип:
- сначала фундамент; - сначала перепроектирование `as is -> to be`;
- потом минимальный end-to-end сценарий; - потом foundation под workspace/agent model;
- потом расширение протоколов; - потом возврат к end-to-end UI сценариям;
- потом observability и access layer;
- потом polish и demo readiness. - потом polish и demo readiness.
## 2. Этап 0. Scaffold проекта ## 2. Этап 1. Перепроектирование `As Is -> To Be`
Цель: Цель:
- создать `cargo workspace`; - зафиксировать новую доменную модель и page-driven backend contract.
- создать приложения и 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.
DoD: DoD:
- создан `cargo workspace`; - зафиксирован `as is -> to be` план;
- все crates и apps объявлены в workspace; - page-by-page gap analysis покрывает все целевые экраны;
- проект собирается без бизнес-логики; - разобраны все архитектурные конфликты UI vs current backend;
- базовые test targets запускаются; - документы `architecture`, `data-model`, `database-schema`, `admin-api`, `mcp-interface` синхронизированы.
- сделан атомарный commit со scaffold.
## 3. Этап 1. Базовая доменная модель ## 3. Этап 2. Workspace foundation
Цель: Цель:
- реализовать типы из `data-model`. - перевести хранение и API на workspace-scoped модель.
Фичи:
- `Operation`
- `Target`
- `Schema`
- `MappingSet`
- `ExecutionConfig`
- `ToolDescription`
- `AuthProfile`
Параллельно:
- unit tests на доменные типы;
- базовая сериализация `JSON`/`YAML`.
Результат:
- модель данных существует как код;
- нет инфраструктурных зависимостей внутри домена.
DoD: DoD:
- типы из `data-model` реализованы; - операции и auth profiles принадлежат workspace;
- базовая сериализация `JSON` и `YAML` проходит тесты; - registry умеет фильтровать данные по workspace;
- доменные `impl` не содержат инфраструктурной логики; - есть default workspace migration path.
- unit tests на ключевые типы проходят;
- изменения зафиксированы через один или несколько `RGR + commit`.
## 4. Этап 2. Schema engine ## 4. Этап 3. Agent publishing foundation
Цель: Цель:
- реализовать `crank-schema`. - ввести `Agent` и agent-scoped MCP publishing.
Фичи:
- model полей и типов;
- schema validation;
- field traversal;
- нормализация JSON samples;
- protobuf -> schema bridge contracts.
Результат:
- можно описывать и валидировать вход/выход.
DoD: DoD:
- реализована схема полей и типов; - можно создать agent и привязать к нему published operations;
- работает schema validation; - `mcp-server` выдает tools в контексте конкретного agent;
- JSON sample normalization покрыт тестами; - один agent видит только свой curated toolset.
- контракты protobuf -> schema зафиксированы;
- нет смешивания schema logic с adapter logic.
## 5. Этап 3. Mapping engine ## 5. Этап 4. Operations and wizard integration
Цель: Цель:
- реализовать `crank-mapping`. - посадить operations catalog и wizard на реальные backend contracts.
Фичи:
- `JSONPath` parsing и validation;
- input mapping;
- output mapping;
- transforms;
- generation draft mapping из samples.
Результат:
- можно преобразовывать MCP input в request model и response в output model.
DoD: DoD:
- `JSONPath` parsing и validation работают; - каталог операций и wizard работают без `localStorage` overrides;
- input/output mapping проходят unit tests; - operation edit/delete/publish/test выполняются через backend;
- generation draft mapping покрыта фикстурами; - все протоколы работают в рамках одного UI flow.
- transforms ограничены и задокументированы;
- mapping engine не знает о конкретных protocol adapters.
## 6. Этап 4. Registry и БД ## 6. Этап 5. Agents UI and backend
Цель: Цель:
- реализовать `crank-registry` и миграции. - реализовать agent-centric слой.
Фичи:
- таблицы из `database-schema`;
- version snapshots;
- published operations;
- auth profiles;
- sample metadata;
- descriptor metadata;
- YAML import job log.
Результат:
- конфигурации можно хранить и версионировать.
DoD: DoD:
- миграции создают таблицы из `database-schema`; - agent CRUD работает;
- version snapshots работают корректно; - binding operations к agent работает;
- publish linkage реализован; - published agent появляется в MCP runtime.
- auth profiles и artifact metadata сохраняются;
- integration tests на registry проходят на реальной БД.
## 7. Этап 5. REST vertical slice ## 7. Этап 6. Platform access
Цель: Цель:
- получить первый рабочий end-to-end сценарий. - реализовать workspace access и platform API keys.
Фичи:
- `crank-adapter-rest`
- `crank-runtime` для REST
- REST test run
- создание REST operation
- publish REST operation
- вызов published REST tool из MCP слоя
Результат:
- MVP работает хотя бы для REST.
DoD: DoD:
- REST operation можно создать, протестировать и опубликовать; - UI screens `API Keys`, `Settings`, `Workspace` имеют backend-контракт;
- runtime исполняет REST operation end-to-end; - platform API keys не смешиваются с upstream auth profiles;
- published REST tool вызывается через MCP слой; - tenant boundary выражен в access layer.
- negative tests на mapping и external errors существуют;
- есть демонстрационный REST сценарий.
## 8. Этап 6. Admin API v1 ## 8. Этап 7. Observability
Цель: Цель:
- дать UI полный backend-контракт для базового сценария. - реализовать логи и usage.
Фичи:
- CRUD operations;
- create version;
- publish;
- upload input/output JSON samples;
- generate draft;
- test run;
- auth profiles CRUD;
- YAML import/export.
Результат:
- UI может полностью управлять REST operation без ручных правок кода.
DoD: DoD:
- доступны CRUD, versioning, publish, samples, draft generation, test runs; - `Logs` page и `Usage` page работают на реальных данных;
- доступны auth profiles и YAML import/export; - есть продуктовые endpoints, а не только application logs;
- API контракты соответствуют документации; - rollups и detail views согласованы с UI.
- integration tests на ключевые endpoints проходят;
- нет скрытой бизнес-логики в handlers.
## 9. Этап 7. UI v1 ## 9. Этап 8. Alpine UI integration
Цель: Цель:
- собрать рабочую административную консоль. - перенести `test-ui` в `apps/ui` и подключить его к реальному backend.
Фичи:
- список операций;
- мастер создания операции;
- sample upload;
- schema viewer;
- mapping editor;
- test run screen;
- publish flow;
- YAML import/export screen.
Результат:
- есть демонстрируемый пользовательский интерфейс.
DoD: DoD:
- UI покрывает основной сценарий от создания operation до publish; - `apps/ui` содержит целевой Alpine.js UI;
- sample upload и mapping editor работают; - mock JSON больше не используется на критическом пути;
- YAML import/export доступен из UI; - UI, backend и docs синхронизированы.
- нет блокирующих заглушек на критическом пути демо;
- основные пользовательские сценарии проверены вручную или integration tests.
## 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: DoD:
- `Streamable HTTP` transport работает; - end-to-end demo воспроизводим;
- list tools и call tool реализованы; - deployment и healthchecks стабильно зелёные;
- 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.
Такой порядок минимизирует архитектурный риск и дает ранний рабочий результат.
+47 -60
View File
@@ -2,40 +2,34 @@
## 1. Назначение документа ## 1. Назначение документа
Этот документ фиксирует, как именно платформа публикует operations в виде MCP tools и какой transport используется в MVP. Этот документ фиксирует, как именно платформа публикует agents и operations в виде MCP tools и какой transport используется в целевой модели.
Главная цель - убрать неопределенность вокруг вопроса "каким именно будет MCP server" до начала реализации.
## 2. Архитектурное решение ## 2. Архитектурное решение
Для MVP `mcp-server` должен публиковать tools через network-oriented MCP transport. `mcp-server` публикует tools через network-oriented MCP transport.
Рекомендуемое решение: Решение:
- основной transport: `Streamable HTTP`; - основной transport: `Streamable HTTP`;
- отдельный `mcp-server` как сервис; - отдельный `mcp-server` как сервис;
- `stdio` не является обязательной частью MVP. - `stdio` не является обязательной частью текущего scope.
Причина:
- проект задуман как `Crank`, а не как локальный single-process adapter;
- нужен удаленный доступ к опубликованным tools;
- published tools должны обновляться без пересборки и без локального обертывания каждого клиента.
## 3. Модель публикации tools ## 3. Модель публикации tools
Каждая published operation превращается в один MCP tool. Каждая published operation превращается в один MCP tool внутри конкретного published agent.
Соответствие: Соответствие:
- одна published version; - один published agent;
- один tool name; - набор `AgentOperationBinding`;
- один tool name на binding;
- одна input schema; - одна input schema;
- один результат. - один результат.
Публикация tool основана на: Публикация tool основана на:
- `operation.name` - `agent.slug`
- `operation.name` или binding-level `tool_name`
- `tool_description` - `tool_description`
- `input_schema` - `input_schema`
- `published runtime view` - `published runtime view`
@@ -44,7 +38,7 @@
`mcp-server` должен: `mcp-server` должен:
- загрузить published operations из registry; - загрузить published agents и их bindings из registry;
- преобразовать их в MCP tool definitions; - преобразовать их в MCP tool definitions;
- принимать вызовы tools от MCP clients; - принимать вызовы tools от MCP clients;
- валидировать вход; - валидировать вход;
@@ -62,12 +56,12 @@
- заниматься protobuf discovery; - заниматься protobuf discovery;
- содержать бизнес-логику admin UI. - содержать бизнес-логику admin UI.
## 6. Published runtime view ## 6. Runtime view
`mcp-server` должен работать не с полной admin-конфигурацией, а с runtime-ready view. В runtime view остаются:
В published runtime view остаются:
- `workspace_id`
- `agent_id`
- `operation_id` - `operation_id`
- `protocol` - `protocol`
- `target` - `target`
@@ -78,29 +72,30 @@
- `execution_config` - `execution_config`
- `tool_description` - `tool_description`
В published runtime view не должны попадать: В runtime view не попадают:
- raw uploaded samples; - raw uploaded samples;
- generated draft metadata; - generated draft metadata;
- YAML import metadata; - YAML import metadata;
- UI-specific helper fields. - UI-specific helper fields.
## 7. Transport для MVP ## 7. MCP endpoint model
### Поддерживается Канонический endpoint:
- `Streamable HTTP` ```text
/mcp/v1/{workspace_slug}/{agent_slug}
```
### Не обязательно в MVP Этот endpoint определяет:
- `stdio` - tenant boundary;
- дополнительные transport adapters - конкретный curated toolset;
- набор usage и log labels.
Если позже понадобится локальная интеграция, `stdio` можно добавить как отдельный transport layer поверх того же runtime.
## 8. MCP lifecycle ## 8. MCP lifecycle
MVP-контракт `mcp-server` строится вокруг JSON-RPC методов MCP: Поддерживаемые JSON-RPC методы:
- `initialize` - `initialize`
- `notifications/initialized` - `notifications/initialized`
@@ -108,37 +103,32 @@ MVP-контракт `mcp-server` строится вокруг JSON-RPC мет
- `tools/list` - `tools/list`
- `tools/call` - `tools/call`
Сессия создается на `initialize` и идентифицируется через `MCP-Session-Id`.
Согласованная версия протокола возвращается и читается через `MCP-Protocol-Version`.
Пока сессии хранятся in-memory внутри `mcp-server`, чего достаточно для MVP и demo-сценариев.
### Tool listing ### Tool listing
После `initialize` и `notifications/initialized`:
1. клиент вызывает `tools/list`; 1. клиент вызывает `tools/list`;
2. `mcp-server` перечитывает published operations по refresh policy; 2. `mcp-server` извлекает `workspace_slug` и `agent_slug` из path;
3. строит или обновляет in-memory catalog tools; 3. перечитывает published agent по refresh policy;
4. отдает список tools через MCP JSON-RPC result. 4. строит или обновляет in-memory catalog tools только для этого agent;
5. отдает список tools через MCP JSON-RPC result.
### Tool call ### Tool call
1. MCP client вызывает tool. 1. клиент вызывает tool;
2. `mcp-server` находит published runtime view. 2. `mcp-server` определяет `workspace` и `agent`;
3. Валидирует input относительно schema. 3. находит binding нужной operation внутри published agent;
4. Делегирует вызов в `crank-runtime`. 4. валидирует input относительно schema;
5. Возвращает результат. 5. делегирует вызов в `crank-runtime`;
6. возвращает результат.
## 9. Обновление tools ## 9. Обновление tools
После публикации новой версии: После публикации новой operation version или agent version:
1. `admin-api` фиксирует published version в registry. 1. `admin-api` фиксирует published version в registry;
2. `registry` обновляет published_operations. 2. `registry` обновляет published operations или published agents;
3. `mcp-server` не требует restart и не опирается на ручной reload signal. 3. `mcp-server` не требует restart;
4. `mcp-server` выполняет controlled refresh опубликованного каталога по interval-based policy. 4. выполняется controlled refresh опубликованного каталога;
5. Новый tool contract становится доступен MCP clients. 5. новый tool contract становится доступен MCP clients.
## 10. Именование tools ## 10. Именование tools
@@ -150,9 +140,9 @@ MVP-контракт `mcp-server` строится вокруг JSON-RPC мет
Требования: Требования:
- имя уникально в пределах платформы; - имя уникально в пределах одного agent;
- имя не зависит от внутреннего numeric version; - имя не зависит от numeric version;
- rename operation должен считаться отдельным осознанным изменением. - один и тот же operation может публиковаться под разными именами в разных agents.
## 11. Ошибки MCP слоя ## 11. Ошибки MCP слоя
@@ -164,14 +154,11 @@ MVP-контракт `mcp-server` строится вокруг JSON-RPC мет
- external service error; - external service error;
- internal runtime error. - internal runtime error.
`mcp-server` не должен терять стадию ошибки при трансляции ответа клиенту.
## 12. Практический итог ## 12. Практический итог
Для MVP достаточно следующей фиксации:
- `mcp-server` - отдельный сервис; - `mcp-server` - отдельный сервис;
- transport - `Streamable HTTP`; - transport - `Streamable HTTP`;
- одна published operation = один MCP tool; - endpoint определяется парой `workspace + agent`;
- одна published operation = один MCP tool внутри agent;
- reload published tools без пересборки сервиса; - reload published tools без пересборки сервиса;
- никакой draft-логики или admin CRUD в MCP слое. - никакой draft-логики или admin CRUD в MCP слое.
+57 -622
View File
@@ -2,44 +2,22 @@
## 1. Цель документа ## 1. Цель документа
Этот документ фиксирует детальную структуру проекта до начала активной разработки. Его задача - заранее ограничить ответственность каждого компонента, избежать разрастания `crank-core`, не допустить появления "универсальных" структур на все случаи жизни и сохранить понятные границы между доменной логикой, runtime, адаптерами, API и UI. Этот документ фиксирует детальную структуру проекта под целевую модель `workspace -> agent -> operations`.
Основной принцип: каждый crate отвечает за один слой системы. Внутри crate модули должны быть маленькими, тематическими и с минимальным количеством публичных сущностей. Основной принцип: каждый crate отвечает за один слой системы. Внутри crate модули маленькие, тематические и с минимальным количеством публичных сущностей.
## 2. Общие архитектурные правила ## 2. Общие архитектурные правила
### 2.1. Что считается правильной декомпозицией
- `core` содержит только базовую доменную модель, идентификаторы, типы ошибок и общие контракты. - `core` содержит только базовую доменную модель, идентификаторы, типы ошибок и общие контракты.
- `registry` отвечает только за хранение и загрузку конфигурации операций. - `registry` отвечает только за хранение и загрузку workspace-scoped конфигурации.
- `runtime` исполняет операции, но не знает о способе их хранения. - `runtime` исполняет операции, но не знает о способе их хранения.
- адаптеры знают только свой протокол и общий контракт runtime. - адаптеры знают только свой протокол и общий контракт runtime.
- `admin-api` оркестрирует use case для UI, но не содержит протокольной логики. - `admin-api` оркестрирует use case для UI, но не содержит протокольной логики.
- `mcp-server` публикует tools и вызывает runtime, но не содержит бизнес-логики конфигурирования. - `mcp-server` публикует agent-scoped tools и вызывает runtime.
- `ui` не знает внутреннюю реализацию runtime и работает только через HTTP API. - `ui` работает только через 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 сущностей;
- отдельные модули для чтения, записи, валидации и исполнения;
- композиция из небольших сервисов вместо одного глобального сервиса.
## 3. Workspace-структура ## 3. Workspace-структура
Рекомендуемая структура:
```text ```text
crank/ crank/
apps/ apps/
@@ -58,7 +36,11 @@ crank/
crank-proto/ crank-proto/
``` ```
Дополнительные crates `crank-mapping`, `crank-schema` и `crank-proto` нужны затем, чтобы не перегружать `crank-core`. Поверх существующих crates должны появиться новые логические поддомены:
- workspace/access domain;
- agent publishing domain;
- observability domain.
## 4. Детальная декомпозиция по crate ## 4. Детальная декомпозиция по crate
@@ -68,58 +50,19 @@ crank/
- базовые доменные типы; - базовые доменные типы;
- идентификаторы; - идентификаторы;
- метаданные операций; - метаданные workspace, operation и agent;
- общие контракты и ошибки верхнего уровня. - общие контракты и ошибки.
Что должно лежать в crate: Внутренние модули:
```text - `ids`
crank-core/ - `protocol`
src/ - `workspace`
lib.rs - `operation`
ids.rs - `agent`
protocol.rs - `auth`
operation/ - `observability`
mod.rs - `errors`
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` должен быть максимально стабильным и независимым. Если положить туда все подряд, он станет точкой связности всей системы.
### 4.2. `crank-schema` ### 4.2. `crank-schema`
@@ -127,107 +70,16 @@ crank-core/
- внутренняя модель схем; - внутренняя модель схем;
- нормализация входа и выхода; - нормализация входа и выхода;
- представление полей для UI и runtime; - представление полей для 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` станет тяжелым и начнет менять версию при каждом изменении схемной логики.
### 4.3. `crank-mapping` ### 4.3. `crank-mapping`
Назначение: Назначение:
- описание mapping DSL; - mapping DSL;
- компиляция mappings в runtime-представление; - `JSONPath` parsing;
- применение mappings к входу и выходу; - input/output mapping;
- автогенерация чернового mapping по загруженным примерам; - draft inference из samples.
- трассировка ошибок маппинга.
Структура:
```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 должен быть единым движком.
### 4.4. `crank-proto` ### 4.4. `crank-proto`
@@ -237,472 +89,55 @@ crank-mapping/
- извлечение services, methods и message schemas; - извлечение services, methods и message schemas;
- преобразование protobuf metadata во внутренние типы. - преобразование 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` ### 4.5. `crank-registry`
Назначение: Назначение:
- хранение операций, схем, descriptor links и статусов; - хранение workspace-scoped operations и version snapshots;
- выдача draft/published представлений; - хранение agents и agent versions;
- поиск активных операций для runtime и MCP server. - auth profiles;
- platform API keys;
Структура: - logs и usage aggregates;
- metadata по sample artifacts и descriptors.
```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`. Он только хранит и отдает согласованные представления.
### 4.6. `crank-runtime` ### 4.6. `crank-runtime`
Назначение: Назначение:
- исполнение операций; - исполнение published operation;
- orchestration между схемой, mapping и адаптерами; - запись invocation events;
- выдача нормализованного результата. - возврат нормализованного результата.
Структура: ### 4.7. Protocol adapters
```text - `crank-adapter-rest`
crank-runtime/ - `crank-adapter-graphql`
src/ - `crank-adapter-grpc`
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
```
Описание: Каждый adapter знает только свой протокол.
- `executor/operation_executor.rs` - основной orchestration use case. ### 4.8. `apps/admin-api`
- `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 представление операции.
Правило: Должен содержать сервисные группы:
`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 не превращать `mcp-server` во второй `admin-api`.
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` останутся тонкими входными слоями;
- добавление нового протокола не потребует переписывать половину проекта.
Это и есть целевая архитектурная дисциплина проекта: отдельные слои, отдельные модели, минимально необходимая публичность и отсутствие "универсальных" структур, в которые со временем начинает стекаться вся система.