Skip to content

Commit 45a3f59

Browse files
jeffhandleyCopilot
andcommitted
Add Core seams for Tasks extension
- Add IMcpServerRawHandlerRegistry seam (McpRawRequestHandler delegate, registry over request handlers, IsDraftProtocolRequest) and McpServerOptions.RawRequestHandlerConfigurators - Add McpServer.CurrentOutgoingRequestInterceptor AsyncLocal seam and McpOutgoingRequestInterceptor delegate; route SampleAsync/ElicitAsync/RequestRootsAsync through it - Make McpSession.IsDraftProtocol public and McpClient.ResolveInputRequestsAsync public abstract (MCPEXP002) - Remove Tasks coupling from Core: ConfigureTasks, task-store tools/call wrapper, SetTaskAugmented, CallToolWithTaskHandler/Filters, task lifecycle handlers, MaxConsecutiveStuckPolls, task method/notification/meta consts, task JsonSerializable entries - Rewrite client CallToolAsync to a plain tools/call returning CallToolResult - Relocate Tasks DTOs and store types into ModelContextProtocol.Extensions.Tasks; drop McpTaskExecutionContext - Add ModelContextProtocol.Extensions.Tasks project and TasksJsonContext Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
1 parent d6b1615 commit 45a3f59

37 files changed

Lines changed: 307 additions & 1255 deletions

src/ModelContextProtocol.Core/Client/McpClient.Methods.cs

Lines changed: 7 additions & 358 deletions
Large diffs are not rendered by default.

src/ModelContextProtocol.Core/Client/McpClient.cs

Lines changed: 3 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -73,7 +73,7 @@ protected McpClient()
7373
public abstract Task<ClientCompletionDetails> Completion { get; }
7474

7575
/// <summary>
76-
/// Resolves input requests embedded in an <see cref="InputRequiredTaskResult"/> by dispatching
76+
/// Resolves input requests embedded in a task that requires input by dispatching
7777
/// each request to the appropriate registered handler.
7878
/// </summary>
7979
/// <param name="inputRequests">
@@ -82,16 +82,10 @@ protected McpClient()
8282
/// </param>
8383
/// <param name="cancellationToken">The <see cref="CancellationToken"/> to monitor for cancellation requests.</param>
8484
/// <returns>A dictionary of responses keyed by the same identifiers as the input requests.</returns>
85-
private protected abstract ValueTask<IDictionary<string, InputResponse>> ResolveInputRequestsAsync(
85+
[System.Diagnostics.CodeAnalysis.Experimental(Experimentals.Subclassing_DiagnosticId, UrlFormat = Experimentals.Subclassing_Url)]
86+
public abstract ValueTask<IDictionary<string, InputResponse>> ResolveInputRequestsAsync(
8687
IDictionary<string, InputRequest> inputRequests, CancellationToken cancellationToken);
8788

88-
/// <summary>
89-
/// Gets the maximum number of consecutive stuck-in-<see cref="McpTaskStatus.InputRequired"/> polls
90-
/// allowed by <see cref="PollTaskToCompletionAsync"/> before the client cancels and throws.
91-
/// Sourced from <see cref="McpClientOptions.MaxConsecutiveStuckPolls"/>.
92-
/// </summary>
93-
private protected abstract int MaxConsecutiveStuckPolls { get; }
94-
9589
/// <summary>
9690
/// Registers one or more tool definitions in the client's tool cache, enabling the transport
9791
/// to send <c>Mcp-Param-*</c> headers for those tools without requiring a prior <see cref="McpClient.ListToolsAsync(RequestOptions?, CancellationToken)"/> call.

src/ModelContextProtocol.Core/Client/McpClientImpl.cs

Lines changed: 1 addition & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,6 @@ internal sealed partial class McpClientImpl : McpClient
4141
/// <param name="options">Options for the client, defining protocol version and capabilities.</param>
4242
/// <param name="loggerFactory">The logger factory.</param>
4343
internal McpClientImpl(ITransport transport, string endpointName, McpClientOptions? options, ILoggerFactory? loggerFactory)
44-
#pragma warning restore MCPEXP002
4544
{
4645
options ??= new();
4746

@@ -178,10 +177,7 @@ private void RegisterHandlers(McpClientOptions options, NotificationHandlers not
178177
public override Task<ClientCompletionDetails> Completion => _sessionHandler.CompletionTask;
179178

180179
/// <inheritdoc/>
181-
private protected override int MaxConsecutiveStuckPolls => _options.MaxConsecutiveStuckPolls;
182-
183-
/// <inheritdoc/>
184-
private protected override async ValueTask<IDictionary<string, InputResponse>> ResolveInputRequestsAsync(
180+
public override async ValueTask<IDictionary<string, InputResponse>> ResolveInputRequestsAsync(
185181
IDictionary<string, InputRequest> inputRequests,
186182
CancellationToken cancellationToken)
187183
{

src/ModelContextProtocol.Core/Client/McpClientOptions.cs

Lines changed: 0 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -146,42 +146,4 @@ public McpClientHandlers Handlers
146146
field = value;
147147
}
148148
}
149-
150-
/// <summary>
151-
/// Gets or sets the maximum number of consecutive task polls during which a task may report
152-
/// <see cref="McpTaskStatus.InputRequired"/> without publishing any new input requests, before
153-
/// the client treats the task as stuck, issues a best-effort <c>tasks/cancel</c>, and throws
154-
/// an <see cref="McpException"/>.
155-
/// </summary>
156-
/// <value>
157-
/// The maximum number of consecutive stuck polls allowed. The default value is <c>60</c>.
158-
/// </value>
159-
/// <remarks>
160-
/// <para>
161-
/// This guard prevents an unbounded poll loop when the server keeps a task in
162-
/// <see cref="McpTaskStatus.InputRequired"/> but never publishes new input requests after the
163-
/// client has already responded to every previously surfaced request. It only affects the
164-
/// long-poll path used by <see cref="McpClient.CallToolAsync(CallToolRequestParams, CancellationToken)"/>;
165-
/// it does not affect direct <see cref="McpClient.GetTaskAsync(string, CancellationToken)"/> calls.
166-
/// </para>
167-
/// <para>
168-
/// Callers should size this value with the configured server-side poll interval in mind: the
169-
/// effective wall-clock timeout is roughly <c>MaxConsecutiveStuckPolls * pollIntervalMs</c>.
170-
/// Setting this to a very small value can cause false positives for servers that are slow to
171-
/// surface follow-up input requests; setting it too large can mask misbehaving servers.
172-
/// </para>
173-
/// </remarks>
174-
/// <exception cref="ArgumentOutOfRangeException">The value is less than <c>1</c>.</exception>
175-
public int MaxConsecutiveStuckPolls
176-
{
177-
get;
178-
set
179-
{
180-
if (value < 1)
181-
{
182-
throw new ArgumentOutOfRangeException(nameof(value), value, "must be greater than or equal to 1.");
183-
}
184-
field = value;
185-
}
186-
} = 60;
187149
}

src/ModelContextProtocol.Core/McpJsonUtilities.cs

Lines changed: 0 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -120,12 +120,6 @@ internal static bool IsValidToolOutputSchema(JsonElement element) =>
120120
[JsonSerializable(typeof(ResourceUpdatedNotificationParams))]
121121
[JsonSerializable(typeof(RootsListChangedNotificationParams))]
122122
[JsonSerializable(typeof(ToolListChangedNotificationParams))]
123-
[JsonSerializable(typeof(TaskStatusNotificationParams))]
124-
[JsonSerializable(typeof(WorkingTaskNotificationParams))]
125-
[JsonSerializable(typeof(CompletedTaskNotificationParams))]
126-
[JsonSerializable(typeof(FailedTaskNotificationParams))]
127-
[JsonSerializable(typeof(CancelledTaskNotificationParams))]
128-
[JsonSerializable(typeof(InputRequiredTaskNotificationParams))]
129123

130124
// MCP Request Params / Results
131125
[JsonSerializable(typeof(CallToolRequestParams))]
@@ -174,19 +168,6 @@ internal static bool IsValidToolOutputSchema(JsonElement element) =>
174168
[JsonSerializable(typeof(IDictionary<string, InputRequest>))]
175169
[JsonSerializable(typeof(IDictionary<string, InputResponse>))]
176170

177-
[JsonSerializable(typeof(GetTaskRequestParams))]
178-
[JsonSerializable(typeof(GetTaskResult))]
179-
[JsonSerializable(typeof(WorkingTaskResult))]
180-
[JsonSerializable(typeof(CompletedTaskResult))]
181-
[JsonSerializable(typeof(FailedTaskResult))]
182-
[JsonSerializable(typeof(CancelledTaskResult))]
183-
[JsonSerializable(typeof(InputRequiredTaskResult))]
184-
[JsonSerializable(typeof(UpdateTaskRequestParams))]
185-
[JsonSerializable(typeof(UpdateTaskResult))]
186-
[JsonSerializable(typeof(CancelTaskRequestParams))]
187-
[JsonSerializable(typeof(CancelTaskResult))]
188-
[JsonSerializable(typeof(CreateTaskResult))]
189-
190171
// MCP Content
191172
[JsonSerializable(typeof(ContentBlock))]
192173
[JsonSerializable(typeof(TextContentBlock))]

src/ModelContextProtocol.Core/McpSession.cs

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,16 @@ public abstract partial class McpSession : IAsyncDisposable
5757
internal bool IsJuly2026OrLaterProtocol() =>
5858
McpHttpHeaders.IsJuly2026OrLaterProtocolVersion(NegotiatedProtocolVersion);
5959

60+
/// <summary>
61+
/// Gets a value indicating whether the negotiated protocol version supports the draft protocol
62+
/// features (the <c>2026-07-28</c> revision or later).
63+
/// </summary>
64+
/// <remarks>
65+
/// This is a public seam used by bolt-on extensions (such as the Tasks extension) to gate
66+
/// functionality that requires the <c>2026-07-28</c> or later protocol revision.
67+
/// </remarks>
68+
public bool IsDraftProtocol() => IsJuly2026OrLaterProtocol();
69+
6070
/// <summary>
6171
/// Sends a JSON-RPC request to the connected session and waits for a response.
6272
/// </summary>

src/ModelContextProtocol.Core/Protocol/MetaKeys.cs

Lines changed: 0 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -53,24 +53,4 @@ public static class MetaKeys
5353
/// belonging to different subscriptions on a shared channel (especially STDIO).
5454
/// </remarks>
5555
public const string SubscriptionId = "io.modelcontextprotocol/subscriptionId";
56-
57-
/// <summary>
58-
/// The metadata key used to associate requests, responses, and notifications with a task.
59-
/// </summary>
60-
/// <remarks>
61-
/// <para>
62-
/// This constant defines the key <c>"io.modelcontextprotocol/related-task"</c> used in the
63-
/// <c>_meta</c> field to associate messages with their originating task across the entire
64-
/// request lifecycle.
65-
/// </para>
66-
/// <para>
67-
/// For example, an elicitation that a task-augmented tool call depends on must share the
68-
/// same related task ID with that tool call's task.
69-
/// </para>
70-
/// <para>
71-
/// For <c>tasks/get</c>, <c>tasks/list</c>, and <c>tasks/cancel</c> operations, this
72-
/// metadata should not be included as the taskId is already present in the message structure.
73-
/// </para>
74-
/// </remarks>
75-
public const string RelatedTask = "io.modelcontextprotocol/related-task";
7656
}

src/ModelContextProtocol.Core/Protocol/NotificationMethods.cs

Lines changed: 0 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -144,16 +144,6 @@ public static class NotificationMethods
144144
/// </remarks>
145145
public const string CancelledNotification = "notifications/cancelled";
146146

147-
/// <summary>
148-
/// The name of the notification sent by the server to push task status updates to subscribed clients.
149-
/// </summary>
150-
/// <remarks>
151-
/// Part of the <c>io.modelcontextprotocol/tasks</c> extension.
152-
/// Each notification carries a complete task state for the current status, identical to what
153-
/// <c>tasks/get</c> would have returned at that moment.
154-
/// </remarks>
155-
public const string TaskStatusNotification = "notifications/tasks/status";
156-
157147
/// <summary>
158148
/// The name of the notification sent first on a <see cref="RequestMethods.SubscriptionsListen"/>
159149
/// response stream to indicate which notification types the server agreed to deliver.

src/ModelContextProtocol.Core/Protocol/RequestMethods.cs

Lines changed: 0 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -125,33 +125,6 @@ public static class RequestMethods
125125
/// </remarks>
126126
public const string Initialize = "initialize";
127127

128-
/// <summary>
129-
/// The name of the request method sent from the client to poll for task completion.
130-
/// </summary>
131-
/// <remarks>
132-
/// Part of the <c>io.modelcontextprotocol/tasks</c> extension.
133-
/// Clients poll for task status by sending this request with the task ID.
134-
/// </remarks>
135-
public const string TasksGet = "tasks/get";
136-
137-
/// <summary>
138-
/// The name of the request method sent from the client to provide input responses to a task.
139-
/// </summary>
140-
/// <remarks>
141-
/// Part of the <c>io.modelcontextprotocol/tasks</c> extension.
142-
/// Used when a task has <c>input_required</c> status and the client needs to fulfill outstanding requests.
143-
/// </remarks>
144-
public const string TasksUpdate = "tasks/update";
145-
146-
/// <summary>
147-
/// The name of the request method sent from the client to signal intent to cancel a task.
148-
/// </summary>
149-
/// <remarks>
150-
/// Part of the <c>io.modelcontextprotocol/tasks</c> extension.
151-
/// Cancellation is cooperative — the server decides whether and when to honor it.
152-
/// </remarks>
153-
public const string TasksCancel = "tasks/cancel";
154-
155128
/// <summary>
156129
/// The name of the request method sent from the client to discover the server's protocol versions,
157130
/// capabilities, and metadata.

src/ModelContextProtocol.Core/Protocol/Result.cs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -30,8 +30,8 @@ private protected Result()
3030
/// When absent or set to <c>"complete"</c>, the result is a normal completed response.
3131
/// When set to <c>"input_required"</c>, the result is an <see cref="InputRequiredResult"/> indicating
3232
/// that additional input is needed before the request can be completed.
33-
/// When set to <c>"task"</c>, the result is a <see cref="CreateTaskResult"/> indicating that the server
34-
/// has created a long-running task in lieu of returning the result directly.
33+
/// When set to <c>"task"</c>, the server has created a long-running task in lieu of returning the
34+
/// result directly.
3535
/// </para>
3636
/// </remarks>
3737
/// <value>Defaults to <see langword="null"/>, which is equivalent to <c>"complete"</c>.</value>

0 commit comments

Comments
 (0)