Summary
Toolcraft commands currently model one finite request/response operation. progress() can emit diagnostics while a handler runs, but there is no typed, lifecycle-aware way for a command to produce an ongoing sequence of values across the CLI, SDK, and MCP surfaces.
This blocks integrations that need realtime watches or subscriptions from using the shared Toolcraft command tree.
Tested against toolcraft@0.0.102.
Use case
A device integration needs to subscribe to state changes and keep emitting typed events until the caller cancels or disconnects. Keeping this outside Toolcraft duplicates CLI signal handling, SDK cancellation, MCP lifecycle handling, schemas, and cleanup logic.
Proposed direction
Add a streaming command primitive, or allow a command handler to return a typed AsyncIterable/stream declaration with an event schema.
The surface should define:
- cancellation through an
AbortSignal or equivalent;
- deterministic cleanup when an SDK consumer stops iteration, a CLI receives
SIGINT, or an MCP client disconnects;
- bounded buffering and backpressure behavior;
- a schema for each emitted event;
- progress/reconnect/status events without conflating them with data events;
- error and terminal-event semantics;
- secret/credential refresh hooks for long-running sessions.
Expected surface behavior
- SDK: return an async iterable with cancellation and cleanup.
- CLI: render events until interrupted, with an NDJSON-compatible mode.
- MCP: use an explicit subscription/notification lifecycle rather than holding a normal tool response open indefinitely.
Acceptance criteria
- One command definition can expose a typed stream to supported surfaces.
- Cancellation releases handler resources exactly once.
- Slow consumers cannot create unbounded memory growth.
- Event schemas are available to adapters and tests.
- The testing harness can drive, cancel, and assert a finite prefix of a stream.
Summary
Toolcraft commands currently model one finite request/response operation.
progress()can emit diagnostics while a handler runs, but there is no typed, lifecycle-aware way for a command to produce an ongoing sequence of values across the CLI, SDK, and MCP surfaces.This blocks integrations that need realtime watches or subscriptions from using the shared Toolcraft command tree.
Tested against
toolcraft@0.0.102.Use case
A device integration needs to subscribe to state changes and keep emitting typed events until the caller cancels or disconnects. Keeping this outside Toolcraft duplicates CLI signal handling, SDK cancellation, MCP lifecycle handling, schemas, and cleanup logic.
Proposed direction
Add a streaming command primitive, or allow a command handler to return a typed
AsyncIterable/stream declaration with an event schema.The surface should define:
AbortSignalor equivalent;SIGINT, or an MCP client disconnects;Expected surface behavior
Acceptance criteria