Documentation
¶
Overview ¶
Package progress provides a Tracker for sending MCP progress notifications to the client during long-running tool operations.
SECURITY: Progress tokens are opaque values provided by the client. They are forwarded as-is — never logged at a level above Debug and never included in error messages returned to the caller.
SPEC: MCP 2025-11-25 requires progress values to strictly increase between notifications for the same token. The Tracker enforces this invariant by dropping non-monotonic Update calls (logged at Debug level), so misbehaving callers cannot violate the protocol contract.
Index ¶
- type Tracker
- func (t Tracker) Done(ctx context.Context, total float64, message string)
- func (t Tracker) IsActive() bool
- func (t Tracker) OnScale(base, outerTotal float64) Tracker
- func (t Tracker) Step(ctx context.Context, step, total int, message string)
- func (t Tracker) Update(ctx context.Context, progress, total float64, message string)
Constants ¶
This section is empty.
Variables ¶
This section is empty.
Functions ¶
This section is empty.
Types ¶
type Tracker ¶
type Tracker struct {
// contains filtered or unexported fields
}
Tracker sends progress notifications to the client for a single tool call. It is safe to call methods on a zero-value or inactive Tracker (all calls become no-ops), so callers never need nil-checks.
Tracker values are safe to copy and share across goroutines: copies share the same underlying monotonic-progress state via a pointer field.
func FromRequest ¶
func FromRequest(req *mcp.CallToolRequest) Tracker
FromRequest extracts the progress token from a CallToolRequest and returns a Tracker bound to that request's session. If the request has no progress token, no valid session, or the session is not initialized, the returned Tracker is inactive (all methods are no-ops).
func (Tracker) Done ¶
Done sends a final progress notification reporting completion (progress==total). It is a convenience for callers that report intermediate steps via Tracker.Step (which uses zero-based progress) and want a clean 100% completion notification. If the Tracker is inactive or total is non-positive, Done is a no-op.
func (Tracker) IsActive ¶
IsActive returns true if the Tracker has both a valid session and a progress token, meaning it can send notifications.
func (Tracker) OnScale ¶
OnScale returns a Tracker that reports on a caller-chosen scale while sharing this one's monotonic state.
It exists because "the progress value MUST increase with each notification" is a property of one progress token, not of one Tracker, and a token belongs to the whole tool call. A sub-step that measures itself in its own units — bytes of one file, while the step above counts files — cannot be made to satisfy that by guarding each counter separately: the two interleave on the wire and the sequence goes backwards. The guard in Update only ever compared a Tracker against itself, so it could not see it.
The fix is one scale rather than one guard. The sub-step keeps reporting its own numbers, and they are placed inside the outer measure here: base is where this sub-step starts, outerTotal is the whole job. The returned Tracker shares the same *progressState, so what the client receives is a single increasing series with a stable total.
func (Tracker) Step ¶
Step is a convenience that reports progress as step/total (1-based step index). Example: Step(ctx, 1, 3, "Fetching MR details...") sends progress=0, total=3.
func (Tracker) Update ¶
Update sends a progress notification with explicit progress and total values. If the Tracker is inactive or the context is canceled, it silently does nothing. Errors from the notification are logged but never propagated — a failed progress notification must not abort the tool operation.
SPEC: MCP 2025-11-25 mandates that progress values strictly increase. Calls where progress is less than or equal to a previously sent value are dropped (logged at Debug level) to preserve the protocol invariant.