--- title: Progress Reporting sidebarTitle: Progress description: Update clients on the progress of long-running operations through the MCP context. icon: chart-line --- import { VersionBadge } from '/snippets/version-badge.mdx' Progress reporting allows MCP tools to notify clients about the progress of long-running operations. This enables clients to display progress indicators and provide better user experience during time-consuming tasks. ## Why Use Progress Reporting? Progress reporting is valuable for: - **User experience**: Keep users informed about long-running operations - **Progress indicators**: Enable clients to show progress bars or percentages - **Timeout prevention**: Demonstrate that operations are actively progressing - **Debugging**: Track execution progress for performance analysis ### Basic Usage Use `ctx.report_progress()` to send progress updates to the client: ```python {14, 21} from fastmcp import FastMCP, Context import asyncio mcp = FastMCP("ProgressDemo") @mcp.tool async def process_items(items: list[str], ctx: Context) -> dict: """Process a list of items with progress updates.""" total = len(items) results = [] for i, item in enumerate(items): # Report progress as we process each item await ctx.report_progress(progress=i, total=total) # Simulate processing time await asyncio.sleep(0.1) results.append(item.upper()) # Report 100% completion await ctx.report_progress(progress=total, total=total) return {"processed": len(results), "results": results} ``` ## Method Signature Report progress to the client for long-running operations Current progress value (e.g., 24, 0.75, 1500) Optional total value (e.g., 100, 1.0, 2000). When provided, clients may interpret this as enabling percentage calculation. ## Progress Patterns ### Percentage-Based Progress Report progress as a percentage (0-100): ```python {13-14} @mcp.tool async def download_file(url: str, ctx: Context) -> str: """Download a file with percentage progress.""" total_size = 1000 # KB downloaded = 0 while downloaded < total_size: # Download chunk chunk_size = min(50, total_size - downloaded) downloaded += chunk_size # Report percentage progress percentage = (downloaded / total_size) * 100 await ctx.report_progress(progress=percentage, total=100) await asyncio.sleep(0.1) # Simulate download time return f"Downloaded file from {url}" ``` ### Absolute Progress Report progress with absolute values: ```python {10} @mcp.tool async def backup_database(ctx: Context) -> str: """Backup database tables with absolute progress.""" tables = ["users", "orders", "products", "inventory", "logs"] for i, table in enumerate(tables): await ctx.info(f"Backing up table: {table}") # Report absolute progress await ctx.report_progress(progress=i + 1, total=len(tables)) # Simulate backup time await asyncio.sleep(0.5) return "Database backup completed" ``` ### Indeterminate Progress Report progress without a known total for operations where the endpoint is unknown: ```python {11} @mcp.tool async def scan_directory(directory: str, ctx: Context) -> dict: """Scan directory with indeterminate progress.""" files_found = 0 # Simulate directory scanning for i in range(10): # Unknown number of files files_found += 1 # Report progress without total for indeterminate operations await ctx.report_progress(progress=files_found) await asyncio.sleep(0.2) return {"files_found": files_found, "directory": directory} ``` ### Multi-Stage Operations Break complex operations into stages with progress for each: ```python @mcp.tool async def data_migration(source: str, destination: str, ctx: Context) -> str: """Migrate data with multi-stage progress reporting.""" # Stage 1: Validation (0-25%) await ctx.info("Validating source data") for i in range(5): await ctx.report_progress(progress=i * 5, total=100) await asyncio.sleep(0.1) # Stage 2: Export (25-60%) await ctx.info("Exporting data from source") for i in range(7): progress = 25 + (i * 5) await ctx.report_progress(progress=progress, total=100) await asyncio.sleep(0.1) # Stage 3: Transform (60-80%) await ctx.info("Transforming data format") for i in range(4): progress = 60 + (i * 5) await ctx.report_progress(progress=progress, total=100) await asyncio.sleep(0.1) # Stage 4: Import (80-100%) await ctx.info("Importing to destination") for i in range(4): progress = 80 + (i * 5) await ctx.report_progress(progress=progress, total=100) await asyncio.sleep(0.1) # Final completion await ctx.report_progress(progress=100, total=100) return f"Migration from {source} to {destination} completed" ``` ## Client Requirements Progress reporting requires clients to support progress handling: - Clients must send a `progressToken` in the initial request to receive progress updates - If no progress token is provided, progress calls will have no effect (they won't error) - See [Client Progress](/v2/clients/progress) for details on implementing client-side progress handling