ExMCP.ProgressTracker (ex_mcp v0.9.0)

View Source

Progress tracking for MCP 2025-06-18 specification compliance.

This module manages progress tokens and sends progress notifications for long-running operations according to the MCP specification.

Features

  • Progress token uniqueness validation
  • Monotonic progress value enforcement
  • Rate limiting to prevent notification flooding
  • Automatic cleanup of completed operations
  • Support for both string and integer progress tokens

Usage

# Start tracking a new operation
{:ok, tracker} = ProgressTracker.start_progress("abc123", sender_pid)

# Send progress updates
ProgressTracker.update_progress("abc123", 25, 100, "Processing...")
ProgressTracker.update_progress("abc123", 50, 100, "Half way there...")
ProgressTracker.update_progress("abc123", 100, 100, "Complete!")

# Clean up when operation completes
ProgressTracker.complete_progress("abc123")

MCP Specification Compliance

This implementation follows the MCP 2025-06-18 specification:

  • Progress tokens must be string or integer values
  • Progress tokens must be unique across all active requests
  • Progress values must increase with each notification
  • Rate limiting prevents notification flooding
  • Supports optional total and message fields

Summary

Functions

Returns a specification to start this module under a supervisor.

Clears all progress tracking state.

Marks a progress operation as complete and cleans up tracking state.

Gets the current state of a progress operation.

Lists all active progress tokens.

Starts the ProgressTracker GenServer.

Starts tracking progress for a new operation.

Types

progress_state()

@type progress_state() :: %ExMCP.ProgressTracker{
  current_progress: number(),
  last_message: String.t() | nil,
  last_notification_time: integer(),
  notification_count: non_neg_integer(),
  progress_token: ExMCP.Types.progress_token(),
  sender_pid: pid(),
  start_time: integer(),
  total: number() | nil
}

Functions

child_spec(init_arg)

Returns a specification to start this module under a supervisor.

See Supervisor.

clear_all()

@spec clear_all() :: :ok

Clears all progress tracking state.

This is primarily useful for testing and cleanup.

complete_progress(progress_token)

@spec complete_progress(ExMCP.Types.progress_token()) :: :ok | {:error, :not_found}

Marks a progress operation as complete and cleans up tracking state.

Parameters

  • progress_token - Token for the operation to complete

Returns

  • :ok - Operation completed and cleaned up
  • {:error, :not_found} - Progress token not found

get_progress_state(progress_token)

@spec get_progress_state(ExMCP.Types.progress_token()) ::
  {:ok, progress_state()} | {:error, :not_found}

Gets the current state of a progress operation.

Parameters

  • progress_token - Token for the operation

Returns

  • {:ok, progress_state} - Current state of the operation
  • {:error, :not_found} - Progress token not found

list_active_tokens()

@spec list_active_tokens() :: [ExMCP.Types.progress_token()]

Lists all active progress tokens.

Returns

List of all currently active progress tokens.

start_link(opts \\ [])

Starts the ProgressTracker GenServer.

start_progress(progress_token, sender_pid)

@spec start_progress(ExMCP.Types.progress_token(), pid()) ::
  {:ok, progress_state()} | {:error, :token_exists | :invalid_token}

Starts tracking progress for a new operation.

Parameters

  • progress_token - Unique token for this operation (string or integer)
  • sender_pid - Process that will receive progress notifications

Returns

  • {:ok, progress_state} - Successfully started tracking
  • {:error, :token_exists} - Progress token is already in use
  • {:error, :invalid_token} - Progress token is not string or integer

update_progress(progress_token, progress, total \\ nil, message \\ nil)

@spec update_progress(
  ExMCP.Types.progress_token(),
  number(),
  number() | nil,
  String.t() | nil
) ::
  :ok | {:error, :not_found | :not_increasing | :rate_limited}

Updates progress for an active operation.

Parameters

  • progress_token - Token for the operation
  • progress - Current progress value (must be > previous value)
  • total - Optional total value for the operation
  • message - Optional human-readable progress message

Returns

  • :ok - Progress updated and notification sent
  • {:error, :not_found} - Progress token not found
  • {:error, :not_increasing} - Progress value must increase
  • {:error, :rate_limited} - Too many notifications too quickly