docs: define streaming mcp architecture

This commit is contained in:
a.tolmachev
2026-04-06 01:45:48 +03:00
parent d7e5ae95d6
commit 04ed704e94
12 changed files with 834 additions and 29 deletions
+3 -1
View File
@@ -20,7 +20,8 @@ Crank - платформа для публикации внешних API в в
- `Agent` как curated MCP surface для LLM. - `Agent` как curated MCP surface для LLM.
- Поддержка REST для `GET`, `POST`, `PUT`, `PATCH` и `DELETE`. - Поддержка REST для `GET`, `POST`, `PUT`, `PATCH` и `DELETE`.
- Поддержка GraphQL для `query` и `mutation`. - Поддержка GraphQL для `query` и `mutation`.
- Поддержка только unary-методов gRPC. - Поддержка unary и bounded server-streaming для gRPC.
- Поддержка controlled streaming modes поверх MCP `Streamable HTTP`.
- Platform API keys и membership layer. - Platform API keys и membership layer.
- Observability: invocation logs, usage aggregates, latency/error metrics. - Observability: invocation logs, usage aggregates, latency/error metrics.
- Импорт и экспорт operation-конфигураций в `YAML`. - Импорт и экспорт operation-конфигураций в `YAML`.
@@ -45,6 +46,7 @@ Crank - платформа для публикации внешних API в в
- `docs/demo-runbook.md` - демонстрационный сценарий. - `docs/demo-runbook.md` - демонстрационный сценарий.
- `docs/public-smoke-targets.md` - готовые публичные upstream-сервисы и payload-ы для smoke-проверки MCP. - `docs/public-smoke-targets.md` - готовые публичные upstream-сервисы и payload-ы для smoke-проверки MCP.
- `docs/secrets-auth-plan.md` - целевая модель upstream secrets, auth profiles и пошаговый план реализации. - `docs/secrets-auth-plan.md` - целевая модель upstream secrets, auth profiles и пошаговый план реализации.
- `docs/streaming-mcp-plan.md` - целевая модель MCP transport streaming, upstream streaming и поэтапный план реализации.
- `docs/rust-design.md` - правила распределения поведения в Rust. - `docs/rust-design.md` - правила распределения поведения в Rust.
- `docs/development-rules.md` - правила разработки и workflow. - `docs/development-rules.md` - правила разработки и workflow.
- `docs/rust-code-rules.md` - Rust-specific coding rules. - `docs/rust-code-rules.md` - Rust-specific coding rules.
+15 -7
View File
@@ -2,23 +2,31 @@
## Current ## Current
### `feat/secret-store-foundation` ### `feat/streaming-mcp-architecture`
Status: completed Status: completed
DoD: DoD:
- Workspace-scoped secret store is persisted in PostgreSQL - Official MCP transport semantics are reflected in docs
- Secret values are encrypted with `CRANK_MASTER_KEY` - Streaming modes and protocol support matrix are documented
- Admin API exposes create/list/get/rotate/delete secret endpoints - Core docs and `TASKS.md` are synchronized around controlled streaming model
- Integration tests cover secret CRUD and rotation
## Next ## Next
- `feat/auth-profile-secret-resolution` - `feat/mcp-streamable-http-alignment`
## Backlog ## Backlog
- `feat/secret-store-foundation` - `feat/streaming-mcp-architecture`
- `feat/mcp-streamable-http-alignment`
- `feat/streaming-core-model`
- `feat/stream-session-store`
- `feat/runtime-window-mode`
- `feat/rest-sse-adapter`
- `feat/grpc-server-streaming-adapter`
- `feat/session-and-job-tools`
- `feat/streaming-ui-config`
- `feat/streaming-e2e`
- `feat/auth-profile-secret-resolution` - `feat/auth-profile-secret-resolution`
- `feat/runtime-upstream-auth` - `feat/runtime-upstream-auth`
- `feat/secrets-ui` - `feat/secrets-ui`
+17 -3
View File
@@ -130,6 +130,7 @@ Crank - платформа для публикации внешних API в в
- `Workspace` как tenant boundary. - `Workspace` как tenant boundary.
- Операции `REST`, `GraphQL`, `unary gRPC`. - Операции `REST`, `GraphQL`, `unary gRPC`.
- Controlled streaming operations поверх `Streamable HTTP`, REST SSE и gRPC server-streaming.
- `Agent` и привязка операций к агенту. - `Agent` и привязка операций к агенту.
- Agent-scoped MCP endpoints. - Agent-scoped MCP endpoints.
- Platform API keys. - Platform API keys.
@@ -141,7 +142,8 @@ Crank - платформа для публикации внешних API в в
### Не входит ### Не входит
- gRPC streaming. - GraphQL `subscription`.
- gRPC client-streaming и bidirectional streaming.
- SOAP. - SOAP.
- Оркестрация workflow. - Оркестрация workflow.
- Биллинг. - Биллинг.
@@ -201,10 +203,20 @@ GraphQL в MCP публикуется как фиксированная опер
### gRPC ### gRPC
- только unary RPC; - unary RPC;
- bounded server-streaming через `window`, `session` и `async_job` execution modes;
- `.proto` и `descriptor set`; - `.proto` и `descriptor set`;
- JSON-oriented schema model поверх protobuf; - JSON-oriented schema model поверх protobuf;
- без streaming. - без client-streaming и bidi.
### Streaming
Платформа поддерживает controlled streaming model:
- downstream transport: `Streamable HTTP` с optional SSE;
- upstream streaming: REST SSE и gRPC server-streaming;
- execution modes: `unary`, `window`, `session`, `async_job`;
- никакого raw infinite stream passthrough в MCP client.
## 8. Работа с файлами и автогенерация черновика ## 8. Работа с файлами и автогенерация черновика
@@ -234,6 +246,8 @@ GraphQL в MCP публикуется как фиксированная опер
- `Agent` - `Agent`
- `AgentVersion` - `AgentVersion`
- `AgentOperationBinding` - `AgentOperationBinding`
- `StreamSession`
- `AsyncJobHandle`
- `AuthProfile` - `AuthProfile`
- `PlatformApiKey` - `PlatformApiKey`
- `UserSession` - `UserSession`
+56 -1
View File
@@ -17,7 +17,7 @@
Каждая `Operation` соответствует одному интеграционному контракту: Каждая `Operation` соответствует одному интеграционному контракту:
- GraphQL -> один конкретный `query` или `mutation`; - GraphQL -> один конкретный `query` или `mutation`;
- gRPC -> один unary method; - gRPC -> один unary method или один server-streaming method в bounded execution mode;
- REST -> один endpoint-сценарий. - REST -> один endpoint-сценарий.
Однако MCP tool публикуется не напрямую из operation, а через `AgentOperationBinding` внутри конкретного `Agent`. Однако MCP tool публикуется не напрямую из operation, а через `AgentOperationBinding` внутри конкретного `Agent`.
@@ -54,6 +54,16 @@
Помимо канонической JSON-модели система поддерживает импорт и экспорт конфигураций в `YAML`. Помимо канонической JSON-модели система поддерживает импорт и экспорт конфигураций в `YAML`.
### 2.7. Streaming operation обязана быть bounded
Если операция использует upstream streaming, она должна работать в одном из execution modes:
- `window`
- `session`
- `async_job`
Бесконечный passthrough stream не является допустимой моделью `Operation`.
## 3. Корневые сущности ## 3. Корневые сущности
### 3.1. `Workspace` ### 3.1. `Workspace`
@@ -96,6 +106,7 @@
- `samples` - `samples`
- `generated_draft` - `generated_draft`
- `config_export` - `config_export`
- `streaming_config`
- `created_at` - `created_at`
- `updated_at` - `updated_at`
- `published_at` - `published_at`
@@ -314,6 +325,50 @@
- `p95_ms` - `p95_ms`
- `p99_ms` - `p99_ms`
### 3.16. `StreamSession`
Поля:
- `id`
- `workspace_id`
- `agent_id`
- `operation_id`
- `mode`
- `status`
- `cursor`
- `state`
- `expires_at`
- `last_poll_at`
- `created_at`
- `closed_at`
Назначение:
- хранение bounded session state для streaming tools;
- поддержка `start/poll/stop`;
- cleanup orphaned и expired sessions.
### 3.17. `AsyncJobHandle`
Поля:
- `id`
- `workspace_id`
- `agent_id`
- `operation_id`
- `status`
- `progress`
- `result`
- `error`
- `expires_at`
- `created_at`
- `updated_at`
- `finished_at`
Назначение:
- поддержка `start/status/result/cancel` для long-running upstream actions.
## 4. `Target` ## 4. `Target`
`Target` описывает конкретный внешний вызов. Это discriminated union по протоколу. `Target` описывает конкретный внешний вызов. Это discriminated union по протоколу.
+42
View File
@@ -49,6 +49,8 @@
- `agent_operation_bindings` - `agent_operation_bindings`
- `published_agents` - `published_agents`
- `platform_api_keys` - `platform_api_keys`
- `stream_sessions`
- `async_jobs`
- `invocation_logs` - `invocation_logs`
- `usage_rollups` - `usage_rollups`
- `yaml_import_jobs` - `yaml_import_jobs`
@@ -180,6 +182,46 @@
- `unique (workspace_id, name)` - `unique (workspace_id, name)`
### `stream_sessions`
- `id`
- `workspace_id`
- `agent_id`
- `operation_id`
- `mode`
- `status`
- `cursor_json`
- `state_json`
- `expires_at`
- `last_poll_at`
- `created_at`
- `closed_at`
Индексы:
- `(workspace_id, status, expires_at)`
- `(operation_id, status)`
### `async_jobs`
- `id`
- `workspace_id`
- `agent_id`
- `operation_id`
- `status`
- `progress_json`
- `result_json`
- `error_json`
- `expires_at`
- `created_at`
- `updated_at`
- `finished_at`
Индексы:
- `(workspace_id, status, expires_at)`
- `(operation_id, status)`
## 7. Workspaces and access layer ## 7. Workspaces and access layer
### `workspaces` ### `workspaces`
+14
View File
@@ -134,3 +134,17 @@ DoD:
- end-to-end demo воспроизводим; - end-to-end demo воспроизводим;
- deployment и healthchecks стабильно зелёные; - deployment и healthchecks стабильно зелёные;
- документация и продуктовый сценарий совпадают. - документация и продуктовый сценарий совпадают.
## 12. Этап 11. MCP streaming proxy support
Цель:
- довести Crank до controlled streaming model поверх MCP `Streamable HTTP`.
DoD:
- `mcp-server` соответствует transport semantics `2025-06-18`;
- execution modes `window`, `session`, `async_job` формально описаны и реализованы;
- REST SSE и gRPC server-streaming поддерживаются в bounded форме;
- UI умеет конфигурировать streaming limits, aggregation и lifecycle;
- e2e сценарии покрывают window/session/job calls.
+39
View File
@@ -11,6 +11,8 @@
Решение: Решение:
- основной transport: `Streamable HTTP`; - основной transport: `Streamable HTTP`;
- `POST` может завершаться `application/json` или `text/event-stream`;
- `GET` SSE stream поддерживается как server-to-client канал;
- отдельный `mcp-server` как сервис; - отдельный `mcp-server` как сервис;
- `stdio` не является обязательной частью текущего scope. - `stdio` не является обязательной частью текущего scope.
@@ -40,6 +42,7 @@
- загрузить published agents и их bindings из registry; - загрузить published agents и их bindings из registry;
- преобразовать их в MCP tool definitions; - преобразовать их в MCP tool definitions;
- вести `Mcp-Session-Id` и `MCP-Protocol-Version`;
- принимать вызовы tools от MCP clients; - принимать вызовы tools от MCP clients;
- валидировать вход; - валидировать вход;
- делегировать исполнение в runtime; - делегировать исполнение в runtime;
@@ -116,6 +119,12 @@
- `tools/list` - `tools/list`
- `tools/call` - `tools/call`
Дополнительно на transport уровне:
- `POST /mcp/v1/{workspace_slug}/{agent_slug}` как основной MCP endpoint;
- `GET /mcp/v1/{workspace_slug}/{agent_slug}` для SSE stream;
- `DELETE /mcp/v1/{workspace_slug}/{agent_slug}` для explicit session termination, если сервер разрешает client-side session close.
### Tool listing ### Tool listing
1. клиент вызывает `tools/list`; 1. клиент вызывает `tools/list`;
@@ -133,6 +142,34 @@
5. делегирует вызов в `crank-runtime`; 5. делегирует вызов в `crank-runtime`;
6. возвращает результат. 6. возвращает результат.
### Tool call и streaming
Поддерживаются четыре execution modes:
- `unary`
- `window`
- `session`
- `async_job`
Правила публикации:
- `unary` и `window` публикуются как один tool;
- `session` публикуется как `start/poll/stop` family;
- `async_job` публикуется как `start/status/result/cancel` family.
Transport-level SSE не отменяет bounded tool semantics. Даже если `POST` отвечает через `text/event-stream`, итогом вызова должен оставаться управляемый JSON-RPC lifecycle.
## 8.1. Multiple SSE connections
Crank должен корректно работать, если MCP client держит несколько SSE streams одновременно.
Правила:
- одно server message отправляется только в один stream;
- disconnect не считается cancel;
- cancel выражается отдельным MCP notification или session/job control tool;
- resumability допустима как будущая возможность, но не обязательна в MVP.
## 9. Обновление tools ## 9. Обновление tools
После публикации новой operation version или agent version: После публикации новой operation version или agent version:
@@ -171,7 +208,9 @@
- `mcp-server` - отдельный сервис; - `mcp-server` - отдельный сервис;
- transport - `Streamable HTTP`; - transport - `Streamable HTTP`;
- `POST` и `GET` transport semantics соответствуют MCP spec `2025-06-18`;
- endpoint определяется парой `workspace + agent`; - endpoint определяется парой `workspace + agent`;
- одна published operation = один MCP tool внутри agent; - одна published operation = один MCP tool внутри agent;
- streaming operations публикуются как bounded tools или tool families;
- reload published tools без пересборки сервиса; - reload published tools без пересборки сервиса;
- никакой draft-логики или admin CRUD в MCP слое. - никакой draft-логики или admin CRUD в MCP слое.
+7
View File
@@ -42,6 +42,7 @@ crank/
- secret management domain; - secret management domain;
- agent publishing domain; - agent publishing domain;
- observability domain. - observability domain.
- streaming execution domain.
## 4. Детальная декомпозиция по crate ## 4. Детальная декомпозиция по crate
@@ -64,6 +65,7 @@ crank/
- `auth` - `auth`
- `secret` - `secret`
- `observability` - `observability`
- `streaming`
- `errors` - `errors`
### 4.2. `crank-schema` ### 4.2. `crank-schema`
@@ -98,6 +100,7 @@ crank/
- хранение workspace-scoped operations и version snapshots; - хранение workspace-scoped operations и version snapshots;
- хранение workspace-scoped secrets и secret versions; - хранение workspace-scoped secrets и secret versions;
- хранение agents и agent versions; - хранение agents и agent versions;
- хранение stream sessions и async jobs;
- auth profiles; - auth profiles;
- platform API keys; - platform API keys;
- logs и usage aggregates; - logs и usage aggregates;
@@ -109,6 +112,7 @@ crank/
- исполнение published operation; - исполнение published operation;
- резолв `auth_profile_ref -> secret -> request auth`; - резолв `auth_profile_ref -> secret -> request auth`;
- orchestration window/session/job execution;
- запись invocation events; - запись invocation events;
- возврат нормализованного результата. - возврат нормализованного результата.
@@ -133,6 +137,7 @@ crank/
- `platform_api_keys` - `platform_api_keys`
- `logs` - `logs`
- `usage` - `usage`
- `streaming`
### 4.9. `apps/mcp-server` ### 4.9. `apps/mcp-server`
@@ -141,6 +146,8 @@ crank/
- публикация published agent bindings как MCP tools; - публикация published agent bindings как MCP tools;
- transport handling; - transport handling;
- JSON-RPC lifecycle; - JSON-RPC lifecycle;
- SSE lifecycle и `Mcp-Session-Id`;
- tool-family generation для `session` и `async_job`;
- вызов runtime. - вызов runtime.
Антипаттерн: Антипаттерн:
+3 -1
View File
@@ -30,6 +30,8 @@ GraphQL поддерживается как отдельный тип интег
- обязательная зависимость от introspection - обязательная зависимость от introspection
- автоматическое построение любого запроса по полной GraphQL schema - автоматическое построение любого запроса по полной GraphQL schema
`subscription` допускается только как future scope после появления отдельного websocket/subscription adapter и controlled streaming lifecycle.
## 4. Ключевое архитектурное ограничение ## 4. Ключевое архитектурное ограничение
Платформа не должна публиковать в MCP общий GraphQL tool, который умеет получать любые поля и принимать любые параметры в зависимости от намерения LLM. Платформа не должна публиковать в MCP общий GraphQL tool, который умеет получать любые поля и принимать любые параметры в зависимости от намерения LLM.
@@ -92,7 +94,7 @@ GraphQL operation должна включать:
- структура ответа зависит от `selection set`, значит она должна быть фиксирована заранее; - структура ответа зависит от `selection set`, значит она должна быть фиксирована заранее;
- GraphQL endpoint обычно один, поэтому операция определяется не URL, а телом запроса; - GraphQL endpoint обычно один, поэтому операция определяется не URL, а телом запроса;
- variables должны быть строго ограничены, иначе один tool станет слишком широким и плохо управляемым; - variables должны быть строго ограничены, иначе один tool станет слишком широким и плохо управляемым;
- `subscription` по смыслу не подходит модели MCP tool, потому что это потоковая, а не request-response интеграция. - `subscription` не входит в текущий scope, потому что требует отдельной lifecycle-модели, близкой к `session` mode, и отдельного transport adapter.
- `JSONPath` используется для точечного извлечения вложенных данных из `data` и для управления структурой итогового ответа. - `JSONPath` используется для точечного извлечения вложенных данных из `data` и для управления структурой итогового ответа.
## 9. Почему GraphQL не считается "почти REST" ## 9. Почему GraphQL не считается "почти REST"
+15 -16
View File
@@ -2,11 +2,12 @@
## 1. Роль протокола в проекте ## 1. Роль протокола в проекте
gRPC поддерживается как третий основной протокол платформы, но в самой узкой и управляемой форме. Цель состоит не в том, чтобы покрыть все возможности gRPC, а в том, чтобы представить unary RPC-методы как обычные MCP tools с формой входа и формой выхода. gRPC поддерживается как третий основной протокол платформы в управляемой форме. Цель состоит не в том, чтобы покрыть все возможности gRPC, а в том, чтобы представить unary и bounded server-streaming методы как MCP tools с предсказуемым жизненным циклом.
## 2. Что поддерживается в MVP ## 2. Что поддерживается в MVP
- только unary RPC - unary RPC
- bounded server-streaming через execution modes `window`, `session`, `async_job`
- загрузка `.proto` - загрузка `.proto`
- загрузка descriptor set - загрузка descriptor set
- загрузка примеров JSON для MCP input/output при необходимости - загрузка примеров JSON для MCP input/output при необходимости
@@ -22,7 +23,6 @@ gRPC поддерживается как третий основной прот
## 3. Что не входит в MVP ## 3. Что не входит в MVP
- `server streaming`
- `client streaming` - `client streaming`
- `bidirectional streaming` - `bidirectional streaming`
- обязательная поддержка server reflection - обязательная поддержка server reflection
@@ -31,17 +31,15 @@ gRPC поддерживается как третий основной прот
## 4. Ключевое архитектурное ограничение ## 4. Ключевое архитектурное ограничение
В проекте поддерживаются только unary-методы, потому что MCP tool в этой архитектуре соответствует модели `один запрос -> один ответ`. В проекте поддерживаются unary-методы и bounded server-streaming, потому что MCP tool в этой архитектуре должен оставаться управляемым.
Это означает: Это означает:
- один request message; - один request message;
- один response message; - один bounded response или управляемая session/job-семантика;
- один завершенный вызов; - явно ограниченный lifecycle stream-сессии.
- отсутствие потоковых сообщений;
- отсутствие отдельного жизненного цикла stream-сессии.
Streaming gRPC не нужен для выбранной модели взаимодействия с LLM и только усложнит runtime, UI и хранение состояния. Streaming gRPC не публикуется как бесконечный raw stream. Он допускается только там, где runtime умеет bounded-ить, агрегировать и завершать результат.
## 5. Внутренняя модель gRPC operation ## 5. Внутренняя модель gRPC operation
@@ -64,7 +62,7 @@ gRPC operation должна включать:
1. Загружает `.proto` или descriptor set. 1. Загружает `.proto` или descriptor set.
2. Система извлекает список services и methods. 2. Система извлекает список services и methods.
3. Оператор выбирает конкретный unary-метод. 3. Оператор выбирает конкретный unary- или server-streaming метод.
4. UI показывает структуру request message и response message. 4. UI показывает структуру request message и response message.
5. При необходимости загружает примеры JSON для MCP input/output. 5. При необходимости загружает примеры JSON для MCP input/output.
6. Система строит черновую схему, стартовый mapping и runtime-ready snapshot descriptor set для выбранного метода. 6. Система строит черновую схему, стартовый mapping и runtime-ready snapshot descriptor set для выбранного метода.
@@ -82,10 +80,11 @@ gRPC operation должна включать:
1. Валидировать MCP input по нормализованной схеме. 1. Валидировать MCP input по нормализованной схеме.
2. Применить input mapping. 2. Применить input mapping.
3. Построить protobuf request message из JSON. 3. Построить protobuf request message из JSON.
4. Выполнить unary RPC вызов. 4. Для unary выполнить unary RPC вызов.
5. Преобразовать protobuf response в нормализованный JSON. 5. Для server-streaming собрать bounded окно или session step.
6. Применить output mapping. 6. Преобразовать protobuf response или stream items в нормализованный JSON.
7. Вернуть итоговый результат. 7. Применить output mapping.
8. Вернуть итоговый результат.
## 8. Критические нюансы ## 8. Критические нюансы
@@ -99,7 +98,7 @@ gRPC operation должна включать:
- пользователь не должен видеть внутреннюю сложность protobuf-контракта больше, чем это нужно для настройки operation. - пользователь не должен видеть внутреннюю сложность protobuf-контракта больше, чем это нужно для настройки operation.
- `JSONPath` используется как единый способ точечной адресации вложенных полей при настройке mapping поверх нормализованной JSON-модели. - `JSONPath` используется как единый способ точечной адресации вложенных полей при настройке mapping поверх нормализованной JSON-модели.
## 9. Почему gRPC ограничивается unary ## 9. Почему gRPC ограничивается controlled streaming
Причина не только в сложности реализации. Главное ограничение архитектурное: Причина не только в сложности реализации. Главное ограничение архитектурное:
@@ -108,4 +107,4 @@ gRPC operation должна включать:
- UI платформы построен вокруг формы входа и формы выхода; - UI платформы построен вокруг формы входа и формы выхода;
- streaming требует отдельной session-модели, buffering, cancellation и состояния. - streaming требует отдельной session-модели, buffering, cancellation и состояния.
Поэтому unary gRPC - это не "обрезанная" поддержка, а осознанно выбранная форма, которая действительно совместима с MCP-платформой. Поэтому поддержка gRPC в Crank ограничивается unary и bounded server-streaming. Client-streaming и bidi остаются вне scope до появления полноценной interactive session model.
+8
View File
@@ -21,6 +21,7 @@ REST - базовый и первый по очередности реализа
- автогенерация чернового mapping - автогенерация чернового mapping
- ручная донастройка через `JSONPath` - ручная донастройка через `JSONPath`
- тестовый вызов перед публикацией - тестовый вызов перед публикацией
- optional REST SSE upstream в bounded `window` и `session` режимах
## 3. Что не входит в MVP ## 3. Что не входит в MVP
@@ -31,6 +32,7 @@ REST - базовый и первый по очередности реализа
- webhooks - webhooks
- long polling как специальный режим - long polling как специальный режим
- `HEAD` и `OPTIONS` как отдельные пользовательские сценарии - `HEAD` и `OPTIONS` как отдельные пользовательские сценарии
- raw infinite SSE passthrough
## 4. Внутренняя модель REST operation ## 4. Внутренняя модель REST operation
@@ -49,6 +51,12 @@ REST operation в системе описывается следующими о
На слое MCP REST operation всегда выглядит как вызов `запрос -> ответ` с фиксированной схемой входа и выхода. На слое MCP REST operation всегда выглядит как вызов `запрос -> ответ` с фиксированной схемой входа и выхода.
Если upstream использует SSE, на слое MCP это все равно должно быть выражено как:
- bounded window result;
- session-oriented `start/poll/stop`;
- async job semantics для длительных действий.
## 5. Как оператор настраивает REST operation ## 5. Как оператор настраивает REST operation
1. Указывает `base_url`. 1. Указывает `base_url`.
+615
View File
@@ -0,0 +1,615 @@
# Streaming MCP Plan
## 1. Назначение документа
Этот документ фиксирует целевую архитектуру потоковой обработки в Crank. Он описывает:
- какие transport- и upstream-протоколы поддерживаются;
- какие streaming-сценарии считаются допустимыми;
- какие ограничения обязательны для безопасности и управляемости;
- какие сущности, API, UI-поля и runtime-механизмы нужно добавить;
- в каком порядке это реализовывать.
Документ синхронизирован с MCP transport specification `2025-06-18`, где `Streamable HTTP` определен как основной HTTP transport, а SSE допускается как часть `POST`-response и как отдельный `GET`-stream.
## 2. Базовое архитектурное решение
Crank поддерживает streaming не как бесконечный текстовый поток в чат, а как управляемую инструментальную модель поверх MCP tools.
Принцип:
- transport может быть long-lived;
- tool contract обязан оставаться ограниченным и управляемым;
- upstream stream всегда преобразуется в bounded result, session poll или async job;
- оператор в UI настраивает не "трубу", а режим сбора, агрегации и завершения.
## 3. Поддерживаемые transport- и upstream-протоколы
### 3.1. Downstream MCP transport
Поддерживается:
- `Streamable HTTP` как канонический network transport;
- HTTP `POST` с ответом `application/json` или `text/event-stream`;
- HTTP `GET` для server-to-client SSE stream;
- несколько SSE streams одновременно в рамках одного MCP session;
- `Mcp-Session-Id` и `MCP-Protocol-Version` headers;
- resumable SSE streams как опциональная возможность.
Не является текущим приоритетом:
- `stdio` как обязательная часть продукта;
- собственные нестандартные transport-режимы поверх MCP.
### 3.2. Upstream protocols
Поддерживаются:
- REST unary;
- REST SSE в bounded режимах;
- GraphQL `query` и `mutation`;
- gRPC unary;
- gRPC server-streaming в bounded режимах.
Отложено:
- GraphQL `subscription`;
- gRPC client-streaming;
- gRPC bidirectional streaming;
- arbitrary websocket passthrough;
- raw infinite stream forwarding в MCP client.
## 4. Поддерживаемые streaming modes
Crank поддерживает четыре режима выполнения operation.
### 4.1. `unary`
Обычный request-response вызов.
Подходит для:
- REST;
- GraphQL `query` и `mutation`;
- gRPC unary.
### 4.2. `window`
Runtime открывает upstream stream или repeatedly polls upstream source, собирает данные в пределах окна и возвращает один bounded ответ.
Параметры:
- `window_duration_ms`
- `max_items`
- `max_bytes`
- `upstream_timeout_ms`
- `aggregation_mode`
Подходит для:
- логи за период;
- метрики за период;
- event window;
- SSE stream snapshot;
- gRPC server-stream window.
### 4.3. `session`
Runtime создает stream session, после чего данные читаются по шагам через session-oriented tool family.
Обязательные операции:
- `start`
- `poll`
- `stop`
Подходит для:
- follow logs;
- telemetry follow;
- alert/event feed;
- контроль длительных stream-подписок.
### 4.4. `async_job`
Runtime запускает long-running upstream operation и возвращает `job_id`.
Обязательные операции:
- `start`
- `status`
- `result`
- `cancel`
Подходит для:
- import/export;
- deploy/reindex;
- batch processing;
- инфраструктурные control-plane действия.
## 5. Бизнес-кейсы
### 5.1. Logs Window
LLM запрашивает:
- ошибки сервиса за последние `30s`;
- top errors за последние `100` записей;
- логи по конкретному `correlation_id`.
Runtime:
- собирает bounded окно;
- агрегирует counts, уровни, sample lines;
- возвращает summary плюс ограниченный список items.
### 5.2. Metrics Window
LLM запрашивает:
- latency/error summary по сервису;
- CPU/memory snapshot;
- anomaly summary за окно.
Runtime:
- читает поток метрик;
- агрегирует min/max/avg/p95 или anomaly set;
- возвращает компактный JSON.
### 5.3. Event Feed
LLM запрашивает:
- audit events за период;
- queue events по фильтру;
- security alerts за окно.
Runtime:
- фильтрует events;
- ограничивает количество;
- возвращает items plus cursor.
### 5.4. Long-running Operation Status
LLM запускает:
- reindex;
- import job;
- rollout;
- repair task.
Runtime:
- создает job handle;
- возвращает `job_id`;
- позволяет дальше получать status/result/cancel.
### 5.5. Control Plane Follow
LLM инициирует:
- reboot;
- rollout;
- node drain;
- workflow transition.
Runtime:
- стартует действие;
- пишет progress в session/job state;
- возвращает snapshots по `poll`.
## 6. Функциональные требования
### 6.1. Общие
- operation должна явно указывать `execution_mode`;
- streaming operation обязана быть bounded;
- runtime обязан поддерживать timeout, max items и max bytes;
- tool output обязан иметь предсказуемую схему;
- stream/session/job state должен быть наблюдаемым и логируемым;
- cancel/stop должен быть явной операцией, а не побочным эффектом disconnect.
### 6.2. Для `window`
- задать размер окна;
- задать лимит items;
- задать лимит bytes;
- выбрать aggregation mode;
- вернуть `truncated` и `window_complete` flags;
- поддерживать optional cursor для следующего окна.
### 6.3. Для `session`
- создать `session_id`;
- поддерживать `poll`;
- поддерживать `stop`;
- хранить курсор и session status;
- иметь `idle_timeout`;
- иметь `max_session_lifetime`;
- удалять expired sessions.
### 6.4. Для `async_job`
- создать `job_id`;
- хранить progress, status и final result metadata;
- поддерживать `cancel`;
- поддерживать retrieval последнего готового результата.
## 7. Нефункциональные требования
### 7.1. Безопасность
- никакого неограниченного passthrough потока;
- обязательные лимиты по времени, items и bytes;
- обязательный redact layer для secret-bearing полей;
- audit trail на `start`, `poll`, `stop`, `cancel`;
- session и job identifiers должны быть криптографически стойкими.
### 7.2. Производительность
- bounded memory per session;
- bounded upstream read buffer;
- ограничение числа параллельных sessions и jobs на workspace и agent;
- backpressure при медленных клиентах;
- возможность early cut-off после достижения лимита.
### 7.3. Надежность
- TTL и cleanup для sessions/jobs;
- устойчивость к disconnect downstream client;
- poll должен быть идемпотентным;
- long-running upstream action не должен считаться отмененным из-за SSE disconnect;
- resumability для downstream SSE допускается, но не является обязательной в MVP.
### 7.4. UX
- оператор должен видеть, что operation является `unary`, `window`, `session` или `async_job`;
- UI должен явно показывать все лимиты и режим агрегации;
- результат тестового вызова должен показывать `truncated`, `window_complete`, `has_more`, `status`.
## 8. UI contract
### 8.1. Новый блок `Execution mode`
Поля:
- `mode`: `unary | window | session | async_job`
- `transport_behavior`: `request_response | server_stream`
### 8.2. Блок `Collection limits`
Поля:
- `window_duration_ms`
- `poll_interval_ms`
- `upstream_timeout_ms`
- `idle_timeout_ms`
- `max_session_lifetime_ms`
- `max_items`
- `max_bytes`
### 8.3. Блок `Aggregation`
Поля:
- `aggregation_mode`: `raw_items | summary_only | summary_plus_samples | stats | latest_state`
- `summary_path`
- `items_path`
- `cursor_path`
- `status_path`
- `done_path`
### 8.4. Блок `Safety`
Поля:
- `truncate_item_fields`
- `max_field_length`
- `redacted_paths`
- `drop_duplicates`
- `sampling_rate`
### 8.5. Block `Tool family`
Для `session`:
- `start_tool_name`
- `poll_tool_name`
- `stop_tool_name`
Для `async_job`:
- `start_tool_name`
- `status_tool_name`
- `result_tool_name`
- `cancel_tool_name`
## 9. Domain model changes
### 9.1. `ExecutionMode`
Новый enum:
- `Unary`
- `Window`
- `Session`
- `AsyncJob`
### 9.2. `StreamingConfig`
Новая часть `execution_config`:
- `mode`
- `window_duration_ms`
- `poll_interval_ms`
- `upstream_timeout_ms`
- `idle_timeout_ms`
- `max_session_lifetime_ms`
- `max_items`
- `max_bytes`
- `aggregation_mode`
- `items_path`
- `summary_path`
- `cursor_path`
- `status_path`
- `done_path`
- `redacted_paths`
### 9.3. `StreamSession`
Новая runtime/store сущность:
- `id`
- `workspace_id`
- `agent_id`
- `operation_id`
- `protocol`
- `mode`
- `status`
- `cursor`
- `state_json`
- `expires_at`
- `last_poll_at`
- `created_at`
- `closed_at`
### 9.4. `AsyncJobHandle`
Новая runtime/store сущность:
- `id`
- `workspace_id`
- `agent_id`
- `operation_id`
- `status`
- `progress_json`
- `result_json`
- `error_json`
- `expires_at`
- `created_at`
- `updated_at`
- `finished_at`
## 10. MCP publishing model for streaming tools
### 10.1. Unary and Window
`unary` и `window` публикуются как один MCP tool:
- один input contract;
- один bounded result;
- transport может использовать `application/json` или SSE response stream до финального JSON-RPC response.
### 10.2. Session
`session` публикуется как tool family:
- `{tool}_start`
- `{tool}_poll`
- `{tool}_stop`
Причина:
- lifecycle становится явным;
- LLM получает контролируемую state machine;
- runtime не скрывает долговременное состояние за одним "магическим" вызовом.
### 10.3. Async Job
`async_job` публикуется как tool family:
- `{tool}_start`
- `{tool}_status`
- `{tool}_result`
- `{tool}_cancel`
## 11. Module decomposition and responsibilities
### 11.1. `crank-core`
Новые модули:
- `streaming`
- `stream_session`
Новые типы:
- `ExecutionMode`
- `StreamingConfig`
- `AggregationMode`
- `StreamSession`
- `AsyncJobHandle`
- `StreamStatus`
- `JobStatus`
### 11.2. `crank-registry`
Новые обязанности:
- хранение `stream_sessions`;
- хранение `async_jobs`;
- cleanup expired rows;
- optimistic updates on poll/stop/cancel.
Ожидаемые функции:
- `create_stream_session`
- `get_stream_session`
- `advance_stream_session`
- `close_stream_session`
- `create_async_job`
- `get_async_job`
- `update_async_job_status`
- `cancel_async_job`
- `delete_expired_stream_sessions`
### 11.3. `crank-runtime`
Новые orchestration функции:
- `execute_unary_operation`
- `execute_window_operation`
- `start_stream_session`
- `poll_stream_session`
- `stop_stream_session`
- `start_async_job`
- `get_async_job_status`
- `get_async_job_result`
- `cancel_async_job`
### 11.4. Protocol adapters
REST:
- unary HTTP;
- bounded SSE collection;
- bounded long-poll collection.
GraphQL:
- `query` и `mutation`;
- `subscription` не входит в MVP.
gRPC:
- unary;
- bounded server-stream collection;
- client/bidi не входят в MVP.
### 11.5. `apps/mcp-server`
Новые обязанности:
- корректно вести `Streamable HTTP` lifecycle;
- принимать `POST` с `Accept: application/json, text/event-stream`;
- отдавать `application/json` или `text/event-stream`;
- поддерживать `GET` SSE stream для server-to-client messages и notifications;
- вести `Mcp-Session-Id`;
- публиковать tool families для `session` и `async_job`.
### 11.6. `apps/admin-api`
Новые обязанности:
- CRUD и versioning для streaming config;
- тестовые window/session/job runs;
- UI-oriented validation ошибок для streaming fields.
### 11.7. `apps/ui`
- конфиг execution mode;
- конфиг limits/aggregation/safety;
- test-run screen для bounded window/session/job behavior;
- отдельные предупреждения про truncation и timeouts.
## 12. Ограничения MVP
В MVP входит:
- `Streamable HTTP` и SSE на MCP transport;
- `window` mode;
- `async_job` mode;
- REST SSE;
- gRPC server streaming;
- tool family generation;
- bounded session/job state.
В MVP не входит:
- GraphQL subscriptions;
- gRPC client streaming;
- gRPC bidirectional streaming;
- raw infinite stream passthrough;
- guaranteed resumability across all stream types;
- generic websocket proxy mode.
## 13. Порядок реализации
### 13.1. `feat/streaming-mcp-architecture`
- зафиксировать docs;
- обновить protocol support matrix;
- синхронизировать `TASKS.md`.
### 13.2. `feat/mcp-streamable-http-alignment`
- довести `mcp-server` до полного соответствия `Streamable HTTP`;
- session headers;
- GET SSE;
- protocol version header validation;
- explicit cancel behavior.
### 13.3. `feat/streaming-core-model`
- ввести `ExecutionMode`, `StreamingConfig`, `StreamSession`, `AsyncJobHandle`.
### 13.4. `feat/stream-session-store`
- таблицы `stream_sessions` и `async_jobs`;
- cleanup;
- optimistic state transitions.
### 13.5. `feat/runtime-window-mode`
- bounded collection для `window`;
- `truncated`, `window_complete`, `has_more`.
### 13.6. `feat/rest-sse-adapter`
- поддержка REST SSE upstream.
### 13.7. `feat/grpc-server-streaming-adapter`
- поддержка bounded gRPC server-streaming.
### 13.8. `feat/session-and-job-tools`
- генерация tool families;
- `start/poll/stop`;
- `start/status/result/cancel`.
### 13.9. `feat/streaming-ui-config`
- новый execution mode selector;
- limits/aggregation/safety blocks;
- test-run UX.
### 13.10. `feat/streaming-e2e`
- публичные smoke targets;
- e2e сценарии;
- manual regression plan.
## 14. Практический итог
Crank должен поддерживать streaming как полнофункциональный MCP proxy, но в управляемой форме:
- transport-level SSE и `Streamable HTTP` поддерживаются;
- upstream streaming поддерживается там, где его можно bounded-ить;
- tool contract остается контролируемым;
- UI настраивает лимиты, aggregation и lifecycle;
- платформа не превращается в бесконечную data pipe.