# 0189 — The framework reports nothing on its own

> Source: https://github.com/max-listov/stitchkit/blob/master/docs/decisions/0189-the-framework-reports-nothing-on-its-own.md

---
# 0189 — The framework reports nothing on its own

**Status:** Accepted
**Date:** 2026-09-22

## Context

A generation tool answers with an id and the waiting is a separate call, bounded
at ten minutes. In a host with widgets that is tolerable — the widget polls and
the person sees a card. In a text host it is silence: the tool is called, nothing
comes back for a minute or ten, and then either the result or a timeout, after
which the model calls again. Neither the person nor the model can tell a queued
job from a stalled provider, while the server knows exactly which it is.

The protocol has the channel. A host sends a `progressToken` in the call's
`_meta` and the server relates `notifications/progress` to that request. The
framework read neither end, so `packages/core/src` contained no occurrence of
either word.

## Decision

`ctx.reportProgress` on every tool call, and **nothing automatic**.

- Present on MCP, agent and CLI calls, a no-op wherever nobody is listening, so
  a handler never branches on transport to say what it is doing. Absent on HTTP,
  which has no channel for it.
- Never throws, never rejects. A message about work must not be able to kill the
  work it describes; a refused notification leaves the host exactly where a host
  that never asked for progress already is.
- `progress` is required by the protocol and is usually the thing a handler does
  not have: it has a stage to name and no scale to name it on. Omitted, the
  ordinal of the update is sent, with no `total`. That is a fact about what
  happened. A synthesised percentage would be a claim nobody measured, and a bar
  stuck at 3% is worse than a counter.
- The framework sends nothing by itself. Progress is meaningful only where an
  operation has observable stages, and manufacturing one per call would be
  inventing a second transport beside the one the protocol provides.

**The one exception, and why it is not a contradiction.** `mountWait` reports
each poll: the phase the last snapshot declared, and that snapshot's own numeric
`progress` when it has one. It is not guessing stages — it already polls and
already holds the answer, and it is precisely the tool whose silence raised the
question. Leaving it out would have left every consumer wiring the same relay by
hand, which is a consumer workaround for a mechanism we own.

## Consequences

`progressToken` is on `McpCallContext` as data; the reporting function is at the
root of the call context and deliberately not on that type, because
`McpCallContext` is also the declared type of `RequestEvent.mcp` and an audit
row's shape must not claim to carry behaviour.

A host that asked for nothing gets byte-for-byte what it got before, including
a plain JSON response rather than a stream. That is asserted, not assumed.
