Progress
The Model Context Protocol (MCP) supports progress tracking for long-running operations through notification messages.
Typically progress tracking is supported by server tools that perform operations that take a significant amount of time to complete, such as image generation or complex calculations. However, progress tracking is defined in the MCP specification as a general feature that can be implemented for any request that's handled by either a server or a client. This project illustrates the common case of a server tool that performs a long-running operation and sends progress updates to the client.
Server Implementation
When processing a request, the server can use the SendNotificationAsync extension method of McpServer to send progress updates,
specifying "notifications/progress" as the notification method name.
The C# SDK registers an instance of McpServer with the dependency injection container,
so tools can simply add a parameter of type McpServer to their method signature to access it.
The parameters passed to SendNotificationAsync should be an instance of ProgressNotificationParams, which includes the current progress, total steps, and an optional message.
The server must verify that the caller provided a progressToken in the request and include it in the call to SendNotificationAsync. The following example demonstrates how a server can send a progress notification:
if (progressToken is not null)
{
await server.SendNotificationAsync("notifications/progress", new ProgressNotificationParams
{
ProgressToken = progressToken.Value,
Progress = new ProgressNotificationValue
{
Progress = i,
Total = steps,
Message = $"Step {i} of {steps} completed.",
},
});
}
Client Implementation
Clients request progress updates by including a progressToken in the parameters of a request.
Note that servers aren't required to support progress tracking, so clients should not depend on receiving progress updates.
In the MCP C# SDK, clients can specify a progressToken in the request parameters when calling a tool method.
The client should also provide a notification handler to process "notifications/progress" notifications.
There are two way to do this. The first is to register a notification handler using the RegisterNotificationHandler method on the McpClient instance. A handler registered this way will receive all progress notifications sent by the server.
mcpClient.RegisterNotificationHandler(NotificationMethods.ProgressNotification,
(notification, cancellationToken) =>
{
if (JsonSerializer.Deserialize<ProgressNotificationParams>(notification.Params) is { } pn &&
pn.ProgressToken == progressToken)
{
// progress.Report(pn.Progress);
Console.WriteLine($"Tool progress: {pn.Progress.Progress} of {pn.Progress.Total} - {pn.Progress.Message}");
}
return ValueTask.CompletedTask;
}).ConfigureAwait(false);
The second way is to pass a Progress<T> instance to the tool method. Progress<T> is a standard .NET type that provides a way to receive progress updates.
For the purposes of MCP progress notifications, T should be ProgressNotificationValue.
The MCP C# SDK will automatically handle progress notifications and report them through the Progress<T> instance.
This notification handler will only receive progress updates for the specific request that was made,
rather than all progress notifications from the server.
var progressHandler = new Progress<ProgressNotificationValue>(value =>
{
Console.WriteLine($"Tool progress: {value.Progress} of {value.Total} - {value.Message}");
});
var result = await mcpClient.CallToolAsync(toolName: tools.First().Name, progress: progressHandler);