Call a slow MCP tool and most clients show you the same thing: a spinner, with no idea whether the server is 10% through a crawl or stuck. MCP progress notifications are the protocol’s answer — a way for whichever side is doing the work to stream small status updates back while a request is still in flight, instead of the caller sitting in silence until the final response arrives. It’s one of the older, quieter utilities in MCP, and it solves a narrower problem than it sounds like: it doesn’t make anything finish faster, and it isn’t the same tool as MCP Tasks, even though both show up in the “my tool call is slow” conversation.

How it actually works

Progress tracking is opt-in per request. When a client sends a request it wants progress on — a tool call, a resource read, anything — it attaches a progressToken inside that request’s _meta field. That token is just an identifier the client made up; the server didn’t need to hand one out first. If the request doesn’t carry a token, the receiver has nothing to attach updates to and shouldn’t send any.

From there, the side doing the work can send as many notifications/progress messages as it wants before the final result goes back. Each one carries the matching progressToken, a progress number, and two optional fields: total, if the server can estimate the full scope of the work, and message, a short human-readable status line like “fetched 40 of roughly 120 pages.” The spec is specific about one rule: progress has to increase with every notification for the same token, even when there’s no total to compute a percentage against. A client can treat a stalled or repeated number as a sign something’s wrong.

Notice what’s missing from that description: no new capability to declare at initialization, no separate request type to poll. It rides on the same connection as the request it’s describing, as a side channel of notifications the caller can choose to display or ignore. Both ends are expected to tolerate the other side not supporting it at all — a server can send progress notifications nobody reads, and a client can request a token that never gets used, and neither is an error.

Progress vs. Tasks: not the same fix

It’s easy to lump this in with MCP Tasks, the start-check-fetch pattern introduced for long-running work, because both exist because some tool calls take a while. They solve different halves of that problem. Progress notifications keep you company on a request that’s still open — the connection stays live, the caller is still waiting, and the updates just make the wait legible instead of blind. Tasks exist so the caller doesn’t have to keep the connection open at all: kick off the work, get a reference back, and check on it later, possibly from a different connection entirely.

In practice that makes them complementary rather than competing. A task that takes minutes can still emit progress updates while it runs, for whoever happens to be polling it at the time. A tool call that finishes in ten seconds has no real use for Tasks’ overhead, but might still benefit from a couple of progress notifications so the model — or whatever UI is showing the call to a person — doesn’t read as frozen for that whole ten seconds.

What it doesn’t give you

Progress notifications are informational only. There’s no way for the caller to respond to one, no built-in way to cancel from inside the same channel, and no guarantee the numbers mean anything precise — a progress of 40 against a total of 120 is whatever the server decided those numbers mean, not a protocol-enforced unit. A server that reports progress as “files processed” and one that reports it as “bytes downloaded” are both spec-compliant, and a client can’t tell which it’s looking at without the message field spelling it out. Treat the numbers as a heartbeat and a rough sense of scale, not a reliable progress bar.

If you build a server

Progress reporting is worth adding to any tool that realistically takes more than a couple of seconds and has a natural way to measure partial completion — pages crawled, rows processed, chunks uploaded. It’s not worth wrapping around a tool that either finishes fast or has no meaningful notion of partial progress; a notification that just says “still working” over and over adds noise without adding information. The spec also expects senders to rate-limit these rather than firing one per row processed in a million-row job — batch the updates so they’re useful, not a firehose.

If you’re generating a server from an existing API with a tool like gate’s MCP server builder, this is another piece an OpenAPI spec has no way to express on its own — an endpoint definition doesn’t say whether the underlying operation is slow enough to deserve progress reporting, or what unit of progress would even make sense for it. That’s a judgment call layered on top of the generated tool surface, the same way we’ve noted for Tasks and sampling. See turning an API into an MCP server for the baseline path most endpoints take without any of this.

Where a gateway fits: a progress notification is metadata about a tool call, not the call itself, so it doesn’t change what the underlying tool is allowed to touch. The per-tool allow, ask, or block rules in a gateway apply the same way whether a call is silent or streams six status updates on the way to its result — and a security scan should still cover what the tool does when it finishes, not just how chatty it is while running. gate’s free MCP security scanner checks the former; a progress bar is not a substitute for it.

The bottom line

Progress notifications let a server stream small, optional status updates on a request that’s still open, keyed to a token the caller supplied up front. They make a slow call feel less like a hang, they’re not a capability either side has to support, and they solve a different problem than Tasks does — company while you wait, not a way to stop waiting. See gate’s MCP server directory for what today’s servers actually implement.