60e0ffc959
Upgrade checks / Notify on failure (push) Has been cancelled
Upgrade checks / Close issue on success (push) Has been cancelled
Schema Crash Test / Real-world schema crash test (232K schemas) (push) Has been cancelled
Run static analysis / static_analysis (push) Has been cancelled
Tests / Tests: Python 3.10 on ubuntu-latest (push) Has been cancelled
Tests / Tests: Python 3.13 on ubuntu-latest (push) Has been cancelled
Tests / Tests: Python 3.10 on windows-latest (push) Has been cancelled
Tests / Tests with lowest-direct dependencies (push) Has been cancelled
Tests / MCP conformance tests (push) Has been cancelled
Tests / Integration tests (push) Has been cancelled
Tests / Package install smoke (push) Has been cancelled
Upgrade checks / Static analysis (push) Has been cancelled
Upgrade checks / Tests: Python 3.10 on ubuntu-latest (push) Has been cancelled
Upgrade checks / Tests: Python 3.13 on ubuntu-latest (push) Has been cancelled
Upgrade checks / Tests: Python 3.10 on windows-latest (push) Has been cancelled
Upgrade checks / Integration tests (push) Has been cancelled
Update MCPServerConfig Schema / update-config-schema (push) Has been cancelled
Update SDK Documentation / update-sdk-docs (push) Has been cancelled
190 lines
5.7 KiB
Plaintext
190 lines
5.7 KiB
Plaintext
---
|
|
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
|
|
|
|
<Card icon="code" title="Context Progress Method">
|
|
<ResponseField name="ctx.report_progress" type="async method">
|
|
Report progress to the client for long-running operations
|
|
|
|
<Expandable title="Parameters">
|
|
<ResponseField name="progress" type="float">
|
|
Current progress value (e.g., 24, 0.75, 1500)
|
|
</ResponseField>
|
|
|
|
<ResponseField name="total" type="float | None" default="None">
|
|
Optional total value (e.g., 100, 1.0, 2000). When provided, clients may interpret this as enabling percentage calculation.
|
|
</ResponseField>
|
|
</Expandable>
|
|
</ResponseField>
|
|
</Card>
|
|
|
|
## 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
|