docs: redesign architecture around workspaces and agents
This commit is contained in:
@@ -2,9 +2,7 @@
|
||||
|
||||

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