Initialize project scaffold and domain model

This commit is contained in:
a.tolmachev
2026-03-25 12:20:42 +03:00
commit fb302b2a2c
51 changed files with 6815 additions and 0 deletions
+696
View File
@@ -0,0 +1,696 @@
# Модель данных
## 1. Назначение документа
Этот документ фиксирует формальную модель данных платформы. Его цель - определить такие структуры, которые можно без существенных изменений перенести в:
- Rust domain types,
- HTTP DTO,
- структуру таблиц БД,
- runtime-представление operation,
- UI-формы и конфигурационные экраны.
Документ не привязан к конкретной СУБД, но задает каноническую JSON-модель сущностей.
## 2. Общие принципы модели
### 2.1. Одна операция - один tool
Каждая `Operation` соответствует одному MCP tool. Это особенно важно для:
- GraphQL, где одна operation соответствует одному конкретному `query` или `mutation`;
- gRPC, где одна operation соответствует одному unary-методу;
- REST, где одна operation соответствует одному endpoint-сценарию.
### 2.2. Внутренний транспортный формат - JSON
Независимо от внешнего протокола внутри системы данные должны быть представлены в JSON-ориентированном виде. Даже если внешний вызов работает с protobuf, runtime, mapping и UI опираются на нормализованный JSON.
### 2.3. Mapping всегда явный
Даже если система умеет строить черновой mapping по примерам данных, итоговая конфигурация mapping должна быть явно сохранена в operation. Нельзя полагаться на неявную "магию" сопоставления во время выполнения.
### 2.4. JSONPath как единый язык адресации
Для input и output mapping используется `JSONPath`. Это позволяет единообразно ссылаться на вложенные поля во входе, промежуточном представлении запроса и нормализованном ответе.
### 2.5. YAML как формат обмена конфигурацией
Помимо канонической JSON-модели система должна поддерживать импорт и экспорт конфигураций в `YAML`. Это внешний формат обмена, а не отдельная доменная модель.
## 3. Корневая сущность `Operation`
`Operation` - основная конфигурационная сущность платформы.
### Поля
- `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` - опциональные метаданные экспортируемой конфигурации.
- `created_at`
- `updated_at`
- `published_at`
### Пример
```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
}
```
## 4. `Target`
`Target` описывает конкретный внешний вызов. Это discriminated union по протоколу.
### 4.1. `RestTarget`
```json
{
"kind": "rest",
"base_url": "https://api.example.com",
"method": "PATCH",
"path_template": "/v1/users/{userId}",
"static_headers": {
"X-App-Source": "mcpaas"
}
}
```
Поля:
- `kind`
- `base_url`
- `method`
- `path_template`
- `static_headers`
### 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`
- `operation_name`
- `query_template`
- `response_path`
### 4.3. `GrpcTarget`
```json
{
"kind": "grpc",
"server_addr": "https://grpc.example.com:443",
"package": "crm.v1",
"service": "LeadService",
"method": "CreateLead",
"descriptor_ref": "desc_01hr7yn4d6g1x6vwt7h9n0e7ab"
}
```
Поля:
- `kind`
- `server_addr`
- `package`
- `service`
- `method`
- `descriptor_ref`
## 5. `Schema`
`Schema` - нормализованное описание входа или выхода. Это не JSON Schema в полном объеме, а внутренняя структурная модель, удобная для UI и runtime.
### Базовая форма
```json
{
"type": "object",
"description": "Lead input",
"fields": {
"name": {
"type": "string",
"required": true,
"description": "Lead full name"
},
"tags": {
"type": "array",
"required": false,
"items": {
"type": "string"
}
}
}
}
```
### Поддерживаемые типы
- `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
}
}
}
}
```
## 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.*`
### `Transform`
Для MVP transformations должны быть ограничены:
- `identity`
- `to_string`
- `to_number`
- `to_boolean`
- `join`
- `split`
- `wrap_array`
- `unwrap_singleton`
Пример:
```json
{
"kind": "to_string"
}
```
## 7. `ExecutionConfig`
`ExecutionConfig` задает параметры выполнения operation.
```json
{
"timeout_ms": 10000,
"retry_policy": {
"max_attempts": 1
},
"auth_profile_ref": "auth_01hr7x8rj2d8nq8v0c4m4t1r9e",
"headers": {
"X-Client": "mcpaas"
},
"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 в `mcpaas-core`, `mcpaas-schema`, `mcpaas-mapping`;
- DTO для `admin-api`;
- таблиц `operations`, `operation_versions`, `operation_samples`, `operation_descriptors`;
- import/export layer для `YAML` конфигураций;
- runtime view, который будет передаваться в `mcpaas-runtime`.
Следующим логическим шагом после этого документа должна стать схема БД, в которой эти сущности будут разложены по таблицам и связям.