703 lines
19 KiB
Markdown
703 lines
19 KiB
Markdown
# 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 режимах;
|
||
- WebSocket в bounded режимах.
|
||
|
||
Отложено:
|
||
|
||
- GraphQL `subscription`;
|
||
- gRPC client-streaming;
|
||
- gRPC bidirectional streaming;
|
||
- arbitrary websocket passthrough;
|
||
- raw infinite stream forwarding в MCP client.
|
||
|
||
### 3.3. Protocol capability matrix
|
||
|
||
| Protocol | Unary | Window | Session | Async Job | Notes |
|
||
| --- | --- | --- | --- | --- | --- |
|
||
| REST | Yes | Yes | Limited | Yes | SSE and long-poll sources are supported in controlled form |
|
||
| GraphQL | Yes | No | No | Limited | `query` and `mutation` only; `subscription` is future scope |
|
||
| gRPC | Yes | Yes | Yes | Yes | server-streaming only; client/bidi deferred |
|
||
| WebSocket | No | Yes | Yes | Yes | upstream adapter only; not downstream MCP transport |
|
||
| SOAP | Yes | Limited | Limited | Yes | primarily request-response enterprise workflows |
|
||
|
||
## 4. Поддерживаемые streaming modes
|
||
|
||
Crank поддерживает четыре режима выполнения operation.
|
||
|
||
### 4.1. `unary`
|
||
|
||
Обычный request-response вызов.
|
||
|
||
Подходит для:
|
||
|
||
- REST;
|
||
- GraphQL `query` и `mutation`;
|
||
- gRPC unary;
|
||
- SOAP.
|
||
|
||
### 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;
|
||
- WebSocket event window.
|
||
|
||
### 4.3. `session`
|
||
|
||
Runtime создает stream session, после чего данные читаются по шагам через session-oriented tool family.
|
||
|
||
Обязательные операции:
|
||
|
||
- `start`
|
||
- `poll`
|
||
- `stop`
|
||
|
||
Подходит для:
|
||
|
||
- follow logs;
|
||
- telemetry follow;
|
||
- alert/event feed;
|
||
- контроль длительных stream-подписок;
|
||
- WebSocket subscriptions.
|
||
|
||
### 4.4. `async_job`
|
||
|
||
Runtime запускает long-running upstream operation и возвращает `job_id`.
|
||
|
||
Обязательные операции:
|
||
|
||
- `start`
|
||
- `status`
|
||
- `result`
|
||
- `cancel`
|
||
|
||
Подходит для:
|
||
|
||
- import/export;
|
||
- deploy/reindex;
|
||
- batch processing;
|
||
- инфраструктурные control-plane действия;
|
||
- SOAP workflows with deferred status polling.
|
||
|
||
## 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`.
|
||
|
||
### 5.6. WebSocket Realtime Feeds
|
||
|
||
LLM запрашивает:
|
||
|
||
- realtime alert snapshot;
|
||
- device telemetry window;
|
||
- market data slice;
|
||
- status feed по subscription channel.
|
||
|
||
Runtime:
|
||
|
||
- открывает upstream WebSocket;
|
||
- подписывается на channel;
|
||
- собирает bounded окно или session step;
|
||
- возвращает summary и limited items.
|
||
|
||
### 5.7. SOAP Enterprise Operations
|
||
|
||
LLM запрашивает:
|
||
|
||
- создание/поиск сущности в ERP;
|
||
- запуск enterprise workflow;
|
||
- получение статуса batch operation;
|
||
- B2B request через SOAP gateway.
|
||
|
||
Runtime:
|
||
|
||
- строит SOAP envelope из MCP input;
|
||
- вызывает enterprise endpoint;
|
||
- нормализует XML response или SOAP Fault;
|
||
- возвращает JSON-oriented output.
|
||
|
||
## 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 допускается как следующая волна, но не является обязательной в первой реализации.
|
||
|
||
### 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` отложен на отдельную protocol wave.
|
||
|
||
gRPC:
|
||
|
||
- unary;
|
||
- bounded server-stream collection;
|
||
- client/bidi отложены на отдельную protocol wave.
|
||
|
||
WebSocket:
|
||
|
||
- bounded event collection;
|
||
- subscribe/poll/stop orchestration;
|
||
- heartbeat and reconnect policy.
|
||
|
||
SOAP:
|
||
|
||
- WSDL-driven request/response adapter;
|
||
- XML normalization;
|
||
- SOAP Fault normalization;
|
||
- future WS-Security expansion.
|
||
|
||
### 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. Границы текущей продуктовой волны
|
||
|
||
В первой продуктовой волне входит:
|
||
|
||
- `Streamable HTTP` и SSE на MCP transport;
|
||
- `window` mode;
|
||
- `async_job` mode;
|
||
- REST SSE;
|
||
- gRPC server streaming;
|
||
- tool family generation;
|
||
- bounded session/job state.
|
||
|
||
Во второй продуктовой волне:
|
||
|
||
- WebSocket upstream adapter;
|
||
- SOAP adapter foundation;
|
||
- richer session tooling;
|
||
- expanded protocol smoke suite.
|
||
|
||
Отложено:
|
||
|
||
- 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/websocket-upstream-adapter`
|
||
|
||
- bounded WebSocket collection;
|
||
- subscribe/unsubscribe templates;
|
||
- heartbeat/reconnect policy;
|
||
- session integration.
|
||
|
||
### 13.10. `feat/soap-architecture-and-core-model`
|
||
|
||
- WSDL/XSD-driven domain model;
|
||
- SOAP execution config;
|
||
- XML normalization strategy.
|
||
|
||
### 13.11. `feat/soap-adapter-foundation`
|
||
|
||
- runtime SOAP adapter;
|
||
- envelope builder;
|
||
- fault normalization;
|
||
- test-run support.
|
||
|
||
### 13.12. `feat/streaming-ui-config`
|
||
|
||
- новый execution mode selector;
|
||
- limits/aggregation/safety blocks;
|
||
- test-run UX.
|
||
|
||
### 13.13. `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.
|