@@ -3884,6 +3884,44 @@ This enables framework-specific context creation (e.g., extracting Identity from
38843884
38853885The lifecycle is: request arrives → ContextFactory.create_context(request) → Executor.call(module_id, inputs, context) → response.
38863886
3887+ #### Streaming Execution Protocol
3888+
3889+ Modules MAY support incremental output by implementing a ` stream ` method alongside the required ` execute ` method:
3890+
3891+ ```
3892+ stream(inputs, context) → AsyncIterable<Record>
3893+ ```
3894+
3895+ ** Semantics:**
3896+
3897+ - Each yielded record is a partial result chunk; the framework does not prescribe chunk structure.
3898+ - The complete result is the shallow merge of all yielded chunks (left-to-right object spread).
3899+ - ` execute() ` MUST remain implemented as the non-streaming fallback.
3900+ - Module descriptors SHOULD declare ` annotations.streaming = true ` when ` stream() ` is provided.
3901+
3902+ ** Executor.stream() pipeline:**
3903+
3904+ 1 . Steps 1–6 identical to ` call() ` : context creation, safety checks, module lookup, ACL, input validation, before-middleware.
3905+ 2 . If module lacks ` stream() ` : fall back to ` call() ` , yield single chunk, return.
3906+ 3 . Iterate ` module.stream(inputs, context) ` , yield each chunk to caller.
3907+ 4 . After all chunks: validate accumulated output against ` output_schema ` , run after-middleware on accumulated result.
3908+
3909+ ** Cross-language signatures:**
3910+
3911+ | Language | Executor method signature |
3912+ | ------------| ------------------------------------------------------------------------------------|
3913+ | TypeScript | ` async *stream(moduleId, inputs?, context?): AsyncGenerator<Record<string, unknown>> ` |
3914+ | Python | ` async def stream(module_id, inputs?, context?) -> AsyncIterator[dict[str, Any]] ` |
3915+
3916+ ** MCP bridge behavior:**
3917+
3918+ When bridging ` Executor.stream() ` to MCP, implementations SHOULD use the standard ` notifications/progress ` mechanism:
3919+
3920+ 1 . Client includes ` _meta.progressToken ` in the ` tools/call ` request to opt into streaming.
3921+ 2 . Server calls ` Executor.stream() ` and for each yielded chunk, sends ` notifications/progress ` with ` message ` containing the JSON-serialized chunk.
3922+ 3 . The final ` CallToolResult ` contains the complete accumulated result.
3923+ 4 . If the client does not provide ` progressToken ` , the bridge accumulates internally and returns an atomic result.
3924+
38873925### 11.3 Cross-language Implementation Requirements
38883926
38893927| Requirement | Python | Rust | Go | Java | TypeScript |
0 commit comments