Implement MCP task support (SEP-1686) (#1170)
Co-authored-by: Stephen Halter <halter73@gmail.com>
This commit is contained in:
parent
81664c3fce
commit
10e4f5b20f
1 changed files with 11580 additions and 46 deletions
168
src/ModelContextProtocol.Core/Server/IMcpTaskStore.cs
Normal file
168
src/ModelContextProtocol.Core/Server/IMcpTaskStore.cs
Normal file
|
|
@ -0,0 +1,168 @@
|
|||
using ModelContextProtocol.Protocol;
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
using System.Text.Json;
|
||||
|
||||
namespace ModelContextProtocol;
|
||||
|
||||
/// <summary>
|
||||
/// Provides an interface for pluggable task storage implementations in MCP servers.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// <para>
|
||||
/// The task store is responsible for managing the lifecycle of tasks, including creation,
|
||||
/// status updates, result storage, and retrieval. Implementations must be thread-safe and
|
||||
/// may support session-based isolation for multi-session scenarios.
|
||||
/// </para>
|
||||
/// <para>
|
||||
/// TTL (Time To Live) Management: Implementations may override the requested TTL value in
|
||||
/// <see cref="McpTaskMetadata.TimeToLive"/> to enforce resource limits. The actual TTL
|
||||
/// used is returned in the <see cref="McpTask.TimeToLive"/> property. A null TTL indicates
|
||||
/// unlimited lifetime. Tasks may be deleted after their TTL expires, regardless of status.
|
||||
/// </para>
|
||||
/// </remarks>
|
||||
[Experimental(Experimentals.Tasks_DiagnosticId, UrlFormat = Experimentals.Tasks_Url)]
|
||||
public interface IMcpTaskStore
|
||||
{
|
||||
/// <summary>
|
||||
/// Creates a new task for tracking an asynchronous operation.
|
||||
/// </summary>
|
||||
/// <param name="taskParams">Metadata for the task, including requested TTL.</param>
|
||||
/// <param name="requestId">The JSON-RPC request ID that initiated this task.</param>
|
||||
/// <param name="request">The original JSON-RPC request that triggered task creation.</param>
|
||||
/// <param name="sessionId">Optional session identifier for multi-session isolation.</param>
|
||||
/// <param name="cancellationToken">Cancellation token for the operation.</param>
|
||||
/// <returns>
|
||||
/// A new <see cref="McpTask"/> with a unique task ID, initial status of <see cref="McpTaskStatus.Working"/>,
|
||||
/// and the actual TTL that will be used (which may differ from the requested TTL).
|
||||
/// </returns>
|
||||
/// <remarks>
|
||||
/// Implementations must generate a unique task ID and set the <see cref="McpTask.CreatedAt"/>
|
||||
/// and <see cref="McpTask.LastUpdatedAt"/> timestamps. The implementation may override the
|
||||
/// requested TTL to enforce storage limits.
|
||||
/// </remarks>
|
||||
Task<McpTask> CreateTaskAsync(
|
||||
McpTaskMetadata taskParams,
|
||||
RequestId requestId,
|
||||
JsonRpcRequest request,
|
||||
string? sessionId = null,
|
||||
CancellationToken cancellationToken = default);
|
||||
|
||||
/// <summary>
|
||||
/// Retrieves a task by its unique identifier.
|
||||
/// </summary>
|
||||
/// <param name="taskId">The unique identifier of the task to retrieve.</param>
|
||||
/// <param name="sessionId">Optional session identifier for access control.</param>
|
||||
/// <param name="cancellationToken">Cancellation token for the operation.</param>
|
||||
/// <returns>
|
||||
/// The <see cref="McpTask"/> if found and accessible, otherwise <see langword="null"/>.
|
||||
/// </returns>
|
||||
/// <remarks>
|
||||
/// Returns null if the task does not exist or if session-based access control denies access.
|
||||
/// </remarks>
|
||||
Task<McpTask?> GetTaskAsync(string taskId, string? sessionId = null, CancellationToken cancellationToken = default);
|
||||
|
||||
/// <summary>
|
||||
/// Stores the final result of a task that has reached a terminal status.
|
||||
/// </summary>
|
||||
/// <param name="taskId">The unique identifier of the task.</param>
|
||||
/// <param name="status">The terminal status: <see cref="McpTaskStatus.Completed"/> or <see cref="McpTaskStatus.Failed"/>.</param>
|
||||
/// <param name="result">The operation result to store as a JSON element.</param>
|
||||
/// <param name="sessionId">Optional session identifier for access control.</param>
|
||||
/// <param name="cancellationToken">Cancellation token for the operation.</param>
|
||||
/// <returns>A task representing the asynchronous operation.</returns>
|
||||
/// <remarks>
|
||||
/// <para>
|
||||
/// The <paramref name="status"/> must be either <see cref="McpTaskStatus.Completed"/> or
|
||||
/// <see cref="McpTaskStatus.Failed"/>. This method updates the task status and stores
|
||||
/// the result for later retrieval via <see cref="GetTaskResultAsync"/>.
|
||||
/// </para>
|
||||
/// <para>
|
||||
/// Implementations should throw <see cref="InvalidOperationException"/> if called on a task
|
||||
/// that is already in a terminal state, to prevent result overwrites.
|
||||
/// </para>
|
||||
/// </remarks>
|
||||
/// <returns>The updated <see cref="McpTask"/> with the new status and result stored.</returns>
|
||||
Task<McpTask> StoreTaskResultAsync(
|
||||
string taskId,
|
||||
McpTaskStatus status,
|
||||
JsonElement result,
|
||||
string? sessionId = null,
|
||||
CancellationToken cancellationToken = default);
|
||||
|
||||
/// <summary>
|
||||
/// Retrieves the stored result of a completed or failed task.
|
||||
/// </summary>
|
||||
/// <param name="taskId">The unique identifier of the task.</param>
|
||||
/// <param name="sessionId">Optional session identifier for access control.</param>
|
||||
/// <param name="cancellationToken">Cancellation token for the operation.</param>
|
||||
/// <returns>The stored operation result as a JSON element.</returns>
|
||||
/// <remarks>
|
||||
/// This method should only be called on tasks in terminal states (<see cref="McpTaskStatus.Completed"/>
|
||||
/// or <see cref="McpTaskStatus.Failed"/>). The result contains the JSON representation of the
|
||||
/// original operation result (e.g., <see cref="CallToolResult"/> for tools/call).
|
||||
/// </remarks>
|
||||
Task<JsonElement> GetTaskResultAsync(string taskId, string? sessionId = null, CancellationToken cancellationToken = default);
|
||||
|
||||
/// <summary>
|
||||
/// Updates the status and optional status message of a task.
|
||||
/// </summary>
|
||||
/// <param name="taskId">The unique identifier of the task.</param>
|
||||
/// <param name="status">The new status to set.</param>
|
||||
/// <param name="statusMessage">Optional diagnostic message describing the status change.</param>
|
||||
/// <param name="sessionId">Optional session identifier for access control.</param>
|
||||
/// <param name="cancellationToken">Cancellation token for the operation.</param>
|
||||
/// <returns>A task representing the asynchronous operation.</returns>
|
||||
/// <remarks>
|
||||
/// This method updates the task's <see cref="McpTask.Status"/>, <see cref="McpTask.StatusMessage"/>,
|
||||
/// and <see cref="McpTask.LastUpdatedAt"/> properties. Common uses include transitioning to
|
||||
/// <see cref="McpTaskStatus.Cancelled"/>, <see cref="McpTaskStatus.InputRequired"/>, or updating
|
||||
/// progress messages while in <see cref="McpTaskStatus.Working"/> status.
|
||||
/// </remarks>
|
||||
/// <returns>The updated <see cref="McpTask"/> with the new status applied.</returns>
|
||||
Task<McpTask> UpdateTaskStatusAsync(
|
||||
string taskId,
|
||||
McpTaskStatus status,
|
||||
string? statusMessage,
|
||||
string? sessionId = null,
|
||||
CancellationToken cancellationToken = default);
|
||||
|
||||
/// <summary>
|
||||
/// Lists tasks with pagination support.
|
||||
/// </summary>
|
||||
/// <param name="cursor">Optional cursor for pagination, from a previous call's nextCursor value.</param>
|
||||
/// <param name="sessionId">Optional session identifier for filtering tasks by session.</param>
|
||||
/// <param name="cancellationToken">Cancellation token for the operation.</param>
|
||||
/// <returns>A <see cref="ListTasksResult"/> containing the tasks and an optional cursor for the next page.</returns>
|
||||
/// <remarks>
|
||||
/// When <paramref name="sessionId"/> is provided, implementations should filter to only return
|
||||
/// tasks associated with that session. The cursor format is implementation-specific.
|
||||
/// </remarks>
|
||||
Task<ListTasksResult> ListTasksAsync(
|
||||
string? cursor = null,
|
||||
string? sessionId = null,
|
||||
CancellationToken cancellationToken = default);
|
||||
|
||||
/// <summary>
|
||||
/// Attempts to cancel a task, transitioning it to <see cref="McpTaskStatus.Cancelled"/> status.
|
||||
/// </summary>
|
||||
/// <param name="taskId">The unique identifier of the task to cancel.</param>
|
||||
/// <param name="sessionId">Optional session identifier for access control.</param>
|
||||
/// <param name="cancellationToken">Cancellation token for the operation.</param>
|
||||
/// <returns>
|
||||
/// The updated <see cref="McpTask"/>. If the task is already in a terminal state
|
||||
/// (<see cref="McpTaskStatus.Completed"/>, <see cref="McpTaskStatus.Failed"/>, or
|
||||
/// <see cref="McpTaskStatus.Cancelled"/>), the task is returned unchanged.
|
||||
/// </returns>
|
||||
/// <remarks>
|
||||
/// <para>
|
||||
/// This method must be idempotent. If called on a task that is already in a terminal state,
|
||||
/// it returns the current task without error. This behavior differs from the MCP specification
|
||||
/// but ensures idempotency and avoids race conditions between cancellation and task completion.
|
||||
/// </para>
|
||||
/// <para>
|
||||
/// For tasks not in a terminal state, the implementation should attempt to stop the underlying
|
||||
/// operation and transition the task to <see cref="McpTaskStatus.Cancelled"/> status before returning.
|
||||
/// </para>
|
||||
/// </remarks>
|
||||
Task<McpTask> CancelTaskAsync(string taskId, string? sessionId = null, CancellationToken cancellationToken = default);
|
||||
}
|
||||
Loading…
Reference in a new issue