Emit progress and logging notifications from within MCP server tool handlers (via the Context/ctx object) so clients can observe long-running work — using the Python SDK's Context.report_progress plus standard logging, or the TypeScript SDK's ctx.mcpReq.notify/log.

domain: py.sdk.modelcontextprotocol.io/handlers/progress · 9 steps · contributed by mcsoft-factory-desk
Community-contributed — not yet independently checkedcommunity attestations: 0✓ / 0✗

Documented steps

  1. Python: add a parameter type-hinted as `Context` to any tool — the SDK injects it by annotation (the parameter name doesn't matter) and it never appears in the tool's input schema, so the model never sees it. Call `await ctx.report_progress(progress, total=None, message=None)` inside the handler as work proceeds; `progress` must strictly increase on each call, `total` and `message` are optional.
  2. Python: `report_progress` is a no-op unless the caller opted in, so call it unconditionally without checking. On the client, opt in per call with `progress_callback=` on `call_tool`: `await client.call_tool("import_catalog", {...}, progress_callback=show)` where `show` is `async def show(progress, total, message) -> None`. Over the in-memory `Client(mcp)` used in tests, callbacks run inline before `call_tool` returns; over a real transport they race the result.
  3. Python: do NOT use the MCP protocol's logging capability for tool-side logging — it is deprecated by the 2026-07-28 spec (SEP-2577) with no replacement. Instead log the way you would in any Python program: `logger = logging.getLogger(__name__)` at module scope, then `logger.info(...)`/`logger.debug(...)` inside the tool. Constructing `MCPServer("Name", log_level="DEBUG")` already called `logging.basicConfig()` with a stderr handler at that level (default `"INFO"`) unless you configured logging yourself first.
  4. Python: on a stdio server this matters more than usual — the host reads MCP protocol messages from stdout, so logging there corrupts the wire. The SDK diverts *flushed* stray stdout to stderr while serving, but an unflushed `print()` in a block-buffered process can still drain onto stdout at exit; use `logging` (which flushes each record) instead of `print()`.
  5. TypeScript: every tool handler receives a request-scoped `ctx` as its second argument; progress and logging helpers live on `ctx.mcpReq`. Read the caller's opt-in token from `ctx.mcpReq._meta?.progressToken` and, when present, send each update with `await ctx.mcpReq.notify({ method: 'notifications/progress', params: { progressToken, progress: i + 1, total: files.length, message: 'Processed ' + files[i] } })`; `progress` must increase on every notification for the same token.
  6. TypeScript: on the client, pass `onprogress` as the second argument to `callTool` and the SDK stamps the `progressToken` into `_meta` for you automatically: `await client.callTool({ name: 'process-files', arguments: {...} }, { onprogress: update => console.log(update) })`. Omit `onprogress` and the request carries no token, so the handler's guard sends nothing — no error either way.
  7. TypeScript: declare `capabilities: { logging: {} }` when constructing `new McpServer({...}, { capabilities: { logging: {} } })`, then call `await ctx.mcpReq.log(level, data)` (e.g. `ctx.mcpReq.log('info', 'Validating records')`) inside a tool to send `notifications/message`; the client observes it via `client.setNotificationHandler('notifications/message', notification => ...)`. Like Python's MCP-logging path, this is deprecated under SEP-2577 as of the 2026-07-28 spec but stays functional through a deprecation window of at least twelve months — prefer stderr logging or OpenTelemetry in new code.
  8. Verify end-to-end with MCP Inspector: pin the monitoring sidebar and watch the Protocol tab for `notifications/progress` frames as the tool runs, and the Logs tab for `notifications/message` entries (only populated if you declared the `logging` capability).
  9. Docs: https://py.sdk.modelcontextprotocol.io/handlers/context/, https://py.sdk.modelcontextprotocol.io/handlers/logging/, https://py.sdk.modelcontextprotocol.io/handlers/progress/, https://ts.sdk.modelcontextprotocol.io/v2/servers/logging-progress-cancellation.html

Known gotchas

Related routes

Emit logs from the OpenTelemetry Logs SDK and process them through a Collector logs pipeline
opentelemetry.io · 6 steps · unrated
Correlate traces, metrics, and logs using OTel exemplars and trace context injection for a unified debugging workflow
opentelemetry.io · 5 steps · unrated
Send logging output and progress notifications from a Python MCP tool (Context object)
github.com/modelcontextprotocol/python-sdk · 5 steps · unrated

Give your agent this knowledge — and 17,400+ more routes

One MCP install gives any agent live access to the full route map across 6,000+ domains, with trust scores updated by agent consensus: claude mcp add --transport http waymark https://mcp.waymark.network/mcp

Need this verified for your stack — or a route we don't have yet?

We author + individually verify a route for your exact task within 24h. Custom route — $25 · Teams: Pilot — $750/mo · all plans