Remove obsolete APIs from codebase and standardize MCP diagnostic IDs (#985)

Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com>
Co-authored-by: MackinnonBuck <10456961+MackinnonBuck@users.noreply.github.com>
Co-authored-by: eiriktsarpalis <2813363+eiriktsarpalis@users.noreply.github.com>
Co-authored-by: Mackinnon Buck <mackinnon.buck@gmail.com>
Co-authored-by: jeffhandley <1031940+jeffhandley@users.noreply.github.com>
Co-authored-by: Jeff Handley <jeffhandley@users.noreply.github.com>
This commit is contained in:
Copilot 2025-12-02 10:28:46 -08:00 • committed by GitHub
commit 136755f2be
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
44 changed files with 663 additions and 2674 deletions

View file

@ -0,0 +1,27 @@
using System.Diagnostics.CodeAnalysis;
namespace ModelContextProtocol;
/// <summary>
/// Defines diagnostic IDs, Messages, and Urls for APIs annotated with <see cref="ExperimentalAttribute"/>.
/// </summary>
/// <remarks>
/// When an experimental API is associated with an experimental specification, the message
/// should refer to the specification version that introduces the feature and the SEP
/// when available. If there is a SEP associated with the experimental API, the Url should
/// point to the SEP issue.
/// <para>
/// Experimental diagnostic IDs are in the format MCP5###.
/// </para>
/// <para>
/// Diagnostic IDs cannot be reused when experimental API are removed or promoted to stable.
/// This ensures that users do not suppress warnings for new diagnostics with existing
/// suppressions that might be left in place from prior uses of the same diagnostic ID.
/// </para>
/// </remarks>
internal static class Experimentals
{
// public const string Tasks_DiagnosticId = "MCP5001";
// public const string Tasks_Message = "The Tasks feature is experimental within specification version 2025-11-25 and is subject to change. See SEP-1686 for more information.";
// public const string Tasks_Url = "https://github.com/modelcontextprotocol/modelcontextprotocol/issues/1686";
}

25
src/Common/Obsoletions.cs Normal file
View file

@ -0,0 +1,25 @@
namespace ModelContextProtocol;
/// <summary>
/// Defines diagnostic IDs, Messages, and Urls for APIs annotated with <see cref="ObsoleteAttribute"/>.
/// </summary>
/// <remarks>
/// When a deprecated API is associated with an specification change, the message
/// should refer to the specification version that introduces the change and the SEP
/// when available. If there is a SEP associated with the experimental API, the Url should
/// point to the SEP issue.
/// <para>
/// Obsolete diagnostic IDs are in the format MCP9###.
/// </para>
/// <para>
/// Diagnostic IDs cannot be reused when obsolete APIs are removed or restored.
/// This ensures that users do not suppress warnings for new diagnostics with existing
/// suppressions that might be left in place from prior uses of the same diagnostic ID.
/// </para>
/// </remarks>
internal static class Obsoletions
{
public const string LegacyTitledEnumSchema_DiagnosticId = "MCP9001";
public const string LegacyTitledEnumSchema_Message = "The EnumSchema and LegacyTitledEnumSchema APIs are deprecated as of specification version 2025-11-25 and will be removed in a future major version. See SEP-1330 for more information.";
public const string LegacyTitledEnumSchema_Url = "https://github.com/modelcontextprotocol/modelcontextprotocol/issues/1330";
}

View file

@ -0,0 +1,65 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
#if !NET
namespace System.Diagnostics.CodeAnalysis;
/// <summary>
/// Indicates that an API is experimental and it may change in the future.
/// </summary>
/// <remarks>
/// This attribute allows call sites to be flagged with a diagnostic that indicates that an experimental
/// feature is used. Authors can use this attribute to ship preview features in their assemblies.
/// </remarks>
[AttributeUsage(AttributeTargets.Assembly |
AttributeTargets.Module |
AttributeTargets.Class |
AttributeTargets.Struct |
AttributeTargets.Enum |
AttributeTargets.Constructor |
AttributeTargets.Method |
AttributeTargets.Property |
AttributeTargets.Field |
AttributeTargets.Event |
AttributeTargets.Interface |
AttributeTargets.Delegate, Inherited = false)]
internal sealed class ExperimentalAttribute : Attribute
{
/// <summary>
/// Initializes a new instance of the <see cref="ExperimentalAttribute"/> class, specifying the ID that the compiler will use
/// when reporting a use of the API the attribute applies to.
/// </summary>
/// <param name="diagnosticId">The ID that the compiler will use when reporting a use of the API the attribute applies to.</param>
public ExperimentalAttribute(string diagnosticId)
{
DiagnosticId = diagnosticId;
}
/// <summary>
/// Gets the ID that the compiler will use when reporting a use of the API the attribute applies to.
/// </summary>
/// <value>The unique diagnostic ID.</value>
/// <remarks>
/// The diagnostic ID is shown in build output for warnings and errors.
/// <para>This property represents the unique ID that can be used to suppress the warnings or errors, if needed.</para>
/// </remarks>
public string DiagnosticId { get; }
/// <summary>
/// Gets or sets an optional message associated with the experimental attribute.
/// </summary>
/// <value>The message that provides additional information about the experimental feature.</value>
/// <remarks>
/// This message can be used to provide more context or guidance about the experimental feature.
/// </remarks>
public string? Message { get; set; }
/// <summary>
/// Gets or sets the URL for corresponding documentation.
/// The API accepts a format string instead of an actual URL, creating a generic URL that includes the diagnostic ID.
/// </summary>
/// <value>The format string that represents a URL to corresponding documentation.</value>
/// <remarks>An example format string is <c>https://contoso.com/obsoletion-warnings/{0}</c>.</remarks>
public string? UrlFormat { get; set; }
}
#endif

View file

@ -0,0 +1,38 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
#if !NET
namespace System;
/// <summary>
/// Marks program elements that are no longer in use.
/// </summary>
[AttributeUsage(AttributeTargets.Class | AttributeTargets.Struct | AttributeTargets.Enum |
AttributeTargets.Interface | AttributeTargets.Constructor | AttributeTargets.Method | AttributeTargets.Property | AttributeTargets.Field | AttributeTargets.Event | AttributeTargets.Delegate,
Inherited = false)]
internal sealed class ObsoleteAttribute : Attribute
{
public ObsoleteAttribute()
{
}
public ObsoleteAttribute(string? message)
{
Message = message;
}
public ObsoleteAttribute(string? message, bool error)
{
Message = message;
IsError = error;
}
public string? Message { get; }
public bool IsError { get; }
public string? DiagnosticId { get; set; }
public string? UrlFormat { get; set; }
}
#endif

View file

@ -9,6 +9,14 @@ using System.Xml.Linq;
namespace ModelContextProtocol.Analyzers;
/// <summary>Provides the diagnostic descriptors used by the assembly.</summary>
/// <remarks>
/// Analyzer diagnostic IDs are in the format MCP### (or MCP1### if ever needed).
/// <para>
/// Diagnostic IDs cannot be reused if an analyzer is removed.
/// This ensures that users do not suppress warnings for new diagnostics with existing
/// suppressions that might be left in place from prior uses of the same diagnostic ID.
/// </para>
/// </remarks>
internal static class Diagnostics
{
public static DiagnosticDescriptor InvalidXmlDocumentation { get; } = new(

View file

@ -1,50 +0,0 @@
using ModelContextProtocol.Protocol;
using System.ComponentModel;
namespace ModelContextProtocol.Client;
/// <summary>
/// Represents an instance of a Model Context Protocol (MCP) client that connects to and communicates with an MCP server.
/// </summary>
[Obsolete($"Use {nameof(McpClient)} instead. This member will be removed in a subsequent release.")] // See: https://github.com/modelcontextprotocol/csharp-sdk/issues/774
[EditorBrowsable(EditorBrowsableState.Never)]
public interface IMcpClient : IMcpEndpoint
{
/// <summary>
/// Gets the capabilities supported by the connected server.
/// </summary>
/// <exception cref="InvalidOperationException">The client is not connected.</exception>
ServerCapabilities ServerCapabilities { get; }
/// <summary>
/// Gets the implementation information of the connected server.
/// </summary>
/// <remarks>
/// <para>
/// This property provides identification details about the connected server, including its name and version.
/// It is populated during the initialization handshake and is available after a successful connection.
/// </para>
/// <para>
/// This information can be useful for logging, debugging, compatibility checks, and displaying server
/// information to users.
/// </para>
/// </remarks>
/// <exception cref="InvalidOperationException">The client is not connected.</exception>
Implementation ServerInfo { get; }
/// <summary>
/// Gets any instructions describing how to use the connected server and its features.
/// </summary>
/// <remarks>
/// <para>
/// This property contains instructions provided by the server during initialization that explain
/// how to effectively use its capabilities. These instructions can include details about available
/// tools, expected input formats, limitations, or any other helpful information.
/// </para>
/// <para>
/// This can be used by clients to improve an LLM's understanding of available tools, prompts, and resources.
/// It can be thought of like a "hint" to the model and may be added to a system prompt.
/// </para>
/// </remarks>
string? ServerInstructions { get; }
}

View file

@ -10,9 +10,7 @@ namespace ModelContextProtocol.Client;
/// <summary>
/// Represents an instance of a Model Context Protocol (MCP) client session that connects to and communicates with an MCP server.
/// </summary>
#pragma warning disable CS0618 // Type or member is obsolete
public abstract partial class McpClient : McpSession, IMcpClient
#pragma warning restore CS0618 // Type or member is obsolete
public abstract partial class McpClient : McpSession
{
/// <summary>Creates an <see cref="McpClient"/>, connecting it to the specified server.</summary>
/// <param name="clientTransport">The transport instance used to communicate with the server.</param>

View file

@ -5,9 +5,7 @@ namespace ModelContextProtocol.Client;
/// <summary>
/// Represents an instance of a Model Context Protocol (MCP) client session that connects to and communicates with an MCP server.
/// </summary>
#pragma warning disable CS0618 // Type or member is obsolete
public abstract partial class McpClient : McpSession, IMcpClient
#pragma warning restore CS0618 // Type or member is obsolete
public abstract partial class McpClient : McpSession
{
/// <summary>
/// Gets the capabilities supported by the connected server.

View file

@ -1,608 +0,0 @@
using Microsoft.Extensions.AI;
using ModelContextProtocol.Protocol;
using System.ComponentModel;
using System.Diagnostics.CodeAnalysis;
using System.Runtime.CompilerServices;
using System.Text.Json;
namespace ModelContextProtocol.Client;
/// <summary>
/// Provides extension methods for interacting with an <see cref="McpClient"/>.
/// </summary>
/// <remarks>
/// This class contains extension methods that simplify common operations with an MCP client,
/// such as pinging a server, listing and working with tools, prompts, and resources, and
/// managing subscriptions to resources.
/// </remarks>
public static class McpClientExtensions
{
/// <summary>
/// Sends a ping request to verify server connectivity.
/// </summary>
/// <param name="client">The client instance used to communicate with the MCP server.</param>
/// <param name="cancellationToken">The <see cref="CancellationToken"/> to monitor for cancellation requests. The default is <see cref="CancellationToken.None"/>.</param>
/// <returns>A task that completes when the ping is successful.</returns>
/// <remarks>
/// <para>
/// This method is used to check if the MCP server is online and responding to requests.
/// It can be useful for health checking, ensuring the connection is established, or verifying
/// that the client has proper authorization to communicate with the server.
/// </para>
/// <para>
/// The ping operation is lightweight and does not require any parameters. A successful completion
/// of the task indicates that the server is operational and accessible.
/// </para>
/// </remarks>
/// <exception cref="ArgumentNullException"><paramref name="client"/> is <see langword="null"/>.</exception>
/// <exception cref="McpException">The server cannot be reached or returned an error response.</exception>
[Obsolete($"Use {nameof(McpClient)}.{nameof(McpClient.PingAsync)} instead. This member will be removed in a subsequent release.")] // See: https://github.com/modelcontextprotocol/csharp-sdk/issues/774
[EditorBrowsable(EditorBrowsableState.Never)]
public static Task PingAsync(this IMcpClient client, CancellationToken cancellationToken = default)
=> AsClientOrThrow(client).PingAsync(cancellationToken);
/// <summary>
/// Retrieves a list of available tools from the server.
/// </summary>
/// <param name="client">The client instance used to communicate with the MCP server.</param>
/// <param name="serializerOptions">The serializer options governing tool parameter serialization. If null, the default options are used.</param>
/// <param name="cancellationToken">The <see cref="CancellationToken"/> to monitor for cancellation requests. The default is <see cref="CancellationToken.None"/>.</param>
/// <returns>A list of all available tools as <see cref="McpClientTool"/> instances.</returns>
/// <remarks>
/// <para>
/// This method fetches all available tools from the MCP server and returns them as a complete list.
/// It automatically handles pagination with cursors if the server responds with only a portion per request.
/// </para>
/// <para>
/// For servers with a large number of tools and that responds with paginated responses, consider using
/// <see cref="EnumerateToolsAsync"/> instead, because it streams tools as they arrive rather than loading them all at once.
/// </para>
/// <para>
/// The serializer options provided are flowed to each <see cref="McpClientTool"/> and will be used
/// when invoking tools in order to serialize any parameters.
/// </para>
/// </remarks>
/// <example>
/// <code>
/// // Get all tools available on the server
/// var tools = await mcpClient.ListToolsAsync();
///
/// // Use tools with an AI client
/// ChatOptions chatOptions = new()
/// {
/// Tools = [.. tools]
/// };
///
/// await foreach (var update in chatClient.GetStreamingResponseAsync(userMessage, chatOptions))
/// {
/// Console.Write(update);
/// }
/// </code>
/// </example>
/// <exception cref="ArgumentNullException"><paramref name="client"/> is <see langword="null"/>.</exception>
[Obsolete($"Use {nameof(McpClient)}.{nameof(McpClient.ListToolsAsync)} instead. This member will be removed in a subsequent release.")] // See: https://github.com/modelcontextprotocol/csharp-sdk/issues/774
[EditorBrowsable(EditorBrowsableState.Never)]
public static ValueTask<IList<McpClientTool>> ListToolsAsync(
this IMcpClient client,
JsonSerializerOptions? serializerOptions = null,
CancellationToken cancellationToken = default)
=> AsClientOrThrow(client).ListToolsAsync(serializerOptions, cancellationToken);
/// <summary>
/// Creates an enumerable for asynchronously enumerating all available tools from the server.
/// </summary>
/// <param name="client">The client instance used to communicate with the MCP server.</param>
/// <param name="serializerOptions">The serializer options governing tool parameter serialization. If null, the default options are used.</param>
/// <param name="cancellationToken">The <see cref="CancellationToken"/> to monitor for cancellation requests. The default is <see cref="CancellationToken.None"/>.</param>
/// <returns>An asynchronous sequence of all available tools as <see cref="McpClientTool"/> instances.</returns>
/// <remarks>
/// <para>
/// This method uses asynchronous enumeration to retrieve tools from the server, which allows processing tools
/// as they arrive rather than waiting for all tools to be retrieved. The method automatically handles pagination
/// with cursors if the server responds with tools split across multiple responses.
/// </para>
/// <para>
/// The serializer options provided are flowed to each <see cref="McpClientTool"/> and will be used
/// when invoking tools to serialize any parameters.
/// </para>
/// <para>
/// Every iteration through the returned <see cref="IAsyncEnumerable{McpClientTool}"/>
/// will result in re-querying the server and yielding the sequence of available tools.
/// </para>
/// </remarks>
/// <example>
/// <code>
/// // Enumerate all tools available on the server
/// await foreach (var tool in client.EnumerateToolsAsync())
/// {
/// Console.WriteLine($"Tool: {tool.Name}");
/// }
/// </code>
/// </example>
/// <exception cref="ArgumentNullException"><paramref name="client"/> is <see langword="null"/>.</exception>
[Obsolete($"Use {nameof(McpClient)}.{nameof(McpClient.EnumerateToolsAsync)} instead. This member will be removed in a subsequent release.")] // See: https://github.com/modelcontextprotocol/csharp-sdk/issues/774
[EditorBrowsable(EditorBrowsableState.Never)]
public static IAsyncEnumerable<McpClientTool> EnumerateToolsAsync(
this IMcpClient client,
JsonSerializerOptions? serializerOptions = null,
CancellationToken cancellationToken = default)
=> AsClientOrThrow(client).EnumerateToolsAsync(serializerOptions, cancellationToken);
/// <summary>
/// Retrieves a list of available prompts from the server.
/// </summary>
/// <param name="client">The client instance used to communicate with the MCP server.</param>
/// <param name="cancellationToken">The <see cref="CancellationToken"/> to monitor for cancellation requests. The default is <see cref="CancellationToken.None"/>.</param>
/// <returns>A list of all available prompts as <see cref="McpClientPrompt"/> instances.</returns>
/// <remarks>
/// <para>
/// This method fetches all available prompts from the MCP server and returns them as a complete list.
/// It automatically handles pagination with cursors if the server responds with only a portion per request.
/// </para>
/// <para>
/// For servers with a large number of prompts and that responds with paginated responses, consider using
/// <see cref="EnumeratePromptsAsync"/> instead, as it streams prompts as they arrive rather than loading them all at once.
/// </para>
/// </remarks>
/// <exception cref="ArgumentNullException"><paramref name="client"/> is <see langword="null"/>.</exception>
[Obsolete($"Use {nameof(McpClient)}.{nameof(McpClient.ListPromptsAsync)} instead. This member will be removed in a subsequent release.")] // See: https://github.com/modelcontextprotocol/csharp-sdk/issues/774
[EditorBrowsable(EditorBrowsableState.Never)]
public static ValueTask<IList<McpClientPrompt>> ListPromptsAsync(
this IMcpClient client, CancellationToken cancellationToken = default)
=> AsClientOrThrow(client).ListPromptsAsync(cancellationToken);
/// <summary>
/// Creates an enumerable for asynchronously enumerating all available prompts from the server.
/// </summary>
/// <param name="client">The client instance used to communicate with the MCP server.</param>
/// <param name="cancellationToken">The <see cref="CancellationToken"/> to monitor for cancellation requests. The default is <see cref="CancellationToken.None"/>.</param>
/// <returns>An asynchronous sequence of all available prompts as <see cref="McpClientPrompt"/> instances.</returns>
/// <remarks>
/// <para>
/// This method uses asynchronous enumeration to retrieve prompts from the server, which allows processing prompts
/// as they arrive rather than waiting for all prompts to be retrieved. The method automatically handles pagination
/// with cursors if the server responds with prompts split across multiple responses.
/// </para>
/// <para>
/// Every iteration through the returned <see cref="IAsyncEnumerable{McpClientPrompt}"/>
/// will result in re-querying the server and yielding the sequence of available prompts.
/// </para>
/// </remarks>
/// <example>
/// <code>
/// // Enumerate all prompts available on the server
/// await foreach (var prompt in client.EnumeratePromptsAsync())
/// {
/// Console.WriteLine($"Prompt: {prompt.Name}");
/// }
/// </code>
/// </example>
/// <exception cref="ArgumentNullException"><paramref name="client"/> is <see langword="null"/>.</exception>
[Obsolete($"Use {nameof(McpClient)}.{nameof(McpClient.EnumeratePromptsAsync)} instead. This member will be removed in a subsequent release.")] // See: https://github.com/modelcontextprotocol/csharp-sdk/issues/774
[EditorBrowsable(EditorBrowsableState.Never)]
public static IAsyncEnumerable<McpClientPrompt> EnumeratePromptsAsync(
this IMcpClient client, CancellationToken cancellationToken = default)
=> AsClientOrThrow(client).EnumeratePromptsAsync(cancellationToken);
/// <summary>
/// Retrieves a specific prompt from the MCP server.
/// </summary>
/// <param name="client">The client instance used to communicate with the MCP server.</param>
/// <param name="name">The name of the prompt to retrieve.</param>
/// <param name="arguments">Optional arguments for the prompt. Keys are parameter names, and values are the argument values.</param>
/// <param name="serializerOptions">The serialization options governing argument serialization.</param>
/// <param name="cancellationToken">The <see cref="CancellationToken"/> to monitor for cancellation requests. The default is <see cref="CancellationToken.None"/>.</param>
/// <returns>A task containing the prompt's result with content and messages.</returns>
/// <remarks>
/// <para>
/// This method sends a request to the MCP server to create the specified prompt with the provided arguments.
/// The server will process the arguments and return a prompt containing messages or other content.
/// </para>
/// <para>
/// Arguments are serialized into JSON and passed to the server, where they may be used to customize the
/// prompt's behavior or content. Each prompt may have different argument requirements.
/// </para>
/// <para>
/// The returned <see cref="GetPromptResult"/> contains a collection of <see cref="PromptMessage"/> objects,
/// which can be converted to <see cref="ChatMessage"/> objects using the <see cref="AIContentExtensions.ToChatMessages"/> method.
/// </para>
/// </remarks>
/// <exception cref="McpException">Thrown when the prompt does not exist, when required arguments are missing, or when the server encounters an error processing the prompt.</exception>
/// <exception cref="ArgumentNullException"><paramref name="client"/> is <see langword="null"/>.</exception>
[Obsolete($"Use {nameof(McpClient)}.{nameof(McpClient.GetPromptAsync)} instead. This member will be removed in a subsequent release.")] // See: https://github.com/modelcontextprotocol/csharp-sdk/issues/774
[EditorBrowsable(EditorBrowsableState.Never)]
public static ValueTask<GetPromptResult> GetPromptAsync(
this IMcpClient client,
string name,
IReadOnlyDictionary<string, object?>? arguments = null,
JsonSerializerOptions? serializerOptions = null,
CancellationToken cancellationToken = default)
=> AsClientOrThrow(client).GetPromptAsync(name, arguments, serializerOptions, cancellationToken);
/// <summary>
/// Retrieves a list of available resource templates from the server.
/// </summary>
/// <param name="client">The client instance used to communicate with the MCP server.</param>
/// <param name="cancellationToken">The <see cref="CancellationToken"/> to monitor for cancellation requests. The default is <see cref="CancellationToken.None"/>.</param>
/// <returns>A list of all available resource templates as <see cref="ResourceTemplate"/> instances.</returns>
/// <remarks>
/// <para>
/// This method fetches all available resource templates from the MCP server and returns them as a complete list.
/// It automatically handles pagination with cursors if the server responds with only a portion per request.
/// </para>
/// <para>
/// For servers with a large number of resource templates and that responds with paginated responses, consider using
/// <see cref="EnumerateResourceTemplatesAsync"/> instead, as it streams templates as they arrive rather than loading them all at once.
/// </para>
/// </remarks>
/// <exception cref="ArgumentNullException"><paramref name="client"/> is <see langword="null"/>.</exception>
[Obsolete($"Use {nameof(McpClient)}.{nameof(McpClient.ListResourceTemplatesAsync)} instead. This member will be removed in a subsequent release.")] // See: https://github.com/modelcontextprotocol/csharp-sdk/issues/774
[EditorBrowsable(EditorBrowsableState.Never)]
public static ValueTask<IList<McpClientResourceTemplate>> ListResourceTemplatesAsync(
this IMcpClient client, CancellationToken cancellationToken = default)
=> AsClientOrThrow(client).ListResourceTemplatesAsync(cancellationToken);
/// <summary>
/// Creates an enumerable for asynchronously enumerating all available resource templates from the server.
/// </summary>
/// <param name="client">The client instance used to communicate with the MCP server.</param>
/// <param name="cancellationToken">The <see cref="CancellationToken"/> to monitor for cancellation requests. The default is <see cref="CancellationToken.None"/>.</param>
/// <returns>An asynchronous sequence of all available resource templates as <see cref="ResourceTemplate"/> instances.</returns>
/// <remarks>
/// <para>
/// This method uses asynchronous enumeration to retrieve resource templates from the server, which allows processing templates
/// as they arrive rather than waiting for all templates to be retrieved. The method automatically handles pagination
/// with cursors if the server responds with templates split across multiple responses.
/// </para>
/// <para>
/// Every iteration through the returned <see cref="IAsyncEnumerable{McpClientResourceTemplate}"/>
/// will result in re-querying the server and yielding the sequence of available resource templates.
/// </para>
/// </remarks>
/// <example>
/// <code>
/// // Enumerate all resource templates available on the server
/// await foreach (var template in client.EnumerateResourceTemplatesAsync())
/// {
/// Console.WriteLine($"Template: {template.Name}");
/// }
/// </code>
/// </example>
/// <exception cref="ArgumentNullException"><paramref name="client"/> is <see langword="null"/>.</exception>
[Obsolete($"Use {nameof(McpClient)}.{nameof(McpClient.EnumerateResourceTemplatesAsync)} instead. This member will be removed in a subsequent release.")] // See: https://github.com/modelcontextprotocol/csharp-sdk/issues/774
[EditorBrowsable(EditorBrowsableState.Never)]
public static IAsyncEnumerable<McpClientResourceTemplate> EnumerateResourceTemplatesAsync(
this IMcpClient client, CancellationToken cancellationToken = default)
=> AsClientOrThrow(client).EnumerateResourceTemplatesAsync(cancellationToken);
/// <summary>
/// Retrieves a list of available resources from the server.
/// </summary>
/// <param name="client">The client instance used to communicate with the MCP server.</param>
/// <param name="cancellationToken">The <see cref="CancellationToken"/> to monitor for cancellation requests. The default is <see cref="CancellationToken.None"/>.</param>
/// <returns>A list of all available resources as <see cref="Resource"/> instances.</returns>
/// <remarks>
/// <para>
/// This method fetches all available resources from the MCP server and returns them as a complete list.
/// It automatically handles pagination with cursors if the server responds with only a portion per request.
/// </para>
/// <para>
/// For servers with a large number of resources and that responds with paginated responses, consider using
/// <see cref="EnumerateResourcesAsync"/> instead, as it streams resources as they arrive rather than loading them all at once.
/// </para>
/// </remarks>
/// <example>
/// <code>
/// // Get all resources available on the server
/// var resources = await client.ListResourcesAsync();
///
/// // Display information about each resource
/// foreach (var resource in resources)
/// {
/// Console.WriteLine($"Resource URI: {resource.Uri}");
/// }
/// </code>
/// </example>
/// <exception cref="ArgumentNullException"><paramref name="client"/> is <see langword="null"/>.</exception>
[Obsolete($"Use {nameof(McpClient)}.{nameof(McpClient.ListResourcesAsync)} instead. This member will be removed in a subsequent release.")] // See: https://github.com/modelcontextprotocol/csharp-sdk/issues/774
[EditorBrowsable(EditorBrowsableState.Never)]
public static ValueTask<IList<McpClientResource>> ListResourcesAsync(
this IMcpClient client, CancellationToken cancellationToken = default)
=> AsClientOrThrow(client).ListResourcesAsync(cancellationToken);
/// <summary>
/// Creates an enumerable for asynchronously enumerating all available resources from the server.
/// </summary>
/// <param name="client">The client instance used to communicate with the MCP server.</param>
/// <param name="cancellationToken">The <see cref="CancellationToken"/> to monitor for cancellation requests. The default is <see cref="CancellationToken.None"/>.</param>
/// <returns>An asynchronous sequence of all available resources as <see cref="Resource"/> instances.</returns>
/// <remarks>
/// <para>
/// This method uses asynchronous enumeration to retrieve resources from the server, which allows processing resources
/// as they arrive rather than waiting for all resources to be retrieved. The method automatically handles pagination
/// with cursors if the server responds with resources split across multiple responses.
/// </para>
/// <para>
/// Every iteration through the returned <see cref="IAsyncEnumerable{McpClientResource}"/>
/// will result in re-querying the server and yielding the sequence of available resources.
/// </para>
/// </remarks>
/// <example>
/// <code>
/// // Enumerate all resources available on the server
/// await foreach (var resource in client.EnumerateResourcesAsync())
/// {
/// Console.WriteLine($"Resource URI: {resource.Uri}");
/// }
/// </code>
/// </example>
/// <exception cref="ArgumentNullException"><paramref name="client"/> is <see langword="null"/>.</exception>
[Obsolete($"Use {nameof(McpClient)}.{nameof(McpClient.EnumerateResourcesAsync)} instead. This member will be removed in a subsequent release.")] // See: https://github.com/modelcontextprotocol/csharp-sdk/issues/774
[EditorBrowsable(EditorBrowsableState.Never)]
public static IAsyncEnumerable<McpClientResource> EnumerateResourcesAsync(
this IMcpClient client, CancellationToken cancellationToken = default)
=> AsClientOrThrow(client).EnumerateResourcesAsync(cancellationToken);
/// <summary>
/// Reads a resource from the server.
/// </summary>
/// <param name="client">The client instance used to communicate with the MCP server.</param>
/// <param name="uri">The uri of the resource.</param>
/// <param name="cancellationToken">The <see cref="CancellationToken"/> to monitor for cancellation requests. The default is <see cref="CancellationToken.None"/>.</param>
/// <exception cref="ArgumentNullException"><paramref name="client"/> is <see langword="null"/>.</exception>
/// <exception cref="ArgumentNullException"><paramref name="uri"/> is <see langword="null"/>.</exception>
/// <exception cref="ArgumentException"><paramref name="uri"/> is empty or composed entirely of whitespace.</exception>
[Obsolete($"Use {nameof(McpClient)}.{nameof(McpClient.ReadResourceAsync)} instead. This member will be removed in a subsequent release.")] // See: https://github.com/modelcontextprotocol/csharp-sdk/issues/774
[EditorBrowsable(EditorBrowsableState.Never)]
public static ValueTask<ReadResourceResult> ReadResourceAsync(
this IMcpClient client, string uri, CancellationToken cancellationToken = default)
=> AsClientOrThrow(client).ReadResourceAsync(uri, cancellationToken);
/// <summary>
/// Reads a resource from the server.
/// </summary>
/// <param name="client">The client instance used to communicate with the MCP server.</param>
/// <param name="uri">The uri of the resource.</param>
/// <param name="cancellationToken">The <see cref="CancellationToken"/> to monitor for cancellation requests. The default is <see cref="CancellationToken.None"/>.</param>
/// <exception cref="ArgumentNullException"><paramref name="client"/> is <see langword="null"/>.</exception>
/// <exception cref="ArgumentNullException"><paramref name="uri"/> is <see langword="null"/>.</exception>
[Obsolete($"Use {nameof(McpClient)}.{nameof(McpClient.ReadResourceAsync)} instead. This member will be removed in a subsequent release.")] // See: https://github.com/modelcontextprotocol/csharp-sdk/issues/774
[EditorBrowsable(EditorBrowsableState.Never)]
public static ValueTask<ReadResourceResult> ReadResourceAsync(
this IMcpClient client, Uri uri, CancellationToken cancellationToken = default)
=> AsClientOrThrow(client).ReadResourceAsync(uri, cancellationToken);
/// <summary>
/// Reads a resource from the server.
/// </summary>
/// <param name="client">The client instance used to communicate with the MCP server.</param>
/// <param name="uriTemplate">The uri template of the resource.</param>
/// <param name="arguments">Arguments to use to format <paramref name="uriTemplate"/>.</param>
/// <param name="cancellationToken">The <see cref="CancellationToken"/> to monitor for cancellation requests. The default is <see cref="CancellationToken.None"/>.</param>
/// <exception cref="ArgumentNullException"><paramref name="client"/> is <see langword="null"/>.</exception>
/// <exception cref="ArgumentNullException"><paramref name="uriTemplate"/> is <see langword="null"/>.</exception>
/// <exception cref="ArgumentException"><paramref name="uriTemplate"/> is empty or composed entirely of whitespace.</exception>
[Obsolete($"Use {nameof(McpClient)}.{nameof(McpClient.ReadResourceAsync)} instead. This member will be removed in a subsequent release.")] // See: https://github.com/modelcontextprotocol/csharp-sdk/issues/774
[EditorBrowsable(EditorBrowsableState.Never)]
public static ValueTask<ReadResourceResult> ReadResourceAsync(
this IMcpClient client, string uriTemplate, IReadOnlyDictionary<string, object?> arguments, CancellationToken cancellationToken = default)
=> AsClientOrThrow(client).ReadResourceAsync(uriTemplate, arguments, cancellationToken);
/// <summary>
/// Requests completion suggestions for a prompt argument or resource reference.
/// </summary>
/// <param name="client">The client instance used to communicate with the MCP server.</param>
/// <param name="reference">The reference object specifying the type and optional URI or name.</param>
/// <param name="argumentName">The name of the argument for which completions are requested.</param>
/// <param name="argumentValue">The current value of the argument, used to filter relevant completions.</param>
/// <param name="cancellationToken">The <see cref="CancellationToken"/> to monitor for cancellation requests. The default is <see cref="CancellationToken.None"/>.</param>
/// <returns>A <see cref="CompleteResult"/> containing completion suggestions.</returns>
/// <remarks>
/// <para>
/// This method allows clients to request auto-completion suggestions for arguments in a prompt template
/// or for resource references.
/// </para>
/// <para>
/// When working with prompt references, the server will return suggestions for the specified argument
/// that match or begin with the current argument value. This is useful for implementing intelligent
/// auto-completion in user interfaces.
/// </para>
/// <para>
/// When working with resource references, the server will return suggestions relevant to the specified
/// resource URI.
/// </para>
/// </remarks>
/// <exception cref="ArgumentNullException"><paramref name="client"/> is <see langword="null"/>.</exception>
/// <exception cref="ArgumentNullException"><paramref name="reference"/> is <see langword="null"/>.</exception>
/// <exception cref="ArgumentNullException"><paramref name="argumentName"/> is <see langword="null"/>.</exception>
/// <exception cref="ArgumentException"><paramref name="argumentName"/> is empty or composed entirely of whitespace.</exception>
/// <exception cref="McpException">The server returned an error response.</exception>
[Obsolete($"Use {nameof(McpClient)}.{nameof(McpClient.CompleteAsync)} instead. This member will be removed in a subsequent release.")] // See: https://github.com/modelcontextprotocol/csharp-sdk/issues/774
[EditorBrowsable(EditorBrowsableState.Never)]
public static ValueTask<CompleteResult> CompleteAsync(this IMcpClient client, Reference reference, string argumentName, string argumentValue, CancellationToken cancellationToken = default)
=> AsClientOrThrow(client).CompleteAsync(reference, argumentName, argumentValue, cancellationToken);
/// <summary>
/// Subscribes to a resource on the server to receive notifications when it changes.
/// </summary>
/// <param name="client">The client instance used to communicate with the MCP server.</param>
/// <param name="uri">The URI of the resource to which to subscribe.</param>
/// <param name="cancellationToken">The <see cref="CancellationToken"/> to monitor for cancellation requests. The default is <see cref="CancellationToken.None"/>.</param>
/// <returns>A task that represents the asynchronous operation.</returns>
/// <remarks>
/// <para>
/// This method allows the client to register interest in a specific resource identified by its URI.
/// When the resource changes, the server will send notifications to the client, enabling real-time
/// updates without polling.
/// </para>
/// <para>
/// The subscription remains active until explicitly unsubscribed using <see cref="M:UnsubscribeFromResourceAsync"/>
/// or until the client disconnects from the server.
/// </para>
/// <para>
/// To handle resource change notifications, register an event handler for the appropriate notification events,
/// such as with <see cref="IMcpEndpoint.RegisterNotificationHandler"/>.
/// </para>
/// </remarks>
/// <exception cref="ArgumentNullException"><paramref name="client"/> is <see langword="null"/>.</exception>
/// <exception cref="ArgumentNullException"><paramref name="uri"/> is <see langword="null"/>.</exception>
/// <exception cref="ArgumentException"><paramref name="uri"/> is empty or composed entirely of whitespace.</exception>
[Obsolete($"Use {nameof(McpClient)}.{nameof(McpClient.SubscribeToResourceAsync)} instead. This member will be removed in a subsequent release.")] // See: https://github.com/modelcontextprotocol/csharp-sdk/issues/774
[EditorBrowsable(EditorBrowsableState.Never)]
public static Task SubscribeToResourceAsync(this IMcpClient client, string uri, CancellationToken cancellationToken = default)
=> AsClientOrThrow(client).SubscribeToResourceAsync(uri, cancellationToken);
/// <summary>
/// Subscribes to a resource on the server to receive notifications when it changes.
/// </summary>
/// <param name="client">The client instance used to communicate with the MCP server.</param>
/// <param name="uri">The URI of the resource to which to subscribe.</param>
/// <param name="cancellationToken">The <see cref="CancellationToken"/> to monitor for cancellation requests. The default is <see cref="CancellationToken.None"/>.</param>
/// <returns>A task that represents the asynchronous operation.</returns>
/// <remarks>
/// <para>
/// This method allows the client to register interest in a specific resource identified by its URI.
/// When the resource changes, the server will send notifications to the client, enabling real-time
/// updates without polling.
/// </para>
/// <para>
/// The subscription remains active until explicitly unsubscribed using <see cref="M:UnsubscribeFromResourceAsync"/>
/// or until the client disconnects from the server.
/// </para>
/// <para>
/// To handle resource change notifications, register an event handler for the appropriate notification events,
/// such as with <see cref="IMcpEndpoint.RegisterNotificationHandler"/>.
/// </para>
/// </remarks>
/// <exception cref="ArgumentNullException"><paramref name="client"/> is <see langword="null"/>.</exception>
/// <exception cref="ArgumentNullException"><paramref name="uri"/> is <see langword="null"/>.</exception>
[Obsolete($"Use {nameof(McpClient)}.{nameof(McpClient.SubscribeToResourceAsync)} instead. This member will be removed in a subsequent release.")] // See: https://github.com/modelcontextprotocol/csharp-sdk/issues/774
[EditorBrowsable(EditorBrowsableState.Never)]
public static Task SubscribeToResourceAsync(this IMcpClient client, Uri uri, CancellationToken cancellationToken = default)
=> AsClientOrThrow(client).SubscribeToResourceAsync(uri, cancellationToken);
/// <summary>
/// Unsubscribes from a resource on the server to stop receiving notifications about its changes.
/// </summary>
/// <param name="client">The client instance used to communicate with the MCP server.</param>
/// <param name="uri">The URI of the resource to unsubscribe from.</param>
/// <param name="cancellationToken">The <see cref="CancellationToken"/> to monitor for cancellation requests. The default is <see cref="CancellationToken.None"/>.</param>
/// <returns>A task that represents the asynchronous operation.</returns>
/// <remarks>
/// <para>
/// This method cancels a previous subscription to a resource, stopping the client from receiving
/// notifications when that resource changes.
/// </para>
/// <para>
/// The unsubscribe operation is idempotent, meaning it can be called multiple times for the same
/// resource without causing errors, even if there is no active subscription.
/// </para>
/// <para>
/// Due to the nature of the MCP protocol, it is possible the client may receive notifications after
/// unsubscribing if those notifications were issued by the server prior to the unsubscribe request being received.
/// </para>
/// </remarks>
/// <exception cref="ArgumentNullException"><paramref name="client"/> is <see langword="null"/>.</exception>
/// <exception cref="ArgumentNullException"><paramref name="uri"/> is <see langword="null"/>.</exception>
/// <exception cref="ArgumentException"><paramref name="uri"/> is empty or composed entirely of whitespace.</exception>
[Obsolete($"Use {nameof(McpClient)}.{nameof(McpClient.UnsubscribeFromResourceAsync)} instead. This member will be removed in a subsequent release.")] // See: https://github.com/modelcontextprotocol/csharp-sdk/issues/774
[EditorBrowsable(EditorBrowsableState.Never)]
public static Task UnsubscribeFromResourceAsync(this IMcpClient client, string uri, CancellationToken cancellationToken = default)
=> AsClientOrThrow(client).UnsubscribeFromResourceAsync(uri, cancellationToken);
/// <summary>
/// Unsubscribes from a resource on the server to stop receiving notifications about its changes.
/// </summary>
/// <param name="client">The client instance used to communicate with the MCP server.</param>
/// <param name="uri">The URI of the resource to unsubscribe from.</param>
/// <param name="cancellationToken">The <see cref="CancellationToken"/> to monitor for cancellation requests. The default is <see cref="CancellationToken.None"/>.</param>
/// <returns>A task that represents the asynchronous operation.</returns>
/// <remarks>
/// <para>
/// This method cancels a previous subscription to a resource, stopping the client from receiving
/// notifications when that resource changes.
/// </para>
/// <para>
/// The unsubscribe operation is idempotent, meaning it can be called multiple times for the same
/// resource without causing errors, even if there is no active subscription.
/// </para>
/// <para>
/// Due to the nature of the MCP protocol, it is possible the client may receive notifications after
/// unsubscribing if those notifications were issued by the server prior to the unsubscribe request being received.
/// </para>
/// </remarks>
/// <exception cref="ArgumentNullException"><paramref name="client"/> is <see langword="null"/>.</exception>
/// <exception cref="ArgumentNullException"><paramref name="uri"/> is <see langword="null"/>.</exception>
[Obsolete($"Use {nameof(McpClient)}.{nameof(McpClient.UnsubscribeFromResourceAsync)} instead. This member will be removed in a subsequent release.")] // See: https://github.com/modelcontextprotocol/csharp-sdk/issues/774
[EditorBrowsable(EditorBrowsableState.Never)]
public static Task UnsubscribeFromResourceAsync(this IMcpClient client, Uri uri, CancellationToken cancellationToken = default)
=> AsClientOrThrow(client).UnsubscribeFromResourceAsync(uri, cancellationToken);
/// <summary>
/// Invokes a tool on the server.
/// </summary>
/// <param name="client">The client instance used to communicate with the MCP server.</param>
/// <param name="toolName">The name of the tool to call on the server..</param>
/// <param name="arguments">An optional dictionary of arguments to pass to the tool. Each key represents a parameter name,
/// and its associated value represents the argument value.
/// </param>
/// <param name="progress">
/// An optional <see cref="IProgress{T}"/> to have progress notifications reported to it. Setting this to a non-<see langword="null"/>
/// value will result in a progress token being included in the call, and any resulting progress notifications during the operation
/// routed to this instance.
/// </param>
/// <param name="serializerOptions">
/// The JSON serialization options governing argument serialization. If <see langword="null"/>, the default serialization options are used.
/// </param>
/// <param name="cancellationToken">The <see cref="CancellationToken"/> to monitor for cancellation requests. The default is <see cref="CancellationToken.None"/>.</param>
/// <returns>
/// A task containing the <see cref="CallToolResult"/> from the tool execution. The response includes
/// the tool's output content, which may be structured data, text, or an error message.
/// </returns>
/// <exception cref="ArgumentNullException"><paramref name="client"/> is <see langword="null"/>.</exception>
/// <exception cref="ArgumentNullException"><paramref name="toolName"/> is <see langword="null"/>.</exception>
/// <exception cref="McpException">The server could not find the requested tool, or the server encountered an error while processing the request.</exception>
/// <example>
/// <code>
/// // Call a simple echo tool with a string argument
/// var result = await client.CallToolAsync(
/// "echo",
/// new Dictionary&lt;string, object?&gt;
/// {
/// ["message"] = "Hello MCP!"
/// });
/// </code>
/// </example>
[Obsolete($"Use {nameof(McpClient)}.{nameof(McpClient.CallToolAsync)} instead. This member will be removed in a subsequent release.")] // See: https://github.com/modelcontextprotocol/csharp-sdk/issues/774
[EditorBrowsable(EditorBrowsableState.Never)]
public static ValueTask<CallToolResult> CallToolAsync(
this IMcpClient client,
string toolName,
IReadOnlyDictionary<string, object?>? arguments = null,
IProgress<ProgressNotificationValue>? progress = null,
JsonSerializerOptions? serializerOptions = null,
CancellationToken cancellationToken = default)
=> AsClientOrThrow(client).CallToolAsync(toolName, arguments, progress, serializerOptions, cancellationToken);
[MethodImpl(MethodImplOptions.AggressiveInlining)]
#pragma warning disable CS0618 // Type or member is obsolete
private static McpClient AsClientOrThrow(IMcpClient client, [CallerMemberName] string memberName = "")
#pragma warning restore CS0618 // Type or member is obsolete
{
if (client is not McpClient mcpClient)
{
ThrowInvalidEndpointType(memberName);
}
return mcpClient;
[DoesNotReturn]
[MethodImpl(MethodImplOptions.NoInlining)]
static void ThrowInvalidEndpointType(string memberName)
=> throw new InvalidOperationException(
$"Only arguments assignable to '{nameof(McpClient)}' are supported. " +
$"Prefer using '{nameof(McpClient)}.{memberName}' instead, as " +
$"'{nameof(McpClientExtensions)}.{memberName}' is obsolete and will be " +
$"removed in the future.");
}
}

View file

@ -1,35 +0,0 @@
using Microsoft.Extensions.Logging;
using System.ComponentModel;
namespace ModelContextProtocol.Client;
/// <summary>
/// Provides factory methods for creating Model Context Protocol (MCP) clients.
/// </summary>
/// <remarks>
/// This factory class is the primary way to instantiate <see cref="IMcpClient"/> instances
/// that connect to MCP servers. It handles the creation and connection
/// of appropriate implementations through the supplied transport.
/// </remarks>
[Obsolete($"Use {nameof(McpClient)}.{nameof(McpClient.CreateAsync)} instead. This member will be removed in a subsequent release.")] // See: https://github.com/modelcontextprotocol/csharp-sdk/issues/774
[EditorBrowsable(EditorBrowsableState.Never)]
public static partial class McpClientFactory
{
/// <summary>Creates an <see cref="IMcpClient"/>, connecting it to the specified server.</summary>
/// <param name="clientTransport">The transport instance used to communicate with the server.</param>
/// <param name="clientOptions">
/// A client configuration object which specifies client capabilities and protocol version.
/// If <see langword="null"/>, details based on the current process will be employed.
/// </param>
/// <param name="loggerFactory">A logger factory for creating loggers for clients.</param>
/// <param name="cancellationToken">The <see cref="CancellationToken"/> to monitor for cancellation requests. The default is <see cref="CancellationToken.None"/>.</param>
/// <returns>An <see cref="IMcpClient"/> that's connected to the specified server.</returns>
/// <exception cref="ArgumentNullException"><paramref name="clientTransport"/> is <see langword="null"/>.</exception>
/// <exception cref="ArgumentNullException"><paramref name="clientOptions"/> is <see langword="null"/>.</exception>
public static async Task<IMcpClient> CreateAsync(
IClientTransport clientTransport,
McpClientOptions? clientOptions = null,
ILoggerFactory? loggerFactory = null,
CancellationToken cancellationToken = default)
=> await McpClient.CreateAsync(clientTransport, clientOptions, loggerFactory, cancellationToken);
}

View file

@ -36,7 +36,7 @@ public class McpClientHandlers
/// </para>
/// <para>
/// Handlers provided via <see cref="NotificationHandlers"/> will be registered with the client for the lifetime of the client.
/// For transient handlers, you can use <see cref="IMcpEndpoint.RegisterNotificationHandler"/> to register a handler that can
/// For transient handlers, you can use <see cref="McpSession.RegisterNotificationHandler"/> to register a handler that can
/// then be unregistered by disposing of the <see cref="IAsyncDisposable"/> returned from the method.
/// </para>
/// </remarks>

View file

@ -58,12 +58,10 @@ internal sealed partial class McpClientImpl : McpClient
{
McpClientHandlers handlers = options.Handlers;
#pragma warning disable CS0618 // Type or member is obsolete
var notificationHandlersFromOptions = handlers.NotificationHandlers ?? options.Capabilities?.NotificationHandlers;
var samplingHandler = handlers.SamplingHandler ?? options.Capabilities?.Sampling?.SamplingHandler;
var rootsHandler = handlers.RootsHandler ?? options.Capabilities?.Roots?.RootsHandler;
var elicitationHandler = handlers.ElicitationHandler ?? options.Capabilities?.Elicitation?.ElicitationHandler;
#pragma warning restore CS0618 // Type or member is obsolete
var notificationHandlersFromOptions = handlers.NotificationHandlers;
var samplingHandler = handlers.SamplingHandler;
var rootsHandler = handlers.RootsHandler;
var elicitationHandler = handlers.ElicitationHandler;
if (notificationHandlersFromOptions is not null)
{

View file

@ -1,85 +0,0 @@
using System.ComponentModel;
using ModelContextProtocol.Client;
using ModelContextProtocol.Protocol;
using ModelContextProtocol.Server;
namespace ModelContextProtocol;
/// <summary>
/// Represents a client or server Model Context Protocol (MCP) endpoint.
/// </summary>
/// <remarks>
/// <para>
/// The MCP endpoint provides the core communication functionality used by both clients and servers:
/// <list type="bullet">
/// <item>Sending JSON-RPC requests and receiving responses.</item>
/// <item>Sending notifications to the connected endpoint.</item>
/// <item>Registering handlers for receiving notifications.</item>
/// </list>
/// </para>
/// <para>
/// <see cref="IMcpEndpoint"/> serves as the base interface for both <see cref="IMcpClient"/> and
/// <see cref="IMcpServer"/> interfaces, providing the common functionality needed for MCP protocol
/// communication. Most applications will use these more specific interfaces rather than working with
/// <see cref="IMcpEndpoint"/> directly.
/// </para>
/// <para>
/// All MCP endpoints should be properly disposed after use as they implement <see cref="IAsyncDisposable"/>.
/// </para>
/// </remarks>
[Obsolete($"Use {nameof(McpSession)} instead. This member will be removed in a subsequent release.")] // See: https://github.com/modelcontextprotocol/csharp-sdk/issues/774
[EditorBrowsable(EditorBrowsableState.Never)]
public interface IMcpEndpoint : IAsyncDisposable
{
/// <summary>Gets an identifier associated with the current MCP session.</summary>
/// <remarks>
/// Typically populated in transports supporting multiple sessions such as Streamable HTTP or SSE.
/// Can return <see langword="null"/> if the session hasn't initialized or if the transport doesn't
/// support multiple sessions (as is the case with STDIO).
/// </remarks>
string? SessionId { get; }
/// <summary>
/// Sends a JSON-RPC request to the connected endpoint and waits for a response.
/// </summary>
/// <param name="request">The JSON-RPC request to send.</param>
/// <param name="cancellationToken">The <see cref="CancellationToken"/> to monitor for cancellation requests. The default is <see cref="CancellationToken.None"/>.</param>
/// <returns>A task containing the endpoint's response.</returns>
/// <exception cref="InvalidOperationException">The transport is not connected, or another error occurs during request processing.</exception>
/// <exception cref="McpException">An error occured during request processing.</exception>
/// <remarks>
/// This method provides low-level access to send raw JSON-RPC requests. For most use cases,
/// consider using the strongly-typed extension methods that provide a more convenient API.
/// </remarks>
Task<JsonRpcResponse> SendRequestAsync(JsonRpcRequest request, CancellationToken cancellationToken = default);
/// <summary>
/// Sends a JSON-RPC message to the connected endpoint.
/// </summary>
/// <param name="message">
/// The JSON-RPC message to send. This can be any type that implements JsonRpcMessage, such as
/// JsonRpcRequest, JsonRpcResponse, JsonRpcNotification, or JsonRpcError.
/// </param>
/// <param name="cancellationToken">The <see cref="CancellationToken"/> to monitor for cancellation requests. The default is <see cref="CancellationToken.None"/>.</param>
/// <returns>A task that represents the asynchronous send operation.</returns>
/// <exception cref="InvalidOperationException">The transport is not connected.</exception>
/// <exception cref="ArgumentNullException"><paramref name="message"/> is <see langword="null"/>.</exception>
/// <remarks>
/// <para>
/// This method provides low-level access to send any JSON-RPC message. For specific message types,
/// consider using the higher-level methods such as <see cref="SendRequestAsync"/> or extension methods
/// like <see cref="McpEndpointExtensions.SendNotificationAsync(IMcpEndpoint, string, CancellationToken)"/>,
/// which provide a simpler API.
/// </para>
/// <para>
/// The method will serialize the message and transmit it using the underlying transport mechanism.
/// </para>
/// </remarks>
Task SendMessageAsync(JsonRpcMessage message, CancellationToken cancellationToken = default);
/// <summary>Registers a handler to be invoked when a notification for the specified method is received.</summary>
/// <param name="method">The notification method.</param>
/// <param name="handler">The handler to be invoked.</param>
/// <returns>An <see cref="IDisposable"/> that will remove the registered handler when disposed.</returns>
IAsyncDisposable RegisterNotificationHandler(string method, Func<JsonRpcNotification, CancellationToken, ValueTask> handler);
}

View file

@ -1,152 +0,0 @@
using ModelContextProtocol.Client;
using ModelContextProtocol.Protocol;
using ModelContextProtocol.Server;
using System.ComponentModel;
using System.Diagnostics.CodeAnalysis;
using System.Runtime.CompilerServices;
using System.Text.Json;
namespace ModelContextProtocol;
/// <summary>
/// Provides extension methods for interacting with an <see cref="IMcpEndpoint"/>.
/// </summary>
/// <remarks>
/// <para>
/// This class provides strongly typed methods for working with the Model Context Protocol (MCP) endpoints,
/// simplifying JSON-RPC communication by handling serialization and deserialization of parameters and results.
/// </para>
/// <para>
/// These extension methods are designed to be used with both client (<see cref="IMcpClient"/>) and
/// server (<see cref="IMcpServer"/>) implementations of the <see cref="IMcpEndpoint"/> interface.
/// </para>
/// </remarks>
public static class McpEndpointExtensions
{
/// <summary>
/// Sends a JSON-RPC request and attempts to deserialize the result to <typeparamref name="TResult"/>.
/// </summary>
/// <typeparam name="TParameters">The type of the request parameters to serialize from.</typeparam>
/// <typeparam name="TResult">The type of the result to deserialize to.</typeparam>
/// <param name="endpoint">The MCP client or server instance.</param>
/// <param name="method">The JSON-RPC method name to invoke.</param>
/// <param name="parameters">The request parameters.</param>
/// <param name="requestId">The request ID for the request.</param>
/// <param name="serializerOptions">The options governing request serialization.</param>
/// <param name="cancellationToken">The <see cref="CancellationToken"/> to monitor for cancellation requests. The default is <see cref="CancellationToken.None"/>.</param>
/// <returns>A task that represents the asynchronous operation. The task result contains the deserialized result.</returns>
[Obsolete($"Use {nameof(McpSession)}.{nameof(McpSession.SendRequestAsync)} instead. This member will be removed in a subsequent release.")] // See: https://github.com/modelcontextprotocol/csharp-sdk/issues/774
[EditorBrowsable(EditorBrowsableState.Never)]
public static ValueTask<TResult> SendRequestAsync<TParameters, TResult>(
this IMcpEndpoint endpoint,
string method,
TParameters parameters,
JsonSerializerOptions? serializerOptions = null,
RequestId requestId = default,
CancellationToken cancellationToken = default)
where TResult : notnull
=> AsSessionOrThrow(endpoint).SendRequestAsync<TParameters, TResult>(method, parameters, serializerOptions, requestId, cancellationToken);
/// <summary>
/// Sends a parameterless notification to the connected endpoint.
/// </summary>
/// <param name="client">The MCP client or server instance.</param>
/// <param name="method">The notification method name.</param>
/// <param name="cancellationToken">The <see cref="CancellationToken"/> to monitor for cancellation requests. The default is <see cref="CancellationToken.None"/>.</param>
/// <returns>A task that represents the asynchronous send operation.</returns>
/// <remarks>
/// <para>
/// This method sends a notification without any parameters. Notifications are one-way messages
/// that don't expect a response. They are commonly used for events, status updates, or to signal
/// changes in state.
/// </para>
/// </remarks>
[Obsolete($"Use {nameof(McpSession)}.{nameof(McpSession.SendNotificationAsync)} instead. This member will be removed in a subsequent release.")] // See: https://github.com/modelcontextprotocol/csharp-sdk/issues/774
[EditorBrowsable(EditorBrowsableState.Never)]
public static Task SendNotificationAsync(this IMcpEndpoint client, string method, CancellationToken cancellationToken = default)
=> AsSessionOrThrow(client).SendNotificationAsync(method, cancellationToken);
/// <summary>
/// Sends a notification with parameters to the connected endpoint.
/// </summary>
/// <typeparam name="TParameters">The type of the notification parameters to serialize.</typeparam>
/// <param name="endpoint">The MCP client or server instance.</param>
/// <param name="method">The JSON-RPC method name for the notification.</param>
/// <param name="parameters">Object representing the notification parameters.</param>
/// <param name="serializerOptions">The options governing parameter serialization. If null, default options are used.</param>
/// <param name="cancellationToken">The <see cref="CancellationToken"/> to monitor for cancellation requests. The default is <see cref="CancellationToken.None"/>.</param>
/// <returns>A task that represents the asynchronous send operation.</returns>
/// <remarks>
/// <para>
/// This method sends a notification with parameters to the connected endpoint. Notifications are one-way
/// messages that don't expect a response, commonly used for events, status updates, or signaling changes.
/// </para>
/// <para>
/// The parameters object is serialized to JSON according to the provided serializer options or the default
/// options if none are specified.
/// </para>
/// <para>
/// The Model Context Protocol defines several standard notification methods in <see cref="NotificationMethods"/>,
/// but custom methods can also be used for application-specific notifications.
/// </para>
/// </remarks>
[Obsolete($"Use {nameof(McpSession)}.{nameof(McpSession.SendNotificationAsync)} instead. This member will be removed in a subsequent release.")] // See: https://github.com/modelcontextprotocol/csharp-sdk/issues/774
[EditorBrowsable(EditorBrowsableState.Never)]
public static Task SendNotificationAsync<TParameters>(
this IMcpEndpoint endpoint,
string method,
TParameters parameters,
JsonSerializerOptions? serializerOptions = null,
CancellationToken cancellationToken = default)
=> AsSessionOrThrow(endpoint).SendNotificationAsync(method, parameters, serializerOptions, cancellationToken);
/// <summary>
/// Notifies the connected endpoint of progress for a long-running operation.
/// </summary>
/// <param name="endpoint">The endpoint issuing the notification.</param>
/// <param name="progressToken">The <see cref="ProgressToken"/> identifying the operation for which progress is being reported.</param>
/// <param name="progress">The progress update to send, containing information such as percentage complete or status message.</param>
/// <param name="cancellationToken">The <see cref="CancellationToken"/> to monitor for cancellation requests. The default is <see cref="CancellationToken.None"/>.</param>
/// <returns>A task representing the completion of the notification operation (not the operation being tracked).</returns>
/// <exception cref="ArgumentNullException"><paramref name="endpoint"/> is <see langword="null"/>.</exception>
/// <remarks>
/// <para>
/// This method sends a progress notification to the connected endpoint using the Model Context Protocol's
/// standardized progress notification format. Progress updates are identified by a <see cref="ProgressToken"/>
/// that allows the recipient to correlate multiple updates with a specific long-running operation.
/// </para>
/// <para>
/// Progress notifications are sent asynchronously and don't block the operation from continuing.
/// </para>
/// </remarks>
[Obsolete($"Use {nameof(McpSession)}.{nameof(McpSession.NotifyProgressAsync)} instead. This member will be removed in a subsequent release.")] // See: https://github.com/modelcontextprotocol/csharp-sdk/issues/774
[EditorBrowsable(EditorBrowsableState.Never)]
public static Task NotifyProgressAsync(
this IMcpEndpoint endpoint,
ProgressToken progressToken,
ProgressNotificationValue progress,
CancellationToken cancellationToken = default)
=> AsSessionOrThrow(endpoint).NotifyProgressAsync(progressToken, progress, cancellationToken);
[MethodImpl(MethodImplOptions.AggressiveInlining)]
#pragma warning disable CS0618 // Type or member is obsolete
private static McpSession AsSessionOrThrow(IMcpEndpoint endpoint, [CallerMemberName] string memberName = "")
#pragma warning restore CS0618 // Type or member is obsolete
{
if (endpoint is not McpSession session)
{
ThrowInvalidEndpointType(memberName);
}
return session;
[DoesNotReturn]
[MethodImpl(MethodImplOptions.NoInlining)]
static void ThrowInvalidEndpointType(string memberName)
=> throw new InvalidOperationException(
$"Only arguments assignable to '{nameof(McpSession)}' are supported. " +
$"Prefer using '{nameof(McpServer)}.{memberName}' instead, as " +
$"'{nameof(McpEndpointExtensions)}.{memberName}' is obsolete and will be " +
$"removed in the future.");
}
}

View file

@ -5,9 +5,7 @@ using System.Text.Json.Serialization.Metadata;
namespace ModelContextProtocol;
#pragma warning disable CS0618 // Type or member is obsolete
public abstract partial class McpSession : IMcpEndpoint, IAsyncDisposable
#pragma warning restore CS0618 // Type or member is obsolete
public abstract partial class McpSession : IAsyncDisposable
{
/// <summary>
/// Sends a JSON-RPC request and attempts to deserialize the result to <typeparamref name="TResult"/>.

View file

@ -26,9 +26,7 @@ namespace ModelContextProtocol;
/// All MCP sessions should be properly disposed after use as they implement <see cref="IAsyncDisposable"/>.
/// </para>
/// </remarks>
#pragma warning disable CS0618 // Type or member is obsolete
public abstract partial class McpSession : IMcpEndpoint, IAsyncDisposable
#pragma warning restore CS0618 // Type or member is obsolete
public abstract partial class McpSession : IAsyncDisposable
{
/// <summary>Gets an identifier associated with the current MCP session.</summary>
/// <remarks>

View file

@ -14,8 +14,15 @@
<IsAotCompatible>true</IsAotCompatible>
</PropertyGroup>
<PropertyGroup Condition="'$(TargetFramework)' == 'netstandard2.0'">
<!-- CS0436: Allow ObsoleteAttribute to be redefined internally -->
<NoWarn>$(NoWarn);CS0436</NoWarn>
</PropertyGroup>
<ItemGroup>
<Compile Include="..\Common\Throw.cs" Link="Throw.cs" />
<Compile Include="..\Common\Obsoletions.cs" Link="Obsoletions.cs" />
<Compile Include="..\Common\Experimentals.cs" Link="Experimentals.cs" />
</ItemGroup>
<!-- Dependencies only needed by netstandard2.0 -->

View file

@ -66,26 +66,4 @@ public sealed class ClientCapabilities
/// </summary>
[JsonPropertyName("elicitation")]
public ElicitationCapability? Elicitation { get; set; }
/// <summary>Gets or sets notification handlers to register with the client.</summary>
/// <remarks>
/// <para>
/// When constructed, the client will enumerate these handlers, which may contain multiple handlers per notification method key, once.
/// The client will not re-enumerate the sequence after initialization.
/// </para>
/// <para>
/// Notification handlers allow the client to respond to server-sent notifications for specific methods.
/// Each key in the collection is a notification method name, and each value is a callback that will be invoked
/// when a notification with that method is received.
/// </para>
/// <para>
/// Handlers provided via <see cref="NotificationHandlers"/> will be registered with the client for the lifetime of the client.
/// For transient handlers, <see cref="McpSession.RegisterNotificationHandler"/> can be used to register a handler that can
/// then be unregistered by disposing of the <see cref="IAsyncDisposable"/> returned from the method.
/// </para>
/// </remarks>
[JsonIgnore]
[Obsolete($"Use {nameof(McpClientOptions.Handlers.NotificationHandlers)} instead. This member will be removed in a subsequent release.")] // See: https://github.com/modelcontextprotocol/csharp-sdk/issues/774
[EditorBrowsable(EditorBrowsableState.Never)]
public IEnumerable<KeyValuePair<string, Func<JsonRpcNotification, CancellationToken, ValueTask>>>? NotificationHandlers { get; set; }
}

View file

@ -28,16 +28,4 @@ namespace ModelContextProtocol.Protocol;
/// </remarks>
public sealed class CompletionsCapability
{
/// <summary>
/// Gets or sets the handler for completion requests.
/// </summary>
/// <remarks>
/// This handler provides auto-completion suggestions for prompt arguments or resource references in the Model Context Protocol.
/// The handler receives a reference type (e.g., "ref/prompt" or "ref/resource") and the current argument value,
/// and should return appropriate completion suggestions.
/// </remarks>
[JsonIgnore]
[Obsolete($"Use {nameof(McpServerOptions.Handlers.CompleteHandler)} instead. This member will be removed in a subsequent release.")] // See: https://github.com/modelcontextprotocol/csharp-sdk/issues/774
[EditorBrowsable(EditorBrowsableState.Never)]
public McpRequestHandler<CompleteRequestParams, CompleteResult>? CompleteHandler { get; set; }
}

View file

@ -247,10 +247,12 @@ public sealed class ElicitRequestParams
{
if (enumNames is not null)
{
// EnumSchema for backward compatibility
#pragma warning disable CS0618 // Type or member is obsolete
// EnumSchema is deprecated but supported for backward compatibility.
// Use the EnumSchema class, which is an alias for LegacyTitledEnumSchema,
// to ensure backward compatibility with existing code relying on that type.
#pragma warning disable MCP9001
psd = new EnumSchema
#pragma warning restore CS0618 // Type or member is obsolete
#pragma warning restore MCP9001
{
Enum = enumValues,
EnumNames = enumNames,
@ -558,22 +560,22 @@ public sealed class ElicitRequestParams
}
break;
#pragma warning disable CS0618 // Type or member is obsolete
case LegacyTitledEnumSchema legacyEnum:
#pragma warning restore CS0618 // Type or member is obsolete
if (legacyEnum.Enum is not null)
#pragma warning disable MCP9001 // LegacyTitledEnumSchema is deprecated but supported for backward compatibility
case LegacyTitledEnumSchema legacyTitledEnum:
#pragma warning restore MCP9001
if (legacyTitledEnum.Enum is not null)
{
writer.WritePropertyName("enum");
JsonSerializer.Serialize(writer, legacyEnum.Enum, McpJsonUtilities.JsonContext.Default.IListString);
JsonSerializer.Serialize(writer, legacyTitledEnum.Enum, McpJsonUtilities.JsonContext.Default.IListString);
}
if (legacyEnum.EnumNames is not null)
if (legacyTitledEnum.EnumNames is not null)
{
writer.WritePropertyName("enumNames");
JsonSerializer.Serialize(writer, legacyEnum.EnumNames, McpJsonUtilities.JsonContext.Default.IListString);
JsonSerializer.Serialize(writer, legacyTitledEnum.EnumNames, McpJsonUtilities.JsonContext.Default.IListString);
}
if (legacyEnum.Default is not null)
if (legacyTitledEnum.Default is not null)
{
writer.WriteString("default", legacyEnum.Default);
writer.WriteString("default", legacyTitledEnum.Default);
}
break;
@ -740,51 +742,6 @@ public sealed class ElicitRequestParams
public bool? Default { get; set; }
}
/// <summary>
/// Represents a legacy schema for an enum type with enumNames.
/// </summary>
/// <remarks>
/// This schema is deprecated in favor of <see cref="TitledSingleSelectEnumSchema"/>.
/// </remarks>
[Obsolete("Use TitledSingleSelectEnumSchema instead. This type will be removed in a future version.")]
public class LegacyTitledEnumSchema : PrimitiveSchemaDefinition
{
/// <inheritdoc/>
[JsonPropertyName("type")]
public override string Type
{
get => "string";
set
{
if (value is not "string")
{
throw new ArgumentException("Type must be 'string'.", nameof(value));
}
}
}
/// <summary>Gets or sets the list of allowed string values for the enum.</summary>
[JsonPropertyName("enum")]
[field: MaybeNull]
public IList<string> Enum
{
get => field ??= [];
set
{
Throw.IfNull(value);
field = value;
}
}
/// <summary>Gets or sets optional display names corresponding to the enum values.</summary>
[JsonPropertyName("enumNames")]
public IList<string>? EnumNames { get; set; }
/// <summary>Gets or sets the default value for the enum.</summary>
[JsonPropertyName("default")]
public string? Default { get; set; }
}
/// <summary>
/// Represents a schema for single-selection enumeration without display titles for options.
/// </summary>
@ -970,10 +927,59 @@ public sealed class ElicitRequestParams
}
/// <summary>
/// Represents a schema for an enum type. This is a compatibility alias for <see cref="LegacyTitledEnumSchema"/>.
/// Represents a legacy schema for an enum type with enumNames.
/// This is a compatibility alias for <see cref="LegacyTitledEnumSchema"/>.
/// </summary>
[Obsolete("Use UntitledSingleSelectEnumSchema or TitledSingleSelectEnumSchema instead. This type will be removed in a future version.")]
/// <remarks>
/// This schema is deprecated in favor of <see cref="TitledSingleSelectEnumSchema"/>.
/// </remarks>
[Obsolete(Obsoletions.LegacyTitledEnumSchema_Message, DiagnosticId = Obsoletions.LegacyTitledEnumSchema_DiagnosticId, UrlFormat = Obsoletions.LegacyTitledEnumSchema_Url)]
public sealed class EnumSchema : LegacyTitledEnumSchema
{
}
/// <summary>
/// Represents a legacy schema for an enum type with enumNames.
/// </summary>
/// <remarks>
/// This schema is deprecated in favor of <see cref="TitledSingleSelectEnumSchema"/>.
/// </remarks>
[Obsolete(Obsoletions.LegacyTitledEnumSchema_Message, DiagnosticId = Obsoletions.LegacyTitledEnumSchema_DiagnosticId, UrlFormat = Obsoletions.LegacyTitledEnumSchema_Url)]
public class LegacyTitledEnumSchema : PrimitiveSchemaDefinition
{
/// <inheritdoc/>
[JsonPropertyName("type")]
public override string Type
{
get => "string";
set
{
if (value is not "string")
{
throw new ArgumentException("Type must be 'string'.", nameof(value));
}
}
}
/// <summary>Gets or sets the list of allowed string values for the enum.</summary>
[JsonPropertyName("enum")]
[field: MaybeNull]
public IList<string> Enum
{
get => field ??= [];
set
{
Throw.IfNull(value);
field = value;
}
}
/// <summary>Gets or sets optional display names corresponding to the enum values.</summary>
[JsonPropertyName("enumNames")]
public IList<string>? EnumNames { get; set; }
/// <summary>Gets or sets the default value for the enum.</summary>
[JsonPropertyName("default")]
public string? Default { get; set; }
}
}

Some files were not shown because too many files have changed in this diff Show more