TellWorldAI World Archive
Ask中文

Home›Practice›Practice

Practice

MCP Protocol Features: Progress, Cancellation, and Logging

MCP servers can report progress, accept cancellation, expose parameterized resource URIs, and return consistent errors. Each feature is shown with Python code, and the 2026-07-28 changes are flagged.

Who this is for

This is for developers who have a working MCP server with basic tools and resources, and now need long jobs, cancellation, or stable error handling. After reading, you should know which features to use, and which parts of the older guides are now legacy.

Six features beyond basic tools

Six features go beyond basic tools and resources: progress notifications, request cancellation, resource templates, application lifecycle, legacy logging control, and error handling patterns [1]. Each one solves a different problem, so you can pick only the ones you need.

Progress notifications for long jobs

Progress notifications keep users informed during data processing, file downloads, and API calls [1]. While the server handles a tools/call request, it sends progress updates and then returns the final result [1]. The Python server sends a ProgressNotification after each item:

        await ctx.send_notification(
            ProgressNotification(
                progressToken=ctx.request_id,
                progress=i + 1,
                total=total,
                message=f"Processed {i + 1}/{total}: {item}"
            )
        )

On the client, register a handler before calling the tool [1]:

async def handle_progress(notification):
    """Handle progress notifications from server."""
    params = notification.params
    print(f"Progress: {params.progress}/{params.total} - {params.message}")

# Register handler
session.on_notification("notifications/progress", handle_progress)

# Call tool (progress updates will arrive via handler)
result = await session.call_tool("process_large_file", {"file_path": "/data/large.csv"})

The TypeScript version sends a notifications/progress message with the same fields: progressToken, progress, total, and message [1].

Cancelling a request

Request cancellation lets a client stop a request that is no longer needed or is taking too long [1]. Inside a loop, the Python tool checks ctx.is_cancelled and raises CancelledError when it is set [1]:

            if ctx.is_cancelled:
                raise CancelledError("Search cancelled by user")

The search example returns the partial results it has found so far [1]. On the client side, the example stops a timed-out search by sending a notifications/cancelled message with requestId and reason [1]:

        await session.send_notification({
            "method": "notifications/cancelled",
            "params": {"requestId": task.request_id, "reason": "Timeout"}
        })

Resource templates for parameterized URIs

Resource templates let a server define dynamic URIs with parameters. They suit APIs and databases [1]. A template has uriTemplate, name, description, and mimeType fields [1]:

        ResourceTemplate(
            uriTemplate="db://users/{user_id}",
            name="User Profile",
            description="Fetch user profile by ID",
            mimeType="application/json"
        ),

The read_resource handler parses the incoming URI and fills in the parameters before it fetches data [1]. The TypeScript example registers a GitHub issue template, github://repos/{owner}/{repo}/issues/{issue_number} [1].

Application lifecycle: start and stop resources

Lifecycle here means process startup and shutdown, not the initialize handshake [1]. The Python example passes an async context manager named lifespan to Server. It opens a database connection and a cache at startup and closes them at shutdown [1]:

@asynccontextmanager
async def lifespan(server: Server):
    """Manage server lifecycle."""
    global db_connection, cache

What 2026-07-28 changed

In MCP 2026-07-28, the initialize handshake and protocol-level sessions are removed. Server startup and shutdown remain your application's job [1]. Tasks is now a separately versioned extension. A server can return a task handle from a tool call, and the client drives it with tasks/get, tasks/update, and tasks/cancel [1]. The experimental 2025-11-25 Tasks API is not backward compatible, and tasks/list no longer exists [1].

Logging control is legacy

MCP logging is deprecated in 2026-07-28 and can be removed in the first specification revision released on or after July 28, 2027 [1]. Older versions let clients change server-side logging levels [1]. For new servers, use stderr with stdio, and OpenTelemetry for structured observability [1].

Error handling: which code to use

Define custom error classes on top of McpError. For example, a validation error uses ErrorCode.INVALID_PARAMS [1]:

class ValidationError(ToolError):
    """Invalid input parameters."""
    def __init__(self, message: str):
        super().__init__(ErrorCode.INVALID_PARAMS, message)

In the Python example, not-found and permission errors use ErrorCode.INVALID_REQUEST, and unexpected internal errors use ErrorCode.INTERNAL_ERROR [1]. The TypeScript example re-throws existing McpError instances and returns a generic message for other errors [1].

Tool annotations are hints

Tool annotations describe read-only, destructive, idempotent, or open-world behavior [1]. They are hints. Do not treat them as trusted authorization or safety guarantees unless they come from a trusted server [1].

How progress, cancellation, and error codes fit together

Progress notifications tell the user how far a task has got. In the example, the total field gives the overall count, so a client can work out the percentage [1]. When a search is cancelled, the example returns the partial results it has found instead of failing outright [1]. For errors, invalid input uses ErrorCode.INVALID_PARAMS, not-found and permission errors use ErrorCode.INVALID_REQUEST, and unexpected problems use ErrorCode.INTERNAL_ERROR [1]. A client can use these codes to tell bad input apart from an internal server fault.

Version boundaries for Tasks and logging

Tasks is a separately versioned extension. When you judge compatibility, check the Tasks extension's own version, not only the core specification version [1]. Logging works the other way round: it is kept for legacy implementations. New servers should use stderr with stdio, or OpenTelemetry for structured observability [1].

Common questions

Should I still use the logging examples? Only for compatibility with older implementations. New servers should use stderr and OpenTelemetry [1].

**Do I still need initialize?** Under 2026-07-28, the handshake is removed, so do not design new code around it [1].

Adapted from the protocol features chapter of Microsoft's open-source course *MCP for Beginners*[1], released under the MIT License.

Sources

  1. 1
    MCP Protocol Features Deep DiveMicrosoft mcp-for-beginners (MIT)

Cite this page: MCP Protocol Features: Progress, Cancellation, and Logging. TellWorld, https://www.tellworld.net/practice/practice-mcp-protocol-features. Updated 2026-11-14

Related news

View all