diff --git a/src/Common/Experimentals.cs b/src/Common/Experimentals.cs
new file mode 100644
index 000000000..c81ef981e
--- /dev/null
+++ b/src/Common/Experimentals.cs
@@ -0,0 +1,27 @@
+using System.Diagnostics.CodeAnalysis;
+
+namespace ModelContextProtocol;
+
+///
+/// Defines diagnostic IDs, Messages, and Urls for APIs annotated with .
+///
+///
+/// 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.
+///
+/// Experimental diagnostic IDs are in the format MCP5###.
+///
+///
+/// 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.
+///
+///
+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";
+}
diff --git a/src/Common/Obsoletions.cs b/src/Common/Obsoletions.cs
new file mode 100644
index 000000000..ae7581997
--- /dev/null
+++ b/src/Common/Obsoletions.cs
@@ -0,0 +1,25 @@
+namespace ModelContextProtocol;
+
+///
+/// Defines diagnostic IDs, Messages, and Urls for APIs annotated with .
+///
+///
+/// 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.
+///
+/// Obsolete diagnostic IDs are in the format MCP9###.
+///
+///
+/// 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.
+///
+///
+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";
+}
diff --git a/src/Common/Polyfills/System/Diagnostics/CodeAnalysis/ExperimentalAttribute.cs b/src/Common/Polyfills/System/Diagnostics/CodeAnalysis/ExperimentalAttribute.cs
new file mode 100644
index 000000000..3e5cea280
--- /dev/null
+++ b/src/Common/Polyfills/System/Diagnostics/CodeAnalysis/ExperimentalAttribute.cs
@@ -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;
+
+///
+/// Indicates that an API is experimental and it may change in the future.
+///
+///
+/// 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.
+///
+[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
+{
+ ///
+ /// Initializes a new instance of the class, specifying the ID that the compiler will use
+ /// when reporting a use of the API the attribute applies to.
+ ///
+ /// The ID that the compiler will use when reporting a use of the API the attribute applies to.
+ public ExperimentalAttribute(string diagnosticId)
+ {
+ DiagnosticId = diagnosticId;
+ }
+
+ ///
+ /// Gets the ID that the compiler will use when reporting a use of the API the attribute applies to.
+ ///
+ /// The unique diagnostic ID.
+ ///
+ /// The diagnostic ID is shown in build output for warnings and errors.
+ /// This property represents the unique ID that can be used to suppress the warnings or errors, if needed.
+ ///
+ public string DiagnosticId { get; }
+
+ ///
+ /// Gets or sets an optional message associated with the experimental attribute.
+ ///
+ /// The message that provides additional information about the experimental feature.
+ ///
+ /// This message can be used to provide more context or guidance about the experimental feature.
+ ///
+ public string? Message { get; set; }
+
+ ///
+ /// 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.
+ ///
+ /// The format string that represents a URL to corresponding documentation.
+ /// An example format string is https://contoso.com/obsoletion-warnings/{0}.
+ public string? UrlFormat { get; set; }
+}
+#endif
diff --git a/src/Common/Polyfills/System/ObsoleteAttribute.cs b/src/Common/Polyfills/System/ObsoleteAttribute.cs
new file mode 100644
index 000000000..02f9b5297
--- /dev/null
+++ b/src/Common/Polyfills/System/ObsoleteAttribute.cs
@@ -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;
+
+///
+/// Marks program elements that are no longer in use.
+///
+[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
diff --git a/src/ModelContextProtocol.Analyzers/Diagnostics.cs b/src/ModelContextProtocol.Analyzers/Diagnostics.cs
index 99f868f88..d8d8ead18 100644
--- a/src/ModelContextProtocol.Analyzers/Diagnostics.cs
+++ b/src/ModelContextProtocol.Analyzers/Diagnostics.cs
@@ -9,6 +9,14 @@
namespace ModelContextProtocol.Analyzers;
/// Provides the diagnostic descriptors used by the assembly.
+///
+/// Analyzer diagnostic IDs are in the format MCP### (or MCP1### if ever needed).
+///
+/// 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.
+///
+///
internal static class Diagnostics
{
public static DiagnosticDescriptor InvalidXmlDocumentation { get; } = new(
diff --git a/src/ModelContextProtocol.Core/Client/IMcpClient.cs b/src/ModelContextProtocol.Core/Client/IMcpClient.cs
deleted file mode 100644
index 141add86a..000000000
--- a/src/ModelContextProtocol.Core/Client/IMcpClient.cs
+++ /dev/null
@@ -1,50 +0,0 @@
-using ModelContextProtocol.Protocol;
-using System.ComponentModel;
-
-namespace ModelContextProtocol.Client;
-
-///
-/// Represents an instance of a Model Context Protocol (MCP) client that connects to and communicates with an MCP server.
-///
-[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
-{
- ///
- /// Gets the capabilities supported by the connected server.
- ///
- /// The client is not connected.
- ServerCapabilities ServerCapabilities { get; }
-
- ///
- /// Gets the implementation information of the connected server.
- ///
- ///
- ///
- /// 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.
- ///
- ///
- /// This information can be useful for logging, debugging, compatibility checks, and displaying server
- /// information to users.
- ///
- ///
- /// The client is not connected.
- Implementation ServerInfo { get; }
-
- ///
- /// Gets any instructions describing how to use the connected server and its features.
- ///
- ///
- ///
- /// 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.
- ///
- ///
- /// 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.
- ///
- ///
- string? ServerInstructions { get; }
-}
diff --git a/src/ModelContextProtocol.Core/Client/McpClient.Methods.cs b/src/ModelContextProtocol.Core/Client/McpClient.Methods.cs
index f6545d920..b96f5ef11 100644
--- a/src/ModelContextProtocol.Core/Client/McpClient.Methods.cs
+++ b/src/ModelContextProtocol.Core/Client/McpClient.Methods.cs
@@ -10,9 +10,7 @@ namespace ModelContextProtocol.Client;
///
/// Represents an instance of a Model Context Protocol (MCP) client session that connects to and communicates with an MCP server.
///
-#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
{
/// Creates an , connecting it to the specified server.
/// The transport instance used to communicate with the server.
diff --git a/src/ModelContextProtocol.Core/Client/McpClient.cs b/src/ModelContextProtocol.Core/Client/McpClient.cs
index f63f4b450..abd556fb6 100644
--- a/src/ModelContextProtocol.Core/Client/McpClient.cs
+++ b/src/ModelContextProtocol.Core/Client/McpClient.cs
@@ -5,9 +5,7 @@ namespace ModelContextProtocol.Client;
///
/// Represents an instance of a Model Context Protocol (MCP) client session that connects to and communicates with an MCP server.
///
-#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
{
///
/// Gets the capabilities supported by the connected server.
diff --git a/src/ModelContextProtocol.Core/Client/McpClientExtensions.cs b/src/ModelContextProtocol.Core/Client/McpClientExtensions.cs
deleted file mode 100644
index 4f46b7492..000000000
--- a/src/ModelContextProtocol.Core/Client/McpClientExtensions.cs
+++ /dev/null
@@ -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;
-
-///
-/// Provides extension methods for interacting with an .
-///
-///
-/// 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.
-///
-public static class McpClientExtensions
-{
- ///
- /// Sends a ping request to verify server connectivity.
- ///
- /// The client instance used to communicate with the MCP server.
- /// The to monitor for cancellation requests. The default is .
- /// A task that completes when the ping is successful.
- ///
- ///
- /// 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.
- ///
- ///
- /// 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.
- ///
- ///
- /// is .
- /// The server cannot be reached or returned an error response.
- [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);
-
- ///
- /// Retrieves a list of available tools from the server.
- ///
- /// The client instance used to communicate with the MCP server.
- /// The serializer options governing tool parameter serialization. If null, the default options are used.
- /// The to monitor for cancellation requests. The default is .
- /// A list of all available tools as instances.
- ///
- ///
- /// 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.
- ///
- ///
- /// For servers with a large number of tools and that responds with paginated responses, consider using
- /// instead, because it streams tools as they arrive rather than loading them all at once.
- ///
- ///
- /// The serializer options provided are flowed to each and will be used
- /// when invoking tools in order to serialize any parameters.
- ///
- ///
- ///
- ///
- /// // 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);
- /// }
- ///
- ///
- /// is .
- [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> ListToolsAsync(
- this IMcpClient client,
- JsonSerializerOptions? serializerOptions = null,
- CancellationToken cancellationToken = default)
- => AsClientOrThrow(client).ListToolsAsync(serializerOptions, cancellationToken);
-
- ///
- /// Creates an enumerable for asynchronously enumerating all available tools from the server.
- ///
- /// The client instance used to communicate with the MCP server.
- /// The serializer options governing tool parameter serialization. If null, the default options are used.
- /// The to monitor for cancellation requests. The default is .
- /// An asynchronous sequence of all available tools as instances.
- ///
- ///
- /// 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.
- ///
- ///
- /// The serializer options provided are flowed to each and will be used
- /// when invoking tools to serialize any parameters.
- ///
- ///
- /// Every iteration through the returned
- /// will result in re-querying the server and yielding the sequence of available tools.
- ///
- ///
- ///
- ///
- /// // Enumerate all tools available on the server
- /// await foreach (var tool in client.EnumerateToolsAsync())
- /// {
- /// Console.WriteLine($"Tool: {tool.Name}");
- /// }
- ///
- ///
- /// is .
- [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 EnumerateToolsAsync(
- this IMcpClient client,
- JsonSerializerOptions? serializerOptions = null,
- CancellationToken cancellationToken = default)
- => AsClientOrThrow(client).EnumerateToolsAsync(serializerOptions, cancellationToken);
-
- ///
- /// Retrieves a list of available prompts from the server.
- ///
- /// The client instance used to communicate with the MCP server.
- /// The to monitor for cancellation requests. The default is .
- /// A list of all available prompts as instances.
- ///
- ///
- /// 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.
- ///
- ///
- /// For servers with a large number of prompts and that responds with paginated responses, consider using
- /// instead, as it streams prompts as they arrive rather than loading them all at once.
- ///
- ///
- /// is .
- [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> ListPromptsAsync(
- this IMcpClient client, CancellationToken cancellationToken = default)
- => AsClientOrThrow(client).ListPromptsAsync(cancellationToken);
-
- ///
- /// Creates an enumerable for asynchronously enumerating all available prompts from the server.
- ///
- /// The client instance used to communicate with the MCP server.
- /// The to monitor for cancellation requests. The default is .
- /// An asynchronous sequence of all available prompts as instances.
- ///
- ///
- /// 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.
- ///
- ///
- /// Every iteration through the returned
- /// will result in re-querying the server and yielding the sequence of available prompts.
- ///
- ///
- ///
- ///
- /// // Enumerate all prompts available on the server
- /// await foreach (var prompt in client.EnumeratePromptsAsync())
- /// {
- /// Console.WriteLine($"Prompt: {prompt.Name}");
- /// }
- ///
- ///
- /// is .
- [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 EnumeratePromptsAsync(
- this IMcpClient client, CancellationToken cancellationToken = default)
- => AsClientOrThrow(client).EnumeratePromptsAsync(cancellationToken);
-
- ///
- /// Retrieves a specific prompt from the MCP server.
- ///
- /// The client instance used to communicate with the MCP server.
- /// The name of the prompt to retrieve.
- /// Optional arguments for the prompt. Keys are parameter names, and values are the argument values.
- /// The serialization options governing argument serialization.
- /// The to monitor for cancellation requests. The default is .
- /// A task containing the prompt's result with content and messages.
- ///
- ///
- /// 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.
- ///
- ///
- /// 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.
- ///
- ///
- /// The returned contains a collection of objects,
- /// which can be converted to objects using the method.
- ///
- ///
- /// Thrown when the prompt does not exist, when required arguments are missing, or when the server encounters an error processing the prompt.
- /// is .
- [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 GetPromptAsync(
- this IMcpClient client,
- string name,
- IReadOnlyDictionary? arguments = null,
- JsonSerializerOptions? serializerOptions = null,
- CancellationToken cancellationToken = default)
- => AsClientOrThrow(client).GetPromptAsync(name, arguments, serializerOptions, cancellationToken);
-
- ///
- /// Retrieves a list of available resource templates from the server.
- ///
- /// The client instance used to communicate with the MCP server.
- /// The to monitor for cancellation requests. The default is .
- /// A list of all available resource templates as instances.
- ///
- ///
- /// 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.
- ///
- ///
- /// For servers with a large number of resource templates and that responds with paginated responses, consider using
- /// instead, as it streams templates as they arrive rather than loading them all at once.
- ///
- ///
- /// is .
- [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> ListResourceTemplatesAsync(
- this IMcpClient client, CancellationToken cancellationToken = default)
- => AsClientOrThrow(client).ListResourceTemplatesAsync(cancellationToken);
-
- ///
- /// Creates an enumerable for asynchronously enumerating all available resource templates from the server.
- ///
- /// The client instance used to communicate with the MCP server.
- /// The to monitor for cancellation requests. The default is .
- /// An asynchronous sequence of all available resource templates as instances.
- ///
- ///
- /// 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.
- ///
- ///
- /// Every iteration through the returned
- /// will result in re-querying the server and yielding the sequence of available resource templates.
- ///
- ///
- ///
- ///
- /// // Enumerate all resource templates available on the server
- /// await foreach (var template in client.EnumerateResourceTemplatesAsync())
- /// {
- /// Console.WriteLine($"Template: {template.Name}");
- /// }
- ///
- ///
- /// is .
- [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 EnumerateResourceTemplatesAsync(
- this IMcpClient client, CancellationToken cancellationToken = default)
- => AsClientOrThrow(client).EnumerateResourceTemplatesAsync(cancellationToken);
-
- ///
- /// Retrieves a list of available resources from the server.
- ///
- /// The client instance used to communicate with the MCP server.
- /// The to monitor for cancellation requests. The default is .
- /// A list of all available resources as instances.
- ///
- ///
- /// 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.
- ///
- ///
- /// For servers with a large number of resources and that responds with paginated responses, consider using
- /// instead, as it streams resources as they arrive rather than loading them all at once.
- ///
- ///
- ///
- ///
- /// // 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}");
- /// }
- ///
- ///
- /// is .
- [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> ListResourcesAsync(
- this IMcpClient client, CancellationToken cancellationToken = default)
- => AsClientOrThrow(client).ListResourcesAsync(cancellationToken);
-
- ///
- /// Creates an enumerable for asynchronously enumerating all available resources from the server.
- ///
- /// The client instance used to communicate with the MCP server.
- /// The to monitor for cancellation requests. The default is .
- /// An asynchronous sequence of all available resources as instances.
- ///
- ///
- /// 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.
- ///
- ///
- /// Every iteration through the returned
- /// will result in re-querying the server and yielding the sequence of available resources.
- ///
- ///
- ///
- ///
- /// // Enumerate all resources available on the server
- /// await foreach (var resource in client.EnumerateResourcesAsync())
- /// {
- /// Console.WriteLine($"Resource URI: {resource.Uri}");
- /// }
- ///
- ///
- /// is .
- [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 EnumerateResourcesAsync(
- this IMcpClient client, CancellationToken cancellationToken = default)
- => AsClientOrThrow(client).EnumerateResourcesAsync(cancellationToken);
-
- ///
- /// Reads a resource from the server.
- ///
- /// The client instance used to communicate with the MCP server.
- /// The uri of the resource.
- /// The to monitor for cancellation requests. The default is .
- /// is .
- /// is .
- /// is empty or composed entirely of whitespace.
- [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 ReadResourceAsync(
- this IMcpClient client, string uri, CancellationToken cancellationToken = default)
- => AsClientOrThrow(client).ReadResourceAsync(uri, cancellationToken);
-
- ///
- /// Reads a resource from the server.
- ///
- /// The client instance used to communicate with the MCP server.
- /// The uri of the resource.
- /// The to monitor for cancellation requests. The default is .
- /// is .
- /// is .
- [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 ReadResourceAsync(
- this IMcpClient client, Uri uri, CancellationToken cancellationToken = default)
- => AsClientOrThrow(client).ReadResourceAsync(uri, cancellationToken);
-
- ///
- /// Reads a resource from the server.
- ///
- /// The client instance used to communicate with the MCP server.
- /// The uri template of the resource.
- /// Arguments to use to format .
- /// The to monitor for cancellation requests. The default is .
- /// is .
- /// is .
- /// is empty or composed entirely of whitespace.
- [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 ReadResourceAsync(
- this IMcpClient client, string uriTemplate, IReadOnlyDictionary arguments, CancellationToken cancellationToken = default)
- => AsClientOrThrow(client).ReadResourceAsync(uriTemplate, arguments, cancellationToken);
-
- ///
- /// Requests completion suggestions for a prompt argument or resource reference.
- ///
- /// The client instance used to communicate with the MCP server.
- /// The reference object specifying the type and optional URI or name.
- /// The name of the argument for which completions are requested.
- /// The current value of the argument, used to filter relevant completions.
- /// The to monitor for cancellation requests. The default is .
- /// A containing completion suggestions.
- ///
- ///
- /// This method allows clients to request auto-completion suggestions for arguments in a prompt template
- /// or for resource references.
- ///
- ///
- /// 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.
- ///
- ///
- /// When working with resource references, the server will return suggestions relevant to the specified
- /// resource URI.
- ///
- ///
- /// is .
- /// is .
- /// is .
- /// is empty or composed entirely of whitespace.
- /// The server returned an error response.
- [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 CompleteAsync(this IMcpClient client, Reference reference, string argumentName, string argumentValue, CancellationToken cancellationToken = default)
- => AsClientOrThrow(client).CompleteAsync(reference, argumentName, argumentValue, cancellationToken);
-
- ///
- /// Subscribes to a resource on the server to receive notifications when it changes.
- ///
- /// The client instance used to communicate with the MCP server.
- /// The URI of the resource to which to subscribe.
- /// The to monitor for cancellation requests. The default is .
- /// A task that represents the asynchronous operation.
- ///
- ///
- /// 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.
- ///
- ///
- /// The subscription remains active until explicitly unsubscribed using
- /// or until the client disconnects from the server.
- ///
- ///
- /// To handle resource change notifications, register an event handler for the appropriate notification events,
- /// such as with .
- ///
- ///
- /// is .
- /// is .
- /// is empty or composed entirely of whitespace.
- [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);
-
- ///
- /// Subscribes to a resource on the server to receive notifications when it changes.
- ///
- /// The client instance used to communicate with the MCP server.
- /// The URI of the resource to which to subscribe.
- /// The to monitor for cancellation requests. The default is .
- /// A task that represents the asynchronous operation.
- ///
- ///
- /// 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.
- ///
- ///
- /// The subscription remains active until explicitly unsubscribed using
- /// or until the client disconnects from the server.
- ///
- ///
- /// To handle resource change notifications, register an event handler for the appropriate notification events,
- /// such as with .
- ///
- ///
- /// is .
- /// is .
- [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);
-
- ///
- /// Unsubscribes from a resource on the server to stop receiving notifications about its changes.
- ///
- /// The client instance used to communicate with the MCP server.
- /// The URI of the resource to unsubscribe from.
- /// The to monitor for cancellation requests. The default is .
- /// A task that represents the asynchronous operation.
- ///
- ///
- /// This method cancels a previous subscription to a resource, stopping the client from receiving
- /// notifications when that resource changes.
- ///
- ///
- /// 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.
- ///
- ///
- /// 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.
- ///
- ///
- /// is .
- /// is .
- /// is empty or composed entirely of whitespace.
- [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);
-
- ///
- /// Unsubscribes from a resource on the server to stop receiving notifications about its changes.
- ///
- /// The client instance used to communicate with the MCP server.
- /// The URI of the resource to unsubscribe from.
- /// The to monitor for cancellation requests. The default is .
- /// A task that represents the asynchronous operation.
- ///
- ///
- /// This method cancels a previous subscription to a resource, stopping the client from receiving
- /// notifications when that resource changes.
- ///
- ///
- /// 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.
- ///
- ///
- /// 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.
- ///
- ///
- /// is .
- /// is .
- [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);
-
- ///
- /// Invokes a tool on the server.
- ///
- /// The client instance used to communicate with the MCP server.
- /// The name of the tool to call on the server..
- /// An optional dictionary of arguments to pass to the tool. Each key represents a parameter name,
- /// and its associated value represents the argument value.
- ///
- ///
- /// An optional to have progress notifications reported to it. Setting this to a non-
- /// value will result in a progress token being included in the call, and any resulting progress notifications during the operation
- /// routed to this instance.
- ///
- ///
- /// The JSON serialization options governing argument serialization. If , the default serialization options are used.
- ///
- /// The to monitor for cancellation requests. The default is .
- ///
- /// A task containing the from the tool execution. The response includes
- /// the tool's output content, which may be structured data, text, or an error message.
- ///
- /// is .
- /// is .
- /// The server could not find the requested tool, or the server encountered an error while processing the request.
- ///
- ///
- /// // Call a simple echo tool with a string argument
- /// var result = await client.CallToolAsync(
- /// "echo",
- /// new Dictionary<string, object?>
- /// {
- /// ["message"] = "Hello MCP!"
- /// });
- ///
- ///
- [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 CallToolAsync(
- this IMcpClient client,
- string toolName,
- IReadOnlyDictionary? arguments = null,
- IProgress? 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.");
- }
-}
diff --git a/src/ModelContextProtocol.Core/Client/McpClientFactory.cs b/src/ModelContextProtocol.Core/Client/McpClientFactory.cs
deleted file mode 100644
index 805787256..000000000
--- a/src/ModelContextProtocol.Core/Client/McpClientFactory.cs
+++ /dev/null
@@ -1,35 +0,0 @@
-using Microsoft.Extensions.Logging;
-using System.ComponentModel;
-
-namespace ModelContextProtocol.Client;
-
-///
-/// Provides factory methods for creating Model Context Protocol (MCP) clients.
-///
-///
-/// This factory class is the primary way to instantiate instances
-/// that connect to MCP servers. It handles the creation and connection
-/// of appropriate implementations through the supplied transport.
-///
-[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
-{
- /// Creates an , connecting it to the specified server.
- /// The transport instance used to communicate with the server.
- ///
- /// A client configuration object which specifies client capabilities and protocol version.
- /// If , details based on the current process will be employed.
- ///
- /// A logger factory for creating loggers for clients.
- /// The to monitor for cancellation requests. The default is .
- /// An that's connected to the specified server.
- /// is .
- /// is .
- public static async Task CreateAsync(
- IClientTransport clientTransport,
- McpClientOptions? clientOptions = null,
- ILoggerFactory? loggerFactory = null,
- CancellationToken cancellationToken = default)
- => await McpClient.CreateAsync(clientTransport, clientOptions, loggerFactory, cancellationToken);
-}
\ No newline at end of file
diff --git a/src/ModelContextProtocol.Core/Client/McpClientHandlers.cs b/src/ModelContextProtocol.Core/Client/McpClientHandlers.cs
index 56e12d26c..ddd4fed80 100644
--- a/src/ModelContextProtocol.Core/Client/McpClientHandlers.cs
+++ b/src/ModelContextProtocol.Core/Client/McpClientHandlers.cs
@@ -36,7 +36,7 @@ public class McpClientHandlers
///
///
/// Handlers provided via will be registered with the client for the lifetime of the client.
- /// For transient handlers, you can use to register a handler that can
+ /// For transient handlers, you can use to register a handler that can
/// then be unregistered by disposing of the returned from the method.
///
///
diff --git a/src/ModelContextProtocol.Core/Client/McpClientImpl.cs b/src/ModelContextProtocol.Core/Client/McpClientImpl.cs
index ccc7b9c64..4cea0e22d 100644
--- a/src/ModelContextProtocol.Core/Client/McpClientImpl.cs
+++ b/src/ModelContextProtocol.Core/Client/McpClientImpl.cs
@@ -58,12 +58,10 @@ private void RegisterHandlers(McpClientOptions options, NotificationHandlers not
{
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)
{
diff --git a/src/ModelContextProtocol.Core/IMcpEndpoint.cs b/src/ModelContextProtocol.Core/IMcpEndpoint.cs
deleted file mode 100644
index 40106cb07..000000000
--- a/src/ModelContextProtocol.Core/IMcpEndpoint.cs
+++ /dev/null
@@ -1,85 +0,0 @@
-using System.ComponentModel;
-using ModelContextProtocol.Client;
-using ModelContextProtocol.Protocol;
-using ModelContextProtocol.Server;
-
-namespace ModelContextProtocol;
-
-///
-/// Represents a client or server Model Context Protocol (MCP) endpoint.
-///
-///
-///
-/// The MCP endpoint provides the core communication functionality used by both clients and servers:
-///
-/// - Sending JSON-RPC requests and receiving responses.
-/// - Sending notifications to the connected endpoint.
-/// - Registering handlers for receiving notifications.
-///
-///
-///
-/// serves as the base interface for both and
-/// interfaces, providing the common functionality needed for MCP protocol
-/// communication. Most applications will use these more specific interfaces rather than working with
-/// directly.
-///
-///
-/// All MCP endpoints should be properly disposed after use as they implement .
-///
-///
-[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
-{
- /// Gets an identifier associated with the current MCP session.
- ///
- /// Typically populated in transports supporting multiple sessions such as Streamable HTTP or SSE.
- /// Can return if the session hasn't initialized or if the transport doesn't
- /// support multiple sessions (as is the case with STDIO).
- ///
- string? SessionId { get; }
-
- ///
- /// Sends a JSON-RPC request to the connected endpoint and waits for a response.
- ///
- /// The JSON-RPC request to send.
- /// The to monitor for cancellation requests. The default is .
- /// A task containing the endpoint's response.
- /// The transport is not connected, or another error occurs during request processing.
- /// An error occured during request processing.
- ///
- /// 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.
- ///
- Task SendRequestAsync(JsonRpcRequest request, CancellationToken cancellationToken = default);
-
- ///
- /// Sends a JSON-RPC message to the connected endpoint.
- ///
- ///
- /// The JSON-RPC message to send. This can be any type that implements JsonRpcMessage, such as
- /// JsonRpcRequest, JsonRpcResponse, JsonRpcNotification, or JsonRpcError.
- ///
- /// The to monitor for cancellation requests. The default is .
- /// A task that represents the asynchronous send operation.
- /// The transport is not connected.
- /// is .
- ///
- ///
- /// This method provides low-level access to send any JSON-RPC message. For specific message types,
- /// consider using the higher-level methods such as or extension methods
- /// like ,
- /// which provide a simpler API.
- ///
- ///
- /// The method will serialize the message and transmit it using the underlying transport mechanism.
- ///
- ///
- Task SendMessageAsync(JsonRpcMessage message, CancellationToken cancellationToken = default);
-
- /// Registers a handler to be invoked when a notification for the specified method is received.
- /// The notification method.
- /// The handler to be invoked.
- /// An that will remove the registered handler when disposed.
- IAsyncDisposable RegisterNotificationHandler(string method, Func handler);
-}
diff --git a/src/ModelContextProtocol.Core/McpEndpointExtensions.cs b/src/ModelContextProtocol.Core/McpEndpointExtensions.cs
deleted file mode 100644
index 160b31c00..000000000
--- a/src/ModelContextProtocol.Core/McpEndpointExtensions.cs
+++ /dev/null
@@ -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;
-
-///
-/// Provides extension methods for interacting with an .
-///
-///
-///
-/// 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.
-///
-///
-/// These extension methods are designed to be used with both client () and
-/// server () implementations of the interface.
-///
-///
-public static class McpEndpointExtensions
-{
- ///
- /// Sends a JSON-RPC request and attempts to deserialize the result to .
- ///
- /// The type of the request parameters to serialize from.
- /// The type of the result to deserialize to.
- /// The MCP client or server instance.
- /// The JSON-RPC method name to invoke.
- /// The request parameters.
- /// The request ID for the request.
- /// The options governing request serialization.
- /// The to monitor for cancellation requests. The default is .
- /// A task that represents the asynchronous operation. The task result contains the deserialized result.
- [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 SendRequestAsync(
- this IMcpEndpoint endpoint,
- string method,
- TParameters parameters,
- JsonSerializerOptions? serializerOptions = null,
- RequestId requestId = default,
- CancellationToken cancellationToken = default)
- where TResult : notnull
- => AsSessionOrThrow(endpoint).SendRequestAsync(method, parameters, serializerOptions, requestId, cancellationToken);
-
- ///
- /// Sends a parameterless notification to the connected endpoint.
- ///
- /// The MCP client or server instance.
- /// The notification method name.
- /// The to monitor for cancellation requests. The default is .
- /// A task that represents the asynchronous send operation.
- ///
- ///
- /// 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.
- ///
- ///
- [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);
-
- ///
- /// Sends a notification with parameters to the connected endpoint.
- ///
- /// The type of the notification parameters to serialize.
- /// The MCP client or server instance.
- /// The JSON-RPC method name for the notification.
- /// Object representing the notification parameters.
- /// The options governing parameter serialization. If null, default options are used.
- /// The to monitor for cancellation requests. The default is .
- /// A task that represents the asynchronous send operation.
- ///
- ///
- /// 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.
- ///
- ///
- /// The parameters object is serialized to JSON according to the provided serializer options or the default
- /// options if none are specified.
- ///
- ///
- /// The Model Context Protocol defines several standard notification methods in ,
- /// but custom methods can also be used for application-specific notifications.
- ///
- ///
- [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 endpoint,
- string method,
- TParameters parameters,
- JsonSerializerOptions? serializerOptions = null,
- CancellationToken cancellationToken = default)
- => AsSessionOrThrow(endpoint).SendNotificationAsync(method, parameters, serializerOptions, cancellationToken);
-
- ///
- /// Notifies the connected endpoint of progress for a long-running operation.
- ///
- /// The endpoint issuing the notification.
- /// The identifying the operation for which progress is being reported.
- /// The progress update to send, containing information such as percentage complete or status message.
- /// The to monitor for cancellation requests. The default is .
- /// A task representing the completion of the notification operation (not the operation being tracked).
- /// is .
- ///
- ///
- /// 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
- /// that allows the recipient to correlate multiple updates with a specific long-running operation.
- ///
- ///
- /// Progress notifications are sent asynchronously and don't block the operation from continuing.
- ///
- ///
- [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.");
- }
-}
diff --git a/src/ModelContextProtocol.Core/McpSession.Methods.cs b/src/ModelContextProtocol.Core/McpSession.Methods.cs
index be3128cb1..79a6e1a0b 100644
--- a/src/ModelContextProtocol.Core/McpSession.Methods.cs
+++ b/src/ModelContextProtocol.Core/McpSession.Methods.cs
@@ -5,9 +5,7 @@
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
{
///
/// Sends a JSON-RPC request and attempts to deserialize the result to .
diff --git a/src/ModelContextProtocol.Core/McpSession.cs b/src/ModelContextProtocol.Core/McpSession.cs
index acc6df5cd..b0469aec0 100644
--- a/src/ModelContextProtocol.Core/McpSession.cs
+++ b/src/ModelContextProtocol.Core/McpSession.cs
@@ -26,9 +26,7 @@ namespace ModelContextProtocol;
/// All MCP sessions should be properly disposed after use as they implement .
///
///
-#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
{
/// Gets an identifier associated with the current MCP session.
///
diff --git a/src/ModelContextProtocol.Core/ModelContextProtocol.Core.csproj b/src/ModelContextProtocol.Core/ModelContextProtocol.Core.csproj
index cdbe25a2d..5cd8339bf 100644
--- a/src/ModelContextProtocol.Core/ModelContextProtocol.Core.csproj
+++ b/src/ModelContextProtocol.Core/ModelContextProtocol.Core.csproj
@@ -14,8 +14,15 @@
true
+
+
+ $(NoWarn);CS0436
+
+
+
+
diff --git a/src/ModelContextProtocol.Core/Protocol/ClientCapabilities.cs b/src/ModelContextProtocol.Core/Protocol/ClientCapabilities.cs
index d4a803085..87a631f95 100644
--- a/src/ModelContextProtocol.Core/Protocol/ClientCapabilities.cs
+++ b/src/ModelContextProtocol.Core/Protocol/ClientCapabilities.cs
@@ -66,26 +66,4 @@ public sealed class ClientCapabilities
///
[JsonPropertyName("elicitation")]
public ElicitationCapability? Elicitation { get; set; }
-
- /// Gets or sets notification handlers to register with the client.
- ///
- ///
- /// 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.
- ///
- ///
- /// 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.
- ///
- ///
- /// Handlers provided via will be registered with the client for the lifetime of the client.
- /// For transient handlers, can be used to register a handler that can
- /// then be unregistered by disposing of the returned from the method.
- ///
- ///
- [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>>? NotificationHandlers { get; set; }
}
diff --git a/src/ModelContextProtocol.Core/Protocol/CompletionsCapability.cs b/src/ModelContextProtocol.Core/Protocol/CompletionsCapability.cs
index 17f028947..8da6e5fc9 100644
--- a/src/ModelContextProtocol.Core/Protocol/CompletionsCapability.cs
+++ b/src/ModelContextProtocol.Core/Protocol/CompletionsCapability.cs
@@ -28,16 +28,4 @@ namespace ModelContextProtocol.Protocol;
///
public sealed class CompletionsCapability
{
- ///
- /// Gets or sets the handler for completion requests.
- ///
- ///
- /// 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.
- ///
- [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? CompleteHandler { get; set; }
}
diff --git a/src/ModelContextProtocol.Core/Protocol/ElicitRequestParams.cs b/src/ModelContextProtocol.Core/Protocol/ElicitRequestParams.cs
index aabd4e4d9..95f8084d0 100644
--- a/src/ModelContextProtocol.Core/Protocol/ElicitRequestParams.cs
+++ b/src/ModelContextProtocol.Core/Protocol/ElicitRequestParams.cs
@@ -247,10 +247,12 @@ public class Converter : JsonConverter
{
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 override void Write(Utf8JsonWriter writer, PrimitiveSchemaDefinition valu
}
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 override string Type
public bool? Default { get; set; }
}
- ///
- /// Represents a legacy schema for an enum type with enumNames.
- ///
- ///
- /// This schema is deprecated in favor of .
- ///
- [Obsolete("Use TitledSingleSelectEnumSchema instead. This type will be removed in a future version.")]
- public class LegacyTitledEnumSchema : PrimitiveSchemaDefinition
- {
- ///
- [JsonPropertyName("type")]
- public override string Type
- {
- get => "string";
- set
- {
- if (value is not "string")
- {
- throw new ArgumentException("Type must be 'string'.", nameof(value));
- }
- }
- }
-
- /// Gets or sets the list of allowed string values for the enum.
- [JsonPropertyName("enum")]
- [field: MaybeNull]
- public IList Enum
- {
- get => field ??= [];
- set
- {
- Throw.IfNull(value);
- field = value;
- }
- }
-
- /// Gets or sets optional display names corresponding to the enum values.
- [JsonPropertyName("enumNames")]
- public IList? EnumNames { get; set; }
-
- /// Gets or sets the default value for the enum.
- [JsonPropertyName("default")]
- public string? Default { get; set; }
- }
-
///
/// Represents a schema for single-selection enumeration without display titles for options.
///
@@ -970,10 +927,59 @@ public override string Type
}
///
- /// Represents a schema for an enum type. This is a compatibility alias for .
+ /// Represents a legacy schema for an enum type with enumNames.
+ /// This is a compatibility alias for .
///
- [Obsolete("Use UntitledSingleSelectEnumSchema or TitledSingleSelectEnumSchema instead. This type will be removed in a future version.")]
+ ///
+ /// This schema is deprecated in favor of .
+ ///
+ [Obsolete(Obsoletions.LegacyTitledEnumSchema_Message, DiagnosticId = Obsoletions.LegacyTitledEnumSchema_DiagnosticId, UrlFormat = Obsoletions.LegacyTitledEnumSchema_Url)]
public sealed class EnumSchema : LegacyTitledEnumSchema
{
}
+
+ ///
+ /// Represents a legacy schema for an enum type with enumNames.
+ ///
+ ///
+ /// This schema is deprecated in favor of .
+ ///
+ [Obsolete(Obsoletions.LegacyTitledEnumSchema_Message, DiagnosticId = Obsoletions.LegacyTitledEnumSchema_DiagnosticId, UrlFormat = Obsoletions.LegacyTitledEnumSchema_Url)]
+ public class LegacyTitledEnumSchema : PrimitiveSchemaDefinition
+ {
+ ///
+ [JsonPropertyName("type")]
+ public override string Type
+ {
+ get => "string";
+ set
+ {
+ if (value is not "string")
+ {
+ throw new ArgumentException("Type must be 'string'.", nameof(value));
+ }
+ }
+ }
+
+ /// Gets or sets the list of allowed string values for the enum.
+ [JsonPropertyName("enum")]
+ [field: MaybeNull]
+ public IList Enum
+ {
+ get => field ??= [];
+ set
+ {
+ Throw.IfNull(value);
+ field = value;
+ }
+ }
+
+ /// Gets or sets optional display names corresponding to the enum values.
+ [JsonPropertyName("enumNames")]
+ public IList? EnumNames { get; set; }
+
+ /// Gets or sets the default value for the enum.
+ [JsonPropertyName("default")]
+ public string? Default { get; set; }
+ }
}
diff --git a/src/ModelContextProtocol.Core/Protocol/ElicitationCapability.cs b/src/ModelContextProtocol.Core/Protocol/ElicitationCapability.cs
index 9d46bcc43..3fdf5ddcb 100644
--- a/src/ModelContextProtocol.Core/Protocol/ElicitationCapability.cs
+++ b/src/ModelContextProtocol.Core/Protocol/ElicitationCapability.cs
@@ -23,21 +23,4 @@ namespace ModelContextProtocol.Protocol;
///
public sealed class ElicitationCapability
{
- ///
- /// Gets or sets the handler for processing requests.
- ///
- ///
- ///
- /// This handler function is called when an MCP server requests the client to provide additional
- /// information during interactions. The client must set this property for the elicitation capability to work.
- ///
- ///
- /// The handler receives message parameters and a cancellation token.
- /// It should return a containing the response to the elicitation request.
- ///
- ///
- [JsonIgnore]
- [Obsolete($"Use {nameof(McpClientOptions.Handlers.ElicitationHandler)} instead. This member will be removed in a subsequent release.")] // See: https://github.com/modelcontextprotocol/csharp-sdk/issues/774
- [EditorBrowsable(EditorBrowsableState.Never)]
- public Func>? ElicitationHandler { get; set; }
}
\ No newline at end of file
diff --git a/src/ModelContextProtocol.Core/Protocol/LoggingCapability.cs b/src/ModelContextProtocol.Core/Protocol/LoggingCapability.cs
index c166a223a..ac8f03f95 100644
--- a/src/ModelContextProtocol.Core/Protocol/LoggingCapability.cs
+++ b/src/ModelContextProtocol.Core/Protocol/LoggingCapability.cs
@@ -20,11 +20,4 @@ namespace ModelContextProtocol.Protocol;
///
public sealed class LoggingCapability
{
- ///
- /// Gets or sets the handler for set logging level requests from clients.
- ///
- [JsonIgnore]
- [Obsolete($"Use {nameof(McpServerOptions.Handlers.SetLoggingLevelHandler)} instead. This member will be removed in a subsequent release.")] // See: https://github.com/modelcontextprotocol/csharp-sdk/issues/774
- [EditorBrowsable(EditorBrowsableState.Never)]
- public McpRequestHandler? SetLoggingLevelHandler { get; set; }
}
\ No newline at end of file
diff --git a/src/ModelContextProtocol.Core/Protocol/PromptsCapability.cs b/src/ModelContextProtocol.Core/Protocol/PromptsCapability.cs
index cc6d823cd..4a830c48e 100644
--- a/src/ModelContextProtocol.Core/Protocol/PromptsCapability.cs
+++ b/src/ModelContextProtocol.Core/Protocol/PromptsCapability.cs
@@ -31,59 +31,4 @@ public sealed class PromptsCapability
///
[JsonPropertyName("listChanged")]
public bool? ListChanged { get; set; }
-
- ///
- /// Gets or sets the handler for requests.
- ///
- ///
- /// This handler is invoked when a client requests a list of available prompts from the server
- /// via a request. Results from this handler are returned
- /// along with any prompts defined in .
- ///
- [JsonIgnore]
- [Obsolete($"Use {nameof(McpServerOptions.Handlers.ListPromptsHandler)} instead. This member will be removed in a subsequent release.")] // See: https://github.com/modelcontextprotocol/csharp-sdk/issues/774
- [EditorBrowsable(EditorBrowsableState.Never)]
- public McpRequestHandler? ListPromptsHandler { get; set; }
-
- ///
- /// Gets or sets the handler for requests.
- ///
- ///
- ///
- /// This handler is invoked when a client requests details for a specific prompt by name and provides arguments
- /// for the prompt if needed. The handler receives the request context containing the prompt name and any arguments,
- /// and should return a with the prompt messages and other details.
- ///
- ///
- /// This handler will be invoked if the requested prompt name is not found in the ,
- /// allowing for dynamic prompt generation or retrieval from external sources.
- ///
- ///
- [JsonIgnore]
- [Obsolete($"Use {nameof(McpServerOptions.Handlers.GetPromptHandler)} instead. This member will be removed in a subsequent release.")] // See: https://github.com/modelcontextprotocol/csharp-sdk/issues/774
- [EditorBrowsable(EditorBrowsableState.Never)]
- public McpRequestHandler? GetPromptHandler { get; set; }
-
- ///
- /// Gets or sets a collection of prompts that will be served by the server.
- ///
- ///
- ///
- /// The contains the predefined prompts that clients can request from the server.
- /// This collection works in conjunction with and
- /// when those are provided:
- ///
- ///
- /// - For requests: The server returns all prompts from this collection
- /// plus any additional prompts provided by the if it's set.
- ///
- ///
- /// - For requests: The server first checks this collection for the requested prompt.
- /// If not found, it will invoke the as a fallback if one is set.
- ///
- ///
- [JsonIgnore]
- [Obsolete($"Use {nameof(McpServerOptions.PromptCollection)} instead. This member will be removed in a subsequent release.")] // See: https://github.com/modelcontextprotocol/csharp-sdk/issues/774
- [EditorBrowsable(EditorBrowsableState.Never)]
- public McpServerPrimitiveCollection? PromptCollection { get; set; }
}
diff --git a/src/ModelContextProtocol.Core/Protocol/ResourcesCapability.cs b/src/ModelContextProtocol.Core/Protocol/ResourcesCapability.cs
index 7cda1a62a..d7b8f6e44 100644
--- a/src/ModelContextProtocol.Core/Protocol/ResourcesCapability.cs
+++ b/src/ModelContextProtocol.Core/Protocol/ResourcesCapability.cs
@@ -29,92 +29,4 @@ public sealed class ResourcesCapability
///
[JsonPropertyName("listChanged")]
public bool? ListChanged { get; set; }
-
- ///
- /// Gets or sets the handler for requests.
- ///
- ///
- /// This handler is called when clients request available resource templates that can be used
- /// to create resources within the Model Context Protocol server.
- /// Resource templates define the structure and URI patterns for resources accessible in the system,
- /// allowing clients to discover available resource types and their access patterns.
- ///
- [JsonIgnore]
- [Obsolete($"Use {nameof(McpServerOptions.Handlers.ListResourceTemplatesHandler)} instead. This member will be removed in a subsequent release.")] // See: https://github.com/modelcontextprotocol/csharp-sdk/issues/774
- [EditorBrowsable(EditorBrowsableState.Never)]
- public McpRequestHandler? ListResourceTemplatesHandler { get; set; }
-
- ///
- /// Gets or sets the handler for requests.
- ///
- ///
- /// This handler responds to client requests for available resources and returns information about resources accessible through the server.
- /// The implementation should return a with the matching resources.
- ///
- [JsonIgnore]
- [Obsolete($"Use {nameof(McpServerOptions.Handlers.ListResourcesHandler)} instead. This member will be removed in a subsequent release.")] // See: https://github.com/modelcontextprotocol/csharp-sdk/issues/774
- [EditorBrowsable(EditorBrowsableState.Never)]
- public McpRequestHandler? ListResourcesHandler { get; set; }
-
- ///
- /// Gets or sets the handler for requests.
- ///
- ///
- /// This handler is responsible for retrieving the content of a specific resource identified by its URI in the Model Context Protocol.
- /// When a client sends a resources/read request, this handler is invoked with the resource URI.
- /// The handler should implement logic to locate and retrieve the requested resource, then return
- /// its contents in a ReadResourceResult object.
- ///
- [JsonIgnore]
- [Obsolete($"Use {nameof(McpServerOptions.Handlers.ReadResourceHandler)} instead. This member will be removed in a subsequent release.")] // See: https://github.com/modelcontextprotocol/csharp-sdk/issues/774
- [EditorBrowsable(EditorBrowsableState.Never)]
- public McpRequestHandler? ReadResourceHandler { get; set; }
-
- ///
- /// Gets or sets the handler for requests.
- ///
- ///
- /// When a client sends a request, this handler is invoked with the resource URI
- /// to be subscribed to. The implementation should register the client's interest in receiving updates
- /// for the specified resource.
- /// Subscriptions allow clients to receive real-time notifications when resources change, without
- /// requiring polling.
- ///
- [JsonIgnore]
- [Obsolete($"Use {nameof(McpServerOptions.Handlers.SubscribeToResourcesHandler)} instead. This member will be removed in a subsequent release.")] // See: https://github.com/modelcontextprotocol/csharp-sdk/issues/774
- [EditorBrowsable(EditorBrowsableState.Never)]
- public McpRequestHandler? SubscribeToResourcesHandler { get; set; }
-
- ///
- /// Gets or sets the handler for requests.
- ///
- ///
- /// When a client sends a request, this handler is invoked with the resource URI
- /// to be unsubscribed from. The implementation should remove the client's registration for receiving updates
- /// about the specified resource.
- ///
- [JsonIgnore]
- [Obsolete($"Use {nameof(McpServerOptions.Handlers.UnsubscribeFromResourcesHandler)} instead. This member will be removed in a subsequent release.")] // See: https://github.com/modelcontextprotocol/csharp-sdk/issues/774
- [EditorBrowsable(EditorBrowsableState.Never)]
- public McpRequestHandler? UnsubscribeFromResourcesHandler { get; set; }
-
- ///
- /// Gets or sets a collection of resources served by the server.
- ///
- ///
- ///
- /// Resources specified via augment the ,
- /// and handlers, if provided. Resources with template expressions in their URI templates are considered resource templates
- /// and are listed via ListResourceTemplate, whereas resources without template parameters are considered static resources and are listed with ListResources.
- ///
- ///
- /// ReadResource requests will first check the for the exact resource being requested. If no match is found, they'll proceed to
- /// try to match the resource against each resource template in . If no match is still found, the request will fall back to
- /// any handler registered for .
- ///
- ///
- [JsonIgnore]
- [Obsolete($"Use {nameof(McpServerOptions.ResourceCollection)} instead. This member will be removed in a subsequent release.")] // See: https://github.com/modelcontextprotocol/csharp-sdk/issues/774
- [EditorBrowsable(EditorBrowsableState.Never)]
- public McpServerResourceCollection? ResourceCollection { get; set; }
}
diff --git a/src/ModelContextProtocol.Core/Protocol/RootsCapability.cs b/src/ModelContextProtocol.Core/Protocol/RootsCapability.cs
index 582e7f98c..4739cde4a 100644
--- a/src/ModelContextProtocol.Core/Protocol/RootsCapability.cs
+++ b/src/ModelContextProtocol.Core/Protocol/RootsCapability.cs
@@ -33,16 +33,4 @@ public sealed class RootsCapability
///
[JsonPropertyName("listChanged")]
public bool? ListChanged { get; set; }
-
- ///
- /// Gets or sets the handler for requests.
- ///
- ///
- /// This handler is invoked when a client sends a request to retrieve available roots.
- /// The handler receives request parameters and should return a containing the collection of available roots.
- ///
- [JsonIgnore]
- [Obsolete($"Use {nameof(McpClientOptions.Handlers.RootsHandler)} instead. This member will be removed in a subsequent release.")] // See: https://github.com/modelcontextprotocol/csharp-sdk/issues/774
- [EditorBrowsable(EditorBrowsableState.Never)]
- public Func>? RootsHandler { get; set; }
}
diff --git a/src/ModelContextProtocol.Core/Protocol/SamplingCapability.cs b/src/ModelContextProtocol.Core/Protocol/SamplingCapability.cs
index aba24a77c..cb530e795 100644
--- a/src/ModelContextProtocol.Core/Protocol/SamplingCapability.cs
+++ b/src/ModelContextProtocol.Core/Protocol/SamplingCapability.cs
@@ -19,29 +19,6 @@ namespace ModelContextProtocol.Protocol;
///
public sealed class SamplingCapability
{
- ///
- /// Gets or sets the handler for processing requests.
- ///
- ///
- ///
- /// This handler function is called when an MCP server requests the client to generate content
- /// using an AI model. The client must set this property for the sampling capability to work.
- ///
- ///
- /// The handler receives message parameters, a progress reporter for updates, and a
- /// cancellation token. It should return a containing the
- /// generated content.
- ///
- ///
- /// You can create a handler using the extension
- /// method with any implementation of .
- ///
- ///
- [JsonIgnore]
- [Obsolete($"Use {nameof(McpClientOptions.Handlers.SamplingHandler)} instead. This member will be removed in a subsequent release.")] // See: https://github.com/modelcontextprotocol/csharp-sdk/issues/774
- [EditorBrowsable(EditorBrowsableState.Never)]
- public Func, CancellationToken, ValueTask>? SamplingHandler { get; set; }
-
///
/// Gets or sets whether the client supports context inclusion via includeContext parameter.
///
@@ -57,4 +34,3 @@ public sealed class SamplingCapability
[JsonPropertyName("tools")]
public SamplingToolsCapability? Tools { get; set; }
}
-
diff --git a/src/ModelContextProtocol.Core/Protocol/ServerCapabilities.cs b/src/ModelContextProtocol.Core/Protocol/ServerCapabilities.cs
index 5dda8da9f..86a823396 100644
--- a/src/ModelContextProtocol.Core/Protocol/ServerCapabilities.cs
+++ b/src/ModelContextProtocol.Core/Protocol/ServerCapabilities.cs
@@ -65,26 +65,4 @@ public sealed class ServerCapabilities
///
[JsonPropertyName("completions")]
public CompletionsCapability? Completions { get; set; }
-
- /// Gets or sets notification handlers to register with the server.
- ///
- ///
- /// When constructed, the server will enumerate these handlers once, which may contain multiple handlers per notification method key.
- /// The server will not re-enumerate the sequence after initialization.
- ///
- ///
- /// Notification handlers allow the server to respond to client-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.
- ///
- ///
- /// Handlers provided via will be registered with the server for the lifetime of the server.
- /// For transient handlers, may be used to register a handler that can
- /// then be unregistered by disposing of the returned from the method.
- ///
- ///
- [JsonIgnore]
- [Obsolete($"Use {nameof(McpServerOptions.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>>? NotificationHandlers { get; set; }
}
diff --git a/src/ModelContextProtocol.Core/Protocol/ToolsCapability.cs b/src/ModelContextProtocol.Core/Protocol/ToolsCapability.cs
index de8ee3654..b06c2f123 100644
--- a/src/ModelContextProtocol.Core/Protocol/ToolsCapability.cs
+++ b/src/ModelContextProtocol.Core/Protocol/ToolsCapability.cs
@@ -22,49 +22,4 @@ public sealed class ToolsCapability
///
[JsonPropertyName("listChanged")]
public bool? ListChanged { get; set; }
-
- ///
- /// Gets or sets the handler for requests.
- ///
- ///
- /// The handler should return a list of available tools when requested by a client.
- /// It supports pagination through the cursor mechanism, where the client can make
- /// repeated calls with the cursor returned by the previous call to retrieve more tools.
- /// When used in conjunction with , both the tools from this handler
- /// and the tools from the collection will be combined to form the complete list of available tools.
- ///
- [JsonIgnore]
- [Obsolete($"Use {nameof(McpServerOptions.Handlers.ListToolsHandler)} instead. This member will be removed in a subsequent release.")] // See: https://github.com/modelcontextprotocol/csharp-sdk/issues/774
- [EditorBrowsable(EditorBrowsableState.Never)]
- public McpRequestHandler? ListToolsHandler { get; set; }
-
- ///
- /// Gets or sets the handler for requests.
- ///
- ///
- /// This handler is invoked when a client makes a call to a tool that isn't found in the .
- /// The handler should implement logic to execute the requested tool and return appropriate results.
- /// It receives a containing information about the tool
- /// being called and its arguments, and should return a with the execution results.
- ///
- [JsonIgnore]
- [Obsolete($"Use {nameof(McpServerOptions.Handlers.CallToolHandler)} instead. This member will be removed in a subsequent release.")] // See: https://github.com/modelcontextprotocol/csharp-sdk/issues/774
- [EditorBrowsable(EditorBrowsableState.Never)]
- public McpRequestHandler? CallToolHandler { get; set; }
-
- ///
- /// Gets or sets a collection of tools served by the server.
- ///
- ///
- /// Tools will specified via augment the and
- /// , if provided. ListTools requests will output information about every tool
- /// in and then also any tools output by , if it's
- /// non-. CallTool requests will first check for the tool
- /// being requested, and if the tool is not found in the , any specified
- /// will be invoked as a fallback.
- ///
- [JsonIgnore]
- [Obsolete($"Use {nameof(McpServerOptions.ToolCollection)} instead. This member will be removed in a subsequent release.")] // See: https://github.com/modelcontextprotocol/csharp-sdk/issues/774
- [EditorBrowsable(EditorBrowsableState.Never)]
- public McpServerPrimitiveCollection? ToolCollection { get; set; }
}
diff --git a/src/ModelContextProtocol.Core/Server/IMcpServer.cs b/src/ModelContextProtocol.Core/Server/IMcpServer.cs
deleted file mode 100644
index 8b88aa7a2..000000000
--- a/src/ModelContextProtocol.Core/Server/IMcpServer.cs
+++ /dev/null
@@ -1,65 +0,0 @@
-using ModelContextProtocol.Protocol;
-using System.ComponentModel;
-
-namespace ModelContextProtocol.Server;
-
-///
-/// Represents an instance of a Model Context Protocol (MCP) server that connects to and communicates with an MCP client.
-///
-[Obsolete($"Use {nameof(McpServer)} instead. This member will be removed in a subsequent release.")] // See: https://github.com/modelcontextprotocol/csharp-sdk/issues/774
-[EditorBrowsable(EditorBrowsableState.Never)]
-public interface IMcpServer : IMcpEndpoint
-{
- ///
- /// Gets the capabilities supported by the client.
- ///
- ///
- ///
- /// These capabilities are established during the initialization handshake and indicate
- /// which features the client supports, such as sampling, roots, and other
- /// protocol-specific functionality.
- ///
- ///
- /// Server implementations can check these capabilities to determine which features
- /// are available when interacting with the client.
- ///
- ///
- ClientCapabilities? ClientCapabilities { get; }
-
- ///
- /// Gets the version and implementation information of the connected client.
- ///
- ///
- ///
- /// This property contains identification information about the client that has connected to this server,
- /// including its name and version. This information is provided by the client during initialization.
- ///
- ///
- /// Server implementations can use this information for logging, tracking client versions,
- /// or implementing client-specific behaviors.
- ///
- ///
- Implementation? ClientInfo { get; }
-
- ///
- /// Gets the options used to construct this server.
- ///
- ///
- /// These options define the server's capabilities, protocol version, and other configuration
- /// settings that were used to initialize the server.
- ///
- McpServerOptions ServerOptions { get; }
-
- ///
- /// Gets the service provider for the server.
- ///
- IServiceProvider? Services { get; }
-
- /// Gets the last logging level set by the client, or if it's never been set.
- LoggingLevel? LoggingLevel { get; }
-
- ///
- /// Runs the server, listening for and handling client requests.
- ///
- Task RunAsync(CancellationToken cancellationToken = default);
-}
diff --git a/src/ModelContextProtocol.Core/Server/McpServer.Methods.cs b/src/ModelContextProtocol.Core/Server/McpServer.Methods.cs
index 604f21583..f8b7b7781 100644
--- a/src/ModelContextProtocol.Core/Server/McpServer.Methods.cs
+++ b/src/ModelContextProtocol.Core/Server/McpServer.Methods.cs
@@ -14,9 +14,7 @@ namespace ModelContextProtocol.Server;
///
/// Represents an instance of a Model Context Protocol (MCP) server that connects to and communicates with an MCP client.
///
-#pragma warning disable CS0618 // Type or member is obsolete
-public abstract partial class McpServer : McpSession, IMcpServer
-#pragma warning restore CS0618 // Type or member is obsolete
+public abstract partial class McpServer : McpSession
{
///
/// Caches request schemas for elicitation requests based on the type and serializer options.
diff --git a/src/ModelContextProtocol.Core/Server/McpServer.cs b/src/ModelContextProtocol.Core/Server/McpServer.cs
index 02c17de1a..2d8ea6826 100644
--- a/src/ModelContextProtocol.Core/Server/McpServer.cs
+++ b/src/ModelContextProtocol.Core/Server/McpServer.cs
@@ -5,9 +5,7 @@ namespace ModelContextProtocol.Server;
///
/// Represents an instance of a Model Context Protocol (MCP) server that connects to and communicates with an MCP client.
///
-#pragma warning disable CS0618 // Type or member is obsolete
-public abstract partial class McpServer : McpSession, IMcpServer
-#pragma warning restore CS0618 // Type or member is obsolete
+public abstract partial class McpServer : McpSession
{
///
/// Gets the capabilities supported by the client.
diff --git a/src/ModelContextProtocol.Core/Server/McpServerExtensions.cs b/src/ModelContextProtocol.Core/Server/McpServerExtensions.cs
deleted file mode 100644
index b20865576..000000000
--- a/src/ModelContextProtocol.Core/Server/McpServerExtensions.cs
+++ /dev/null
@@ -1,138 +0,0 @@
-using Microsoft.Extensions.AI;
-using Microsoft.Extensions.Logging;
-using ModelContextProtocol.Protocol;
-using System.ComponentModel;
-using System.Diagnostics.CodeAnalysis;
-using System.Runtime.CompilerServices;
-using System.Text.Json;
-
-namespace ModelContextProtocol.Server;
-
-///
-/// Provides extension methods for interacting with an instance.
-///
-public static class McpServerExtensions
-{
- ///
- /// Requests to sample an LLM via the client using the specified request parameters.
- ///
- /// The server instance initiating the request.
- /// The parameters for the sampling request.
- /// The to monitor for cancellation requests.
- /// A task containing the sampling result from the client.
- /// is .
- /// The client does not support sampling.
- ///
- /// This method requires the client to support sampling capabilities.
- /// It allows detailed control over sampling parameters including messages, system prompt, temperature,
- /// and token limits.
- ///
- [Obsolete($"Use {nameof(McpServer)}.{nameof(McpServer.SampleAsync)} 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 SampleAsync(
- this IMcpServer server, CreateMessageRequestParams request, CancellationToken cancellationToken = default)
- => AsServerOrThrow(server).SampleAsync(request, cancellationToken);
-
- ///
- /// Requests to sample an LLM via the client using the provided chat messages and options.
- ///
- /// The server initiating the request.
- /// The messages to send as part of the request.
- /// The options to use for the request, including model parameters and constraints.
- /// The to monitor for cancellation requests. The default is .
- /// A task containing the chat response from the model.
- /// is .
- /// is .
- /// The client does not support sampling.
- ///
- /// This method converts the provided chat messages into a format suitable for the sampling API,
- /// handling different content types such as text, images, and audio.
- ///
- [Obsolete($"Use {nameof(McpServer)}.{nameof(McpServer.SampleAsync)} 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 SampleAsync(
- this IMcpServer server,
- IEnumerable messages, ChatOptions? options = default, CancellationToken cancellationToken = default)
- => AsServerOrThrow(server).SampleAsync(messages, options, cancellationToken);
-
- ///
- /// Creates an wrapper that can be used to send sampling requests to the client.
- ///
- /// The server to be wrapped as an .
- /// The that can be used to issue sampling requests to the client.
- /// is .
- /// The client does not support sampling.
- [Obsolete($"Use {nameof(McpServer)}.{nameof(McpServer.AsSamplingChatClient)} instead. This member will be removed in a subsequent release.")] // See: https://github.com/modelcontextprotocol/csharp-sdk/issues/774
- [EditorBrowsable(EditorBrowsableState.Never)]
- public static IChatClient AsSamplingChatClient(this IMcpServer server)
- => AsServerOrThrow(server).AsSamplingChatClient();
-
- /// Gets an on which logged messages will be sent as notifications to the client.
- /// The server to wrap as an .
- /// An that can be used to log to the client..
- [Obsolete($"Use {nameof(McpServer)}.{nameof(McpServer.AsSamplingChatClient)} instead. This member will be removed in a subsequent release.")] // See: https://github.com/modelcontextprotocol/csharp-sdk/issues/774
- [EditorBrowsable(EditorBrowsableState.Never)]
- public static ILoggerProvider AsClientLoggerProvider(this IMcpServer server)
- => AsServerOrThrow(server).AsClientLoggerProvider();
-
- ///
- /// Requests the client to list the roots it exposes.
- ///
- /// The server initiating the request.
- /// The parameters for the list roots request.
- /// The to monitor for cancellation requests.
- /// A task containing the list of roots exposed by the client.
- /// is .
- /// The client does not support roots.
- ///
- /// This method requires the client to support the roots capability.
- /// Root resources allow clients to expose a hierarchical structure of resources that can be
- /// navigated and accessed by the server. These resources might include file systems, databases,
- /// or other structured data sources that the client makes available through the protocol.
- ///
- [Obsolete($"Use {nameof(McpServer)}.{nameof(McpServer.RequestRootsAsync)} 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 RequestRootsAsync(
- this IMcpServer server, ListRootsRequestParams request, CancellationToken cancellationToken = default)
- => AsServerOrThrow(server).RequestRootsAsync(request, cancellationToken);
-
- ///
- /// Requests additional information from the user via the client, allowing the server to elicit structured data.
- ///
- /// The server initiating the request.
- /// The parameters for the elicitation request.
- /// The to monitor for cancellation requests.
- /// A task containing the elicitation result.
- /// is .
- /// The client does not support elicitation.
- ///
- /// This method requires the client to support the elicitation capability.
- ///
- [Obsolete($"Use {nameof(McpServer)}.{nameof(McpServer.ElicitAsync)} 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 ElicitAsync(
- this IMcpServer server, ElicitRequestParams request, CancellationToken cancellationToken = default)
- => AsServerOrThrow(server).ElicitAsync(request, cancellationToken);
-
- [MethodImpl(MethodImplOptions.AggressiveInlining)]
-#pragma warning disable CS0618 // Type or member is obsolete
- private static McpServer AsServerOrThrow(IMcpServer server, [CallerMemberName] string memberName = "")
-#pragma warning restore CS0618 // Type or member is obsolete
- {
- if (server is not McpServer mcpServer)
- {
- ThrowInvalidSessionType(memberName);
- }
-
- return mcpServer;
-
- [DoesNotReturn]
- [MethodImpl(MethodImplOptions.NoInlining)]
- static void ThrowInvalidSessionType(string memberName)
- => throw new InvalidOperationException(
- $"Only arguments assignable to '{nameof(McpServer)}' are supported. " +
- $"Prefer using '{nameof(McpServer)}.{memberName}' instead, as " +
- $"'{nameof(McpServerExtensions)}.{memberName}' is obsolete and will be " +
- $"removed in the future.");
- }
-}
diff --git a/src/ModelContextProtocol.Core/Server/McpServerFactory.cs b/src/ModelContextProtocol.Core/Server/McpServerFactory.cs
deleted file mode 100644
index 7a6609d0d..000000000
--- a/src/ModelContextProtocol.Core/Server/McpServerFactory.cs
+++ /dev/null
@@ -1,34 +0,0 @@
-using Microsoft.Extensions.Logging;
-using ModelContextProtocol.Protocol;
-using System.ComponentModel;
-
-namespace ModelContextProtocol.Server;
-
-///
-/// Provides a factory for creating instances.
-///
-///
-/// This is the recommended way to create instances.
-/// The factory handles proper initialization of server instances with the required dependencies.
-///
-[Obsolete($"Use {nameof(McpServer)}.{nameof(McpServer.Create)} instead. This member will be removed in a subsequent release.")] // See: https://github.com/modelcontextprotocol/csharp-sdk/issues/774
-[EditorBrowsable(EditorBrowsableState.Never)]
-public static class McpServerFactory
-{
- ///
- /// Creates a new instance of an .
- ///
- /// Transport to use for the server representing an already-established MCP session.
- /// Configuration options for this server, including capabilities.
- /// Logger factory to use for logging. If null, logging will be disabled.
- /// Optional service provider to create new instances of tools and other dependencies.
- /// An instance that should be disposed when no longer needed.
- /// is .
- /// is .
- public static IMcpServer Create(
- ITransport transport,
- McpServerOptions serverOptions,
- ILoggerFactory? loggerFactory = null,
- IServiceProvider? serviceProvider = null)
- => McpServer.Create(transport, serverOptions, loggerFactory, serviceProvider);
-}
diff --git a/src/ModelContextProtocol.Core/Server/McpServerHandlers.cs b/src/ModelContextProtocol.Core/Server/McpServerHandlers.cs
index 77102ac7e..6dbcea8af 100644
--- a/src/ModelContextProtocol.Core/Server/McpServerHandlers.cs
+++ b/src/ModelContextProtocol.Core/Server/McpServerHandlers.cs
@@ -169,7 +169,7 @@ public sealed class McpServerHandlers
///
///
/// Handlers provided via will be registered with the server for the lifetime of the server.
- /// For transient handlers, may be used to register a handler that can
+ /// For transient handlers, may be used to register a handler that can
/// then be unregistered by disposing of the returned from the method.
///
///
diff --git a/src/ModelContextProtocol.Core/Server/McpServerImpl.cs b/src/ModelContextProtocol.Core/Server/McpServerImpl.cs
index 41408c22b..706578d9b 100644
--- a/src/ModelContextProtocol.Core/Server/McpServerImpl.cs
+++ b/src/ModelContextProtocol.Core/Server/McpServerImpl.cs
@@ -230,10 +230,6 @@ private void ConfigureCompletion(McpServerOptions options)
var completeHandler = options.Handlers.CompleteHandler;
var completionsCapability = options.Capabilities?.Completions;
-#pragma warning disable CS0618 // Type or member is obsolete
- completeHandler ??= completionsCapability?.CompleteHandler;
-#pragma warning restore CS0618 // Type or member is obsolete
-
if (completeHandler is null && completionsCapability is null)
{
return;
@@ -266,14 +262,6 @@ private void ConfigureResources(McpServerOptions options)
var resources = options.ResourceCollection;
var resourcesCapability = options.Capabilities?.Resources;
-#pragma warning disable CS0618 // Type or member is obsolete
- listResourcesHandler ??= resourcesCapability?.ListResourcesHandler;
- listResourceTemplatesHandler ??= resourcesCapability?.ListResourceTemplatesHandler;
- readResourceHandler ??= resourcesCapability?.ReadResourceHandler;
- subscribeHandler ??= resourcesCapability?.SubscribeToResourcesHandler;
- unsubscribeHandler ??= resourcesCapability?.UnsubscribeFromResourcesHandler;
-#pragma warning restore CS0618 // Type or member is obsolete
-
if (listResourcesHandler is null && listResourceTemplatesHandler is null && readResourceHandler is null &&
subscribeHandler is null && unsubscribeHandler is null && resources is null &&
resourcesCapability is null)
@@ -427,11 +415,6 @@ private void ConfigurePrompts(McpServerOptions options)
var prompts = options.PromptCollection;
var promptsCapability = options.Capabilities?.Prompts;
-#pragma warning disable CS0618 // Type or member is obsolete
- listPromptsHandler ??= promptsCapability?.ListPromptsHandler;
- getPromptHandler ??= promptsCapability?.GetPromptHandler;
-#pragma warning restore CS0618 // Type or member is obsolete
-
if (listPromptsHandler is null && getPromptHandler is null && prompts is null &&
promptsCapability is null)
{
@@ -515,11 +498,6 @@ private void ConfigureTools(McpServerOptions options)
var tools = options.ToolCollection;
var toolsCapability = options.Capabilities?.Tools;
-#pragma warning disable CS0618 // Type or member is obsolete
- listToolsHandler ??= toolsCapability?.ListToolsHandler;
- callToolHandler ??= toolsCapability?.CallToolHandler;
-#pragma warning restore CS0618 // Type or member is obsolete
-
if (listToolsHandler is null && callToolHandler is null && tools is null &&
toolsCapability is null)
{
@@ -618,10 +596,6 @@ private void ConfigureLogging(McpServerOptions options)
// We don't require that the handler be provided, as we always store the provided log level to the server.
var setLoggingLevelHandler = options.Handlers.SetLoggingLevelHandler;
-#pragma warning disable CS0618 // Type or member is obsolete
- setLoggingLevelHandler ??= options.Capabilities?.Logging?.SetLoggingLevelHandler;
-#pragma warning restore CS0618 // Type or member is obsolete
-
// Apply filters to the handler
if (setLoggingLevelHandler is not null)
{
diff --git a/src/ModelContextProtocol.Core/Server/RequestServiceProvider.cs b/src/ModelContextProtocol.Core/Server/RequestServiceProvider.cs
index 9359ea157..86f0a6884 100644
--- a/src/ModelContextProtocol.Core/Server/RequestServiceProvider.cs
+++ b/src/ModelContextProtocol.Core/Server/RequestServiceProvider.cs
@@ -19,18 +19,13 @@ internal sealed class RequestServiceProvider(RequestContext
serviceType == typeof(RequestContext) ||
serviceType == typeof(McpServer) ||
-#pragma warning disable CS0618 // Type or member is obsolete
- serviceType == typeof(IMcpServer) ||
-#pragma warning restore CS0618 // Type or member is obsolete
serviceType == typeof(IProgress) ||
serviceType == typeof(ClaimsPrincipal);
///
public object? GetService(Type serviceType) =>
serviceType == typeof(RequestContext) ? request :
-#pragma warning disable CS0618 // Type or member is obsolete
- serviceType == typeof(McpServer) || serviceType == typeof(IMcpServer) ? request.Server :
-#pragma warning restore CS0618 // Type or member is obsolete
+ serviceType == typeof(McpServer) ? request.Server :
serviceType == typeof(IProgress) ?
(request.Params?.ProgressToken is { } progressToken ? new TokenProgress(request.Server, progressToken) : NullProgress.Instance) :
serviceType == typeof(ClaimsPrincipal) ? request.User :
diff --git a/tests/ModelContextProtocol.Tests/Client/McpClientExtensionsTests.cs b/tests/ModelContextProtocol.Tests/Client/McpClientExtensionsTests.cs
deleted file mode 100644
index 5a2816003..000000000
--- a/tests/ModelContextProtocol.Tests/Client/McpClientExtensionsTests.cs
+++ /dev/null
@@ -1,394 +0,0 @@
-using ModelContextProtocol.Client;
-using ModelContextProtocol.Protocol;
-using Moq;
-using System.Text.Json;
-
-namespace ModelContextProtocol.Tests;
-
-#pragma warning disable CS0618 // Type or member is obsolete
-
-public class McpClientExtensionsTests
-{
- [Fact]
- public async Task PingAsync_Throws_When_Not_McpClient()
- {
- var client = new Mock(MockBehavior.Strict).Object;
-
- var ex = await Assert.ThrowsAsync(async () => await client.PingAsync(TestContext.Current.CancellationToken));
- Assert.Contains("Prefer using 'McpClient.PingAsync' instead", ex.Message);
- }
-
- [Fact]
- public async Task GetPromptAsync_Throws_When_Not_McpClient()
- {
- var client = new Mock(MockBehavior.Strict).Object;
-
- var ex = await Assert.ThrowsAsync(async () => await client.GetPromptAsync(
- "name", cancellationToken: TestContext.Current.CancellationToken));
- Assert.Contains("Prefer using 'McpClient.GetPromptAsync' instead", ex.Message);
- }
-
- [Fact]
- public async Task CallToolAsync_Throws_When_Not_McpClient()
- {
- var client = new Mock(MockBehavior.Strict).Object;
-
- var ex = await Assert.ThrowsAsync(async () => await client.CallToolAsync(
- "tool", cancellationToken: TestContext.Current.CancellationToken));
- Assert.Contains("Prefer using 'McpClient.CallToolAsync' instead", ex.Message);
- }
-
- [Fact]
- public async Task ListResourcesAsync_Throws_When_Not_McpClient()
- {
- var client = new Mock(MockBehavior.Strict).Object;
-
- var ex = await Assert.ThrowsAsync(async () => await client.ListResourcesAsync(
- cancellationToken: TestContext.Current.CancellationToken));
- Assert.Contains("Prefer using 'McpClient.ListResourcesAsync' instead", ex.Message);
- }
-
- [Fact]
- public void EnumerateResourcesAsync_Throws_When_Not_McpClient()
- {
- var client = new Mock(MockBehavior.Strict).Object;
-
- var ex = Assert.Throws(() => client.EnumerateResourcesAsync(cancellationToken: TestContext.Current.CancellationToken));
- Assert.Contains("Prefer using 'McpClient.EnumerateResourcesAsync' instead", ex.Message);
- }
-
- [Fact]
- public async Task SubscribeToResourceAsync_String_Throws_When_Not_McpClient()
- {
- var client = new Mock(MockBehavior.Strict).Object;
-
- var ex = await Assert.ThrowsAsync(async () => await client.SubscribeToResourceAsync(
- "mcp://resource/1", TestContext.Current.CancellationToken));
- Assert.Contains("Prefer using 'McpClient.SubscribeToResourceAsync' instead", ex.Message);
- }
-
- [Fact]
- public async Task SubscribeToResourceAsync_Uri_Throws_When_Not_McpClient()
- {
- var client = new Mock(MockBehavior.Strict).Object;
-
- var ex = await Assert.ThrowsAsync(async () => await client.SubscribeToResourceAsync(
- new Uri("mcp://resource/1"), TestContext.Current.CancellationToken));
- Assert.Contains("Prefer using 'McpClient.SubscribeToResourceAsync' instead", ex.Message);
- }
-
- [Fact]
- public async Task UnsubscribeFromResourceAsync_String_Throws_When_Not_McpClient()
- {
- var client = new Mock(MockBehavior.Strict).Object;
-
- var ex = await Assert.ThrowsAsync(async () => await client.UnsubscribeFromResourceAsync(
- "mcp://resource/1", TestContext.Current.CancellationToken));
- Assert.Contains("Prefer using 'McpClient.UnsubscribeFromResourceAsync' instead", ex.Message);
- }
-
- [Fact]
- public async Task UnsubscribeFromResourceAsync_Uri_Throws_When_Not_McpClient()
- {
- var client = new Mock(MockBehavior.Strict).Object;
-
- var ex = await Assert.ThrowsAsync(async () => await client.UnsubscribeFromResourceAsync(
- new Uri("mcp://resource/1"), TestContext.Current.CancellationToken));
- Assert.Contains("Prefer using 'McpClient.UnsubscribeFromResourceAsync' instead", ex.Message);
- }
-
- [Fact]
- public async Task ReadResourceAsync_String_Throws_When_Not_McpClient()
- {
- var client = new Mock(MockBehavior.Strict).Object;
-
- var ex = await Assert.ThrowsAsync(async () => await client.ReadResourceAsync(
- "mcp://resource/1", TestContext.Current.CancellationToken));
- Assert.Contains("Prefer using 'McpClient.ReadResourceAsync' instead", ex.Message);
- }
-
- [Fact]
- public async Task ReadResourceAsync_Uri_Throws_When_Not_McpClient()
- {
- var client = new Mock(MockBehavior.Strict).Object;
-
- var ex = await Assert.ThrowsAsync(async () => await client.ReadResourceAsync(
- new Uri("mcp://resource/1"), TestContext.Current.CancellationToken));
- Assert.Contains("Prefer using 'McpClient.ReadResourceAsync' instead", ex.Message);
- }
-
- [Fact]
- public async Task ReadResourceAsync_Template_Throws_When_Not_McpClient()
- {
- var client = new Mock(MockBehavior.Strict).Object;
-
- var ex = await Assert.ThrowsAsync(async () => await client.ReadResourceAsync(
- "mcp://resource/{id}", new Dictionary { ["id"] = 1 }, TestContext.Current.CancellationToken));
- Assert.Contains("Prefer using 'McpClient.ReadResourceAsync' instead", ex.Message);
- }
-
- [Fact]
- public async Task CompleteAsync_Throws_When_Not_McpClient()
- {
- var client = new Mock(MockBehavior.Strict).Object;
- var reference = new PromptReference { Name = "prompt" };
-
- var ex = await Assert.ThrowsAsync(async () => await client.CompleteAsync(
- reference, "arg", "val", TestContext.Current.CancellationToken));
- Assert.Contains("Prefer using 'McpClient.CompleteAsync' instead", ex.Message);
- }
-
- [Fact]
- public async Task ListToolsAsync_Throws_When_Not_McpClient()
- {
- var client = new Mock(MockBehavior.Strict).Object;
-
- var ex = await Assert.ThrowsAsync(async () => await client.ListToolsAsync(
- cancellationToken: TestContext.Current.CancellationToken));
- Assert.Contains("Prefer using 'McpClient.ListToolsAsync' instead", ex.Message);
- }
-
- [Fact]
- public void EnumerateToolsAsync_Throws_When_Not_McpClient()
- {
- var client = new Mock(MockBehavior.Strict).Object;
-
- var ex = Assert.Throws(() => client.EnumerateToolsAsync(cancellationToken: TestContext.Current.CancellationToken));
- Assert.Contains("Prefer using 'McpClient.EnumerateToolsAsync' instead", ex.Message);
- }
-
- [Fact]
- public async Task ListPromptsAsync_Throws_When_Not_McpClient()
- {
- var client = new Mock(MockBehavior.Strict).Object;
-
- var ex = await Assert.ThrowsAsync(async () => await client.ListPromptsAsync(
- cancellationToken: TestContext.Current.CancellationToken));
- Assert.Contains("Prefer using 'McpClient.ListPromptsAsync' instead", ex.Message);
- }
-
- [Fact]
- public void EnumeratePromptsAsync_Throws_When_Not_McpClient()
- {
- var client = new Mock(MockBehavior.Strict).Object;
-
- var ex = Assert.Throws(() => client.EnumeratePromptsAsync(cancellationToken: TestContext.Current.CancellationToken));
- Assert.Contains("Prefer using 'McpClient.EnumeratePromptsAsync' instead", ex.Message);
- }
-
- [Fact]
- public async Task ListResourceTemplatesAsync_Throws_When_Not_McpClient()
- {
- var client = new Mock(MockBehavior.Strict).Object;
-
- var ex = await Assert.ThrowsAsync(async () => await client.ListResourceTemplatesAsync(
- cancellationToken: TestContext.Current.CancellationToken));
- Assert.Contains("Prefer using 'McpClient.ListResourceTemplatesAsync' instead", ex.Message);
- }
-
- [Fact]
- public void EnumerateResourceTemplatesAsync_Throws_When_Not_McpClient()
- {
- var client = new Mock(MockBehavior.Strict).Object;
-
- var ex = Assert.Throws(() => client.EnumerateResourceTemplatesAsync(cancellationToken: TestContext.Current.CancellationToken));
- Assert.Contains("Prefer using 'McpClient.EnumerateResourceTemplatesAsync' instead", ex.Message);
- }
-
- [Fact]
- public async Task PingAsync_Forwards_To_McpClient_SendRequestAsync()
- {
- var mockClient = new Mock { CallBase = true };
-
- mockClient
- .Setup(c => c.SendRequestAsync(It.IsAny(), It.IsAny()))
- .ReturnsAsync(new JsonRpcResponse
- {
- Result = JsonSerializer.SerializeToNode(new object(), McpJsonUtilities.DefaultOptions),
- });
-
- IMcpClient client = mockClient.Object;
-
- await client.PingAsync(TestContext.Current.CancellationToken);
-
- mockClient.Verify(c => c.SendRequestAsync(It.IsAny(), It.IsAny()), Times.Once);
- }
-
- [Fact]
- public async Task GetPromptAsync_Forwards_To_McpClient_SendRequestAsync()
- {
- var mockClient = new Mock { CallBase = true };
-
- var resultPayload = new GetPromptResult
- {
- Messages = [new()
- {
- Role = Role.User,
- Content = new TextContentBlock { Text = "hi" }
- }]
- };
-
- mockClient
- .Setup(c => c.SendRequestAsync(It.IsAny(), It.IsAny()))
- .ReturnsAsync(new JsonRpcResponse
- {
- Result = JsonSerializer.SerializeToNode(resultPayload, McpJsonUtilities.DefaultOptions),
- });
-
- IMcpClient client = mockClient.Object;
-
- var result = await client.GetPromptAsync("name", cancellationToken: TestContext.Current.CancellationToken);
-
- Assert.Equal("hi", Assert.IsType(result.Messages[0].Content).Text);
- mockClient.Verify(c => c.SendRequestAsync(It.IsAny(), It.IsAny()), Times.Once);
- }
-
- [Fact]
- public async Task CallToolAsync_Forwards_To_McpClient_SendRequestAsync()
- {
- var mockClient = new Mock { CallBase = true };
-
- var callResult = new CallToolResult { Content = [new TextContentBlock { Text = "ok" }] };
-
- mockClient
- .Setup(c => c.SendRequestAsync(It.IsAny(), It.IsAny()))
- .ReturnsAsync(new JsonRpcResponse
- {
- Result = JsonSerializer.SerializeToNode(callResult, McpJsonUtilities.DefaultOptions),
- });
-
- IMcpClient client = mockClient.Object;
-
- var result = await client.CallToolAsync("tool", cancellationToken: TestContext.Current.CancellationToken);
-
- Assert.Equal("ok", Assert.IsType(result.Content[0]).Text);
- mockClient.Verify(c => c.SendRequestAsync(It.IsAny(), It.IsAny()), Times.Once);
- }
-
- [Fact]
- public async Task SubscribeToResourceAsync_Forwards_To_McpClient_SendRequestAsync()
- {
- var mockClient = new Mock { CallBase = true };
-
- mockClient
- .Setup(c => c.SendRequestAsync(It.IsAny(), It.IsAny()))
- .ReturnsAsync(new JsonRpcResponse
- {
- Result = JsonSerializer.SerializeToNode(new EmptyResult(), McpJsonUtilities.DefaultOptions),
- });
-
- IMcpClient client = mockClient.Object;
-
- await client.SubscribeToResourceAsync("mcp://resource/1", TestContext.Current.CancellationToken);
-
- mockClient.Verify(c => c.SendRequestAsync(It.IsAny(), It.IsAny()), Times.Once);
- }
-
- [Fact]
- public async Task UnsubscribeFromResourceAsync_Forwards_To_McpClient_SendRequestAsync()
- {
- var mockClient = new Mock { CallBase = true };
-
- mockClient
- .Setup(c => c.SendRequestAsync(It.IsAny(), It.IsAny()))
- .ReturnsAsync(new JsonRpcResponse
- {
- Result = JsonSerializer.SerializeToNode(new EmptyResult(), McpJsonUtilities.DefaultOptions),
- });
-
- IMcpClient client = mockClient.Object;
-
- await client.UnsubscribeFromResourceAsync("mcp://resource/1", TestContext.Current.CancellationToken);
-
- mockClient.Verify(c => c.SendRequestAsync(It.IsAny(), It.IsAny()), Times.Once);
- }
-
- [Fact]
- public async Task CompleteAsync_Forwards_To_McpClient_SendRequestAsync()
- {
- var mockClient = new Mock { CallBase = true };
-
- var completion = new Completion { Values = ["one", "two"] };
- var resultPayload = new CompleteResult { Completion = completion };
-
- mockClient
- .Setup(c => c.SendRequestAsync(It.IsAny(), It.IsAny()))
- .ReturnsAsync(new JsonRpcResponse
- {
- Result = JsonSerializer.SerializeToNode(resultPayload, McpJsonUtilities.DefaultOptions),
- });
-
- IMcpClient client = mockClient.Object;
-
- var result = await client.CompleteAsync(new PromptReference { Name = "p" }, "arg", "val", TestContext.Current.CancellationToken);
-
- Assert.Contains("one", result.Completion.Values);
- mockClient.Verify(c => c.SendRequestAsync(It.IsAny(), It.IsAny()), Times.Once);
- }
-
- [Fact]
- public async Task ReadResourceAsync_String_Forwards_To_McpClient_SendRequestAsync()
- {
- var mockClient = new Mock { CallBase = true };
-
- var resultPayload = new ReadResourceResult { Contents = [] };
-
- mockClient
- .Setup(c => c.SendRequestAsync(It.IsAny(), It.IsAny()))
- .ReturnsAsync(new JsonRpcResponse
- {
- Result = JsonSerializer.SerializeToNode(resultPayload, McpJsonUtilities.DefaultOptions),
- });
-
- IMcpClient client = mockClient.Object;
-
- var result = await client.ReadResourceAsync("mcp://resource/1", TestContext.Current.CancellationToken);
-
- Assert.NotNull(result);
- mockClient.Verify(c => c.SendRequestAsync(It.IsAny(), It.IsAny()), Times.Once);
- }
-
- [Fact]
- public async Task ReadResourceAsync_Uri_Forwards_To_McpClient_SendRequestAsync()
- {
- var mockClient = new Mock { CallBase = true };
-
- var resultPayload = new ReadResourceResult { Contents = [] };
-
- mockClient
- .Setup(c => c.SendRequestAsync(It.IsAny(), It.IsAny()))
- .ReturnsAsync(new JsonRpcResponse
- {
- Result = JsonSerializer.SerializeToNode(resultPayload, McpJsonUtilities.DefaultOptions),
- });
-
- IMcpClient client = mockClient.Object;
-
- var result = await client.ReadResourceAsync(new Uri("mcp://resource/1"), TestContext.Current.CancellationToken);
-
- Assert.NotNull(result);
- mockClient.Verify(c => c.SendRequestAsync(It.IsAny(), It.IsAny()), Times.Once);
- }
-
- [Fact]
- public async Task ReadResourceAsync_Template_Forwards_To_McpClient_SendRequestAsync()
- {
- var mockClient = new Mock { CallBase = true };
-
- var resultPayload = new ReadResourceResult { Contents = [] };
-
- mockClient
- .Setup(c => c.SendRequestAsync(It.IsAny(), It.IsAny()))
- .ReturnsAsync(new JsonRpcResponse
- {
- Result = JsonSerializer.SerializeToNode(resultPayload, McpJsonUtilities.DefaultOptions),
- });
-
- IMcpClient client = mockClient.Object;
-
- var result = await client.ReadResourceAsync("mcp://resource/{id}", new Dictionary { ["id"] = 1 }, TestContext.Current.CancellationToken);
-
- Assert.NotNull(result);
- mockClient.Verify(c => c.SendRequestAsync(It.IsAny(), It.IsAny()), Times.Once);
- }
-}
diff --git a/tests/ModelContextProtocol.Tests/McpEndpointExtensionsTests.cs b/tests/ModelContextProtocol.Tests/McpEndpointExtensionsTests.cs
deleted file mode 100644
index 613c703c3..000000000
--- a/tests/ModelContextProtocol.Tests/McpEndpointExtensionsTests.cs
+++ /dev/null
@@ -1,118 +0,0 @@
-using ModelContextProtocol.Protocol;
-using Moq;
-using System.Text.Json;
-
-namespace ModelContextProtocol.Tests;
-
-#pragma warning disable CS0618 // Type or member is obsolete
-
-public class McpEndpointExtensionsTests
-{
- [Fact]
- public async Task SendRequestAsync_Generic_Throws_When_Not_McpSession()
- {
- var endpoint = new Mock(MockBehavior.Strict).Object;
-
- var ex = await Assert.ThrowsAsync(async () => await McpEndpointExtensions.SendRequestAsync(
- endpoint, "method", "param", cancellationToken: TestContext.Current.CancellationToken));
- Assert.Contains("Prefer using 'McpServer.SendRequestAsync' instead", ex.Message);
- }
-
- [Fact]
- public async Task SendNotificationAsync_Parameterless_Throws_When_Not_McpSession()
- {
- var endpoint = new Mock(MockBehavior.Strict).Object;
-
- var ex = await Assert.ThrowsAsync(async () => await McpEndpointExtensions.SendNotificationAsync(
- endpoint, "notify", cancellationToken: TestContext.Current.CancellationToken));
- Assert.Contains("Prefer using 'McpServer.SendNotificationAsync' instead", ex.Message);
- }
-
- [Fact]
- public async Task SendNotificationAsync_Generic_Throws_When_Not_McpSession()
- {
- var endpoint = new Mock(MockBehavior.Strict).Object;
-
- var ex = await Assert.ThrowsAsync(async () => await McpEndpointExtensions.SendNotificationAsync(
- endpoint, "notify", "payload", cancellationToken: TestContext.Current.CancellationToken));
- Assert.Contains("Prefer using 'McpServer.SendNotificationAsync' instead", ex.Message);
- }
-
- [Fact]
- public async Task NotifyProgressAsync_Throws_When_Not_McpSession()
- {
- var endpoint = new Mock(MockBehavior.Strict).Object;
-
- var ex = await Assert.ThrowsAsync(async () => await McpEndpointExtensions.NotifyProgressAsync(
- endpoint, new ProgressToken("t1"), new ProgressNotificationValue { Progress = 0.5f }, cancellationToken: TestContext.Current.CancellationToken));
- Assert.Contains("Prefer using 'McpServer.NotifyProgressAsync' instead", ex.Message);
- }
-
- [Fact]
- public async Task SendRequestAsync_Generic_Forwards_To_McpSession_SendRequestAsync()
- {
- var mockSession = new Mock { CallBase = true };
-
- mockSession
- .Setup(s => s.SendRequestAsync(It.IsAny(), It.IsAny()))
- .ReturnsAsync(new JsonRpcResponse
- {
- Result = JsonSerializer.SerializeToNode(42, McpJsonUtilities.DefaultOptions),
- });
-
- IMcpEndpoint endpoint = mockSession.Object;
-
- var result = await endpoint.SendRequestAsync("method", "param", cancellationToken: TestContext.Current.CancellationToken);
-
- Assert.Equal(42, result);
- mockSession.Verify(s => s.SendRequestAsync(It.IsAny(), It.IsAny()), Times.Once);
- }
-
- [Fact]
- public async Task SendNotificationAsync_Parameterless_Forwards_To_McpSession_SendMessageAsync()
- {
- var mockSession = new Mock { CallBase = true };
-
- mockSession
- .Setup(s => s.SendMessageAsync(It.IsAny(), It.IsAny()))
- .Returns(Task.CompletedTask);
-
- IMcpEndpoint endpoint = mockSession.Object;
-
- await endpoint.SendNotificationAsync("notify", cancellationToken: TestContext.Current.CancellationToken);
-
- mockSession.Verify(s => s.SendMessageAsync(It.IsAny(), It.IsAny()), Times.Once);
- }
-
- [Fact]
- public async Task SendNotificationAsync_Generic_Forwards_To_McpSession_SendMessageAsync()
- {
- var mockSession = new Mock { CallBase = true };
-
- mockSession
- .Setup(s => s.SendMessageAsync(It.IsAny(), It.IsAny()))
- .Returns(Task.CompletedTask);
-
- IMcpEndpoint endpoint = mockSession.Object;
-
- await endpoint.SendNotificationAsync("notify", "payload", cancellationToken: TestContext.Current.CancellationToken);
-
- mockSession.Verify(s => s.SendMessageAsync(It.IsAny(), It.IsAny()), Times.Once);
- }
-
- [Fact]
- public async Task NotifyProgressAsync_Forwards_To_McpSession_SendMessageAsync()
- {
- var mockSession = new Mock { CallBase = true };
-
- mockSession
- .Setup(s => s.SendMessageAsync(It.IsAny(), It.IsAny()))
- .Returns(Task.CompletedTask);
-
- IMcpEndpoint endpoint = mockSession.Object;
-
- await endpoint.NotifyProgressAsync(new ProgressToken("progress-token"), new ProgressNotificationValue { Progress = 1 }, cancellationToken: TestContext.Current.CancellationToken);
-
- mockSession.Verify(s => s.SendMessageAsync(It.IsAny(), It.IsAny()), Times.Once);
- }
-}
\ No newline at end of file
diff --git a/tests/ModelContextProtocol.Tests/Protocol/ElicitationDefaultValuesTests.cs b/tests/ModelContextProtocol.Tests/Protocol/ElicitationDefaultValuesTests.cs
index c29f7d033..5bf7ddb40 100644
--- a/tests/ModelContextProtocol.Tests/Protocol/ElicitationDefaultValuesTests.cs
+++ b/tests/ModelContextProtocol.Tests/Protocol/ElicitationDefaultValuesTests.cs
@@ -1,14 +1,12 @@
using ModelContextProtocol.Protocol;
using System.Text.Json;
-#pragma warning disable CS0618 // Type or member is obsolete
-
namespace ModelContextProtocol.Tests.Protocol;
-public class ElicitationDefaultValuesTests
+public static class ElicitationDefaultValuesTests
{
[Fact]
- public void StringSchema_Default_Serializes_Correctly()
+ public static void StringSchema_Default_Serializes_Correctly()
{
// Arrange
var schema = new ElicitRequestParams.StringSchema
@@ -32,7 +30,7 @@ public void StringSchema_Default_Serializes_Correctly()
}
[Fact]
- public void StringSchema_Default_Null_DoesNotSerialize()
+ public static void StringSchema_Default_Null_DoesNotSerialize()
{
// Arrange
var schema = new ElicitRequestParams.StringSchema
@@ -48,7 +46,7 @@ public void StringSchema_Default_Null_DoesNotSerialize()
}
[Fact]
- public void NumberSchema_Default_Serializes_Correctly()
+ public static void NumberSchema_Default_Serializes_Correctly()
{
// Arrange
var schema = new ElicitRequestParams.NumberSchema
@@ -74,7 +72,7 @@ public void NumberSchema_Default_Serializes_Correctly()
}
[Fact]
- public void NumberSchema_Integer_Default_Serializes_Correctly()
+ public static void NumberSchema_Integer_Default_Serializes_Correctly()
{
// Arrange
var schema = new ElicitRequestParams.NumberSchema
@@ -97,7 +95,7 @@ public void NumberSchema_Integer_Default_Serializes_Correctly()
}
[Fact]
- public void NumberSchema_Default_Null_DoesNotSerialize()
+ public static void NumberSchema_Default_Null_DoesNotSerialize()
{
// Arrange
var schema = new ElicitRequestParams.NumberSchema
@@ -113,50 +111,7 @@ public void NumberSchema_Default_Null_DoesNotSerialize()
}
[Fact]
- public void EnumSchema_Default_Serializes_Correctly()
- {
- // Arrange
- var schema = new ElicitRequestParams.EnumSchema
- {
- Title = "Priority",
- Description = "Task priority",
- Enum = ["low", "medium", "high"],
- EnumNames = ["Low Priority", "Medium Priority", "High Priority"],
- Default = "medium"
- };
-
- // Act - serialize as base type to use the converter
- string json = JsonSerializer.Serialize(schema, McpJsonUtilities.DefaultOptions);
- var deserialized = JsonSerializer.Deserialize(json, McpJsonUtilities.DefaultOptions);
-
- // Assert
- Assert.NotNull(deserialized);
- var enumSchema = Assert.IsType(deserialized);
- Assert.Equal("medium", enumSchema.Default);
- Assert.Equal("Priority", enumSchema.Title);
- Assert.Equal("Task priority", enumSchema.Description);
- Assert.Contains("\"default\":\"medium\"", json);
- }
-
- [Fact]
- public void EnumSchema_Default_Null_DoesNotSerialize()
- {
- // Arrange
- var schema = new ElicitRequestParams.EnumSchema
- {
- Title = "Priority",
- Enum = ["low", "medium", "high"]
- };
-
- // Act - serialize as base type to use the converter
- string json = JsonSerializer.Serialize(schema, McpJsonUtilities.DefaultOptions);
-
- // Assert
- Assert.DoesNotContain("\"default\"", json);
- }
-
- [Fact]
- public void BooleanSchema_Default_True_Serializes_Correctly()
+ public static void BooleanSchema_Default_True_Serializes_Correctly()
{
// Arrange
var schema = new ElicitRequestParams.BooleanSchema
@@ -178,7 +133,7 @@ public void BooleanSchema_Default_True_Serializes_Correctly()
}
[Fact]
- public void BooleanSchema_Default_False_Serializes_Correctly()
+ public static void BooleanSchema_Default_False_Serializes_Correctly()
{
// Arrange
var schema = new ElicitRequestParams.BooleanSchema
@@ -199,7 +154,7 @@ public void BooleanSchema_Default_False_Serializes_Correctly()
}
[Fact]
- public void PrimitiveSchemaDefinition_StringSchema_WithDefault_RoundTrips()
+ public static void PrimitiveSchemaDefinition_StringSchema_WithDefault_RoundTrips()
{
// Arrange
var schema = new ElicitRequestParams.StringSchema
@@ -221,7 +176,7 @@ public void PrimitiveSchemaDefinition_StringSchema_WithDefault_RoundTrips()
}
[Fact]
- public void PrimitiveSchemaDefinition_NumberSchema_WithDefault_RoundTrips()
+ public static void PrimitiveSchemaDefinition_NumberSchema_WithDefault_RoundTrips()
{
// Arrange
var schema = new ElicitRequestParams.NumberSchema
@@ -245,30 +200,110 @@ public void PrimitiveSchemaDefinition_NumberSchema_WithDefault_RoundTrips()
}
[Fact]
- public void PrimitiveSchemaDefinition_EnumSchema_WithDefault_RoundTrips()
+ public static void UntitledSingleSelectEnumSchema_Default_Null_DoesNotSerialize()
{
// Arrange
- var schema = new ElicitRequestParams.EnumSchema
+ var schema = new ElicitRequestParams.UntitledSingleSelectEnumSchema
{
- Title = "Status",
- Enum = ["draft", "published", "archived"],
- Default = "draft"
+ Title = "Priority",
+ Enum = ["low", "medium", "high"]
};
- // Act - serialize as base type to test the converter
+ // Act - serialize as base type to use the converter
string json = JsonSerializer.Serialize(schema, McpJsonUtilities.DefaultOptions);
- var deserialized = JsonSerializer.Deserialize(json, McpJsonUtilities.DefaultOptions);
// Assert
- Assert.NotNull(deserialized);
- // EnumSchema without enumNames deserializes as UntitledSingleSelectEnumSchema
- var enumSchema = Assert.IsType(deserialized);
- Assert.Equal("draft", enumSchema.Default);
- Assert.Equal(["draft", "published", "archived"], enumSchema.Enum);
+ Assert.DoesNotContain("\"default\"", json);
+ }
+
+ [Fact]
+ public static void TitledSingleSelectEnumSchema_Default_Null_DoesNotSerialize()
+ {
+ // Arrange
+ var schema = new ElicitRequestParams.TitledSingleSelectEnumSchema
+ {
+ Title = "Priority",
+ OneOf =
+ {
+ new ElicitRequestParams.EnumSchemaOption { Title = "Low", Const = "low" },
+ new ElicitRequestParams.EnumSchemaOption { Title = "Medium", Const = "medium" },
+ new ElicitRequestParams.EnumSchemaOption { Title = "High", Const = "high" }
+ }
+ };
+
+ // Act - serialize as base type to use the converter
+ string json = JsonSerializer.Serialize(schema, McpJsonUtilities.DefaultOptions);
+
+ // Assert
+ Assert.DoesNotContain("\"default\"", json);
+ }
+
+ [Fact]
+ public static void UntitledMultiSelectEnumSchema_Default_Null_DoesNotSerialize()
+ {
+ // Arrange
+ var schema = new ElicitRequestParams.UntitledMultiSelectEnumSchema
+ {
+ Title = "Tags",
+ Items = new ElicitRequestParams.UntitledEnumItemsSchema
+ {
+ Enum = ["tag1", "tag2", "tag3"]
+ }
+ };
+
+ // Act - serialize as base type to use the converter
+ string json = JsonSerializer.Serialize(schema, McpJsonUtilities.DefaultOptions);
+
+ // Assert
+ Assert.DoesNotContain("\"default\"", json);
+ }
+
+ [Fact]
+ public static void TitledMultiSelectEnumSchema_Default_Null_DoesNotSerialize()
+ {
+ // Arrange
+ var schema = new ElicitRequestParams.TitledMultiSelectEnumSchema
+ {
+ Title = "Tags",
+ Items = new ElicitRequestParams.TitledEnumItemsSchema
+ {
+ AnyOf =
+ [
+ new ElicitRequestParams.EnumSchemaOption { Title = "Tag 1", Const = "tag1" },
+ new ElicitRequestParams.EnumSchemaOption { Title = "Tag 2", Const = "tag2" },
+ new ElicitRequestParams.EnumSchemaOption { Title = "Tag 3", Const = "tag3" }
+ ]
+ }
+ };
+ // Act - serialize as base type to use the converter
+ string json = JsonSerializer.Serialize(schema, McpJsonUtilities.DefaultOptions);
+
+ // Assert
+ Assert.DoesNotContain("\"default\"", json);
}
+#pragma warning disable MCP9001 // LegacyTitledEnumSchema is deprecated but supported for backward compatibility
[Fact]
- public void RequestSchema_WithAllDefaultTypes_Serializes_Correctly()
+ public static void LegacyTitledEnumSchema_Default_Null_DoesNotSerialize()
+ {
+ // Arrange
+ var schema = new ElicitRequestParams.LegacyTitledEnumSchema
+ {
+ Title = "Legacy Options",
+ Enum = ["option1", "option2"],
+ EnumNames = ["Option 1", "Option 2"]
+ };
+
+ // Act - serialize as base type to use the converter
+ string json = JsonSerializer.Serialize(schema, McpJsonUtilities.DefaultOptions);
+
+ // Assert
+ Assert.DoesNotContain("\"default\"", json);
+ }
+#pragma warning restore MCP9001
+
+ [Fact]
+ public static void RequestSchema_WithAllDefaultTypes_Serializes_Correctly()
{
// Arrange
var requestParams = new ElicitRequestParams
@@ -299,11 +334,46 @@ public void RequestSchema_WithAllDefaultTypes_Serializes_Correctly()
Title = "Active",
Default = true
},
- ["status"] = new ElicitRequestParams.EnumSchema
+ ["status"] = new ElicitRequestParams.UntitledSingleSelectEnumSchema
{
Title = "Status",
Enum = ["active", "inactive"],
Default = "active"
+ },
+ ["tags"] = new ElicitRequestParams.UntitledMultiSelectEnumSchema
+ {
+ Title = "Tags",
+ Items = new ElicitRequestParams.UntitledEnumItemsSchema
+ {
+ Enum = ["tag1", "tag2", "tag3"]
+ },
+ Default = ["tag1", "tag3"]
+ },
+ ["salutation"] = new ElicitRequestParams.TitledSingleSelectEnumSchema
+ {
+ Title = "Salutation",
+ OneOf =
+ {
+ new ElicitRequestParams.EnumSchemaOption { Title = "N/A", Const = "none" },
+ new ElicitRequestParams.EnumSchemaOption { Title = "Mr.", Const = "mr" },
+ new ElicitRequestParams.EnumSchemaOption { Title = "Ms.", Const = "ms" },
+ new ElicitRequestParams.EnumSchemaOption { Title = "Dr.", Const = "dr" }
+ },
+ Default = "none"
+ },
+ ["categories"] = new ElicitRequestParams.TitledMultiSelectEnumSchema
+ {
+ Title = "Categories",
+ Items = new ElicitRequestParams.TitledEnumItemsSchema
+ {
+ AnyOf =
+ [
+ new ElicitRequestParams.EnumSchemaOption { Title = "Category 1", Const = "cat1" },
+ new ElicitRequestParams.EnumSchemaOption { Title = "Category 2", Const = "cat2" },
+ new ElicitRequestParams.EnumSchemaOption { Title = "Category 3", Const = "cat3" }
+ ]
+ },
+ Default = ["cat2", "cat3"]
}
}
}
@@ -315,22 +385,30 @@ public void RequestSchema_WithAllDefaultTypes_Serializes_Correctly()
// Assert
Assert.NotNull(deserialized);
- Assert.Equal(5, deserialized.RequestedSchema.Properties.Count);
-
+ Assert.Equal(8, deserialized.RequestedSchema.Properties.Count);
+
var nameSchema = Assert.IsType(deserialized.RequestedSchema.Properties["name"]);
Assert.Equal("John Doe", nameSchema.Default);
-
+
var ageSchema = Assert.IsType(deserialized.RequestedSchema.Properties["age"]);
Assert.Equal(30, ageSchema.Default);
-
+
var scoreSchema = Assert.IsType(deserialized.RequestedSchema.Properties["score"]);
Assert.Equal(85.5, scoreSchema.Default);
-
+
var activeSchema = Assert.IsType(deserialized.RequestedSchema.Properties["active"]);
Assert.True(activeSchema.Default);
-
- // EnumSchema without enumNames deserializes as UntitledSingleSelectEnumSchema
+
var statusSchema = Assert.IsType(deserialized.RequestedSchema.Properties["status"]);
Assert.Equal("active", statusSchema.Default);
+
+ var tagsSchema = Assert.IsType(deserialized.RequestedSchema.Properties["tags"]);
+ Assert.Equal(new List { "tag1", "tag3" }, tagsSchema.Default);
+
+ var salutationSchema = Assert.IsType(deserialized.RequestedSchema.Properties["salutation"]);
+ Assert.Equal("none", salutationSchema.Default);
+
+ var categoriesSchema = Assert.IsType(deserialized.RequestedSchema.Properties["categories"]);
+ Assert.Equal(new List { "cat2", "cat3" }, categoriesSchema.Default);
}
}
diff --git a/tests/ModelContextProtocol.Tests/Protocol/ElicitationTests.cs b/tests/ModelContextProtocol.Tests/Protocol/ElicitationTests.cs
index 92db42f72..781b281e5 100644
--- a/tests/ModelContextProtocol.Tests/Protocol/ElicitationTests.cs
+++ b/tests/ModelContextProtocol.Tests/Protocol/ElicitationTests.cs
@@ -3,8 +3,6 @@
using ModelContextProtocol.Protocol;
using System.Text.Json;
-#pragma warning disable CS0618 // Type or member is obsolete
-
namespace ModelContextProtocol.Tests.Configuration;
public partial class ElicitationTests : ClientServerTestBase
@@ -46,10 +44,14 @@ protected override void ConfigureServices(ServiceCollection services, IMcpServer
Description = "description4",
Default = true,
},
- ["prop4"] = new ElicitRequestParams.EnumSchema
+ ["prop4"] = new ElicitRequestParams.TitledSingleSelectEnumSchema
{
- Enum = ["option1", "option2", "option3"],
- EnumNames = ["Name1", "Name2", "Name3"],
+ OneOf =
+ [
+ new ElicitRequestParams.EnumSchemaOption { Const = "option1", Title = "Name1" },
+ new ElicitRequestParams.EnumSchemaOption { Const = "option2", Title = "Name2" },
+ new ElicitRequestParams.EnumSchemaOption { Const = "option3", Title = "Name3" },
+ ]
},
},
},
@@ -104,9 +106,9 @@ public async Task Can_Elicit_Information()
break;
case "prop4":
- var primitiveEnum = Assert.IsType(entry.Value);
- Assert.Equal(["option1", "option2", "option3"], primitiveEnum.Enum);
- Assert.Equal(["Name1", "Name2", "Name3"], primitiveEnum.EnumNames);
+ var primitiveEnum = Assert.IsType(entry.Value);
+ Assert.Equal(["option1", "option2", "option3"], primitiveEnum.OneOf.Select(e => e.Const));
+ Assert.Equal(["Name1", "Name2", "Name3"], primitiveEnum.OneOf.Select(e => e.Title));
break;
default:
diff --git a/tests/ModelContextProtocol.Tests/Protocol/ElicitationTypedTests.cs b/tests/ModelContextProtocol.Tests/Protocol/ElicitationTypedTests.cs
index ab0a5d4f4..737c5d627 100644
--- a/tests/ModelContextProtocol.Tests/Protocol/ElicitationTypedTests.cs
+++ b/tests/ModelContextProtocol.Tests/Protocol/ElicitationTypedTests.cs
@@ -4,8 +4,6 @@
using System.Text.Json;
using System.Text.Json.Serialization;
-#pragma warning disable CS0618 // Type or member is obsolete
-
namespace ModelContextProtocol.Tests.Configuration;
public partial class ElicitationTypedTests : ClientServerTestBase
diff --git a/tests/ModelContextProtocol.Tests/Protocol/EnumSchemaTests.cs b/tests/ModelContextProtocol.Tests/Protocol/EnumSchemaTests.cs
index 86f0be591..f0addb9d7 100644
--- a/tests/ModelContextProtocol.Tests/Protocol/EnumSchemaTests.cs
+++ b/tests/ModelContextProtocol.Tests/Protocol/EnumSchemaTests.cs
@@ -1,8 +1,6 @@
using ModelContextProtocol.Protocol;
using System.Text.Json;
-#pragma warning disable CS0618 // Type or member is obsolete
-
namespace ModelContextProtocol.Tests.Protocol;
public class EnumSchemaTests
@@ -161,36 +159,9 @@ public void TitledMultiSelectEnumSchema_Serializes_Correctly()
}
[Fact]
- public void LegacyTitledEnumSchema_WithEnumNames_Deserializes_As_EnumSchema()
+ public void SingleSelectEnum_WithEnum_Deserializes_As_UntitledSingleSelect()
{
- // Arrange - JSON with enumNames should deserialize as EnumSchema for backward compatibility
- string json = """
- {
- "type": "string",
- "title": "Status",
- "enum": ["active", "inactive", "pending"],
- "enumNames": ["Active", "Inactive", "Pending"],
- "default": "active"
- }
- """;
-
- // Act
- var deserialized = JsonSerializer.Deserialize(json, McpJsonUtilities.DefaultOptions);
-
- // Assert
- Assert.NotNull(deserialized);
- var result = Assert.IsType(deserialized);
- Assert.Equal("string", result.Type);
- Assert.Equal("Status", result.Title);
- Assert.Equal(["active", "inactive", "pending"], result.Enum);
- Assert.Equal(["Active", "Inactive", "Pending"], result.EnumNames);
- Assert.Equal("active", result.Default);
- }
-
- [Fact]
- public void EnumSchema_WithoutEnumNames_Deserializes_As_UntitledSingleSelect()
- {
- // Arrange - JSON without enumNames should deserialize as UntitledSingleSelectEnumSchema
+ // Arrange - JSON with enum should deserialize as UntitledSingleSelectEnumSchema
string json = """
{
"type": "string",
@@ -213,7 +184,7 @@ public void EnumSchema_WithoutEnumNames_Deserializes_As_UntitledSingleSelect()
}
[Fact]
- public void EnumSchema_WithOneOf_Deserializes_As_TitledSingleSelect()
+ public void SingleSelectEnum_WithOneOf_Deserializes_As_TitledSingleSelect()
{
// Arrange - JSON with oneOf should deserialize as TitledSingleSelectEnumSchema
string json = """
@@ -246,7 +217,7 @@ public void EnumSchema_WithOneOf_Deserializes_As_TitledSingleSelect()
[Fact]
public void MultiSelectEnum_WithEnum_Deserializes_As_UntitledMultiSelect()
{
- // Arrange
+ // Arrange - JSON with items.enum should deserialize as UntitledMultiSelectEnumSchema
string json = """
{
"type": "array",
@@ -275,7 +246,7 @@ public void MultiSelectEnum_WithEnum_Deserializes_As_UntitledMultiSelect()
[Fact]
public void MultiSelectEnum_WithAnyOf_Deserializes_As_TitledMultiSelect()
{
- // Arrange
+ // Arrange - JSON with items.anyOf should deserialize as TitledMultiSelectEnumSchema
string json = """
{
"type": "array",
@@ -306,4 +277,92 @@ public void MultiSelectEnum_WithAnyOf_Deserializes_As_TitledMultiSelect()
Assert.Equal("Administrator", result.Items.AnyOf[0].Title);
Assert.Equal(["user"], result.Default);
}
+
+#pragma warning disable MCP9001 // EnumSchema and LegacyTitledEnumSchema are deprecated but supported for backward compatibility
+ [Fact]
+ public void LegacyTitledEnumSchema_Serializes_Correctly()
+ {
+ // Arrange
+ var schema = new ElicitRequestParams.LegacyTitledEnumSchema
+ {
+ Title = "Environment",
+ Description = "Deployment environment",
+ Enum = ["dev", "staging", "prod"],
+ EnumNames = ["Development", "Staging", "Production"],
+ Default = "staging"
+ };
+
+ // Act
+ string json = JsonSerializer.Serialize(schema, McpJsonUtilities.DefaultOptions);
+ var deserialized = JsonSerializer.Deserialize(json, McpJsonUtilities.DefaultOptions);
+
+ // Assert
+ Assert.NotNull(deserialized);
+ var result = Assert.IsType(deserialized);
+ Assert.Equal("string", result.Type);
+ Assert.Equal("Environment", result.Title);
+ Assert.Equal("Deployment environment", result.Description);
+ Assert.Equal(["dev", "staging", "prod"], result.Enum);
+ Assert.Equal(["Development", "Staging", "Production"], result.EnumNames);
+ Assert.Equal("staging", result.Default);
+ Assert.Contains("\"enumNames\":[\"Development\",\"Staging\",\"Production\"]", json);
+ }
+
+ [Fact]
+ public void EnumSchema_Serializes_Correctly()
+ {
+ // Arrange
+ var schema = new ElicitRequestParams.EnumSchema
+ {
+ Title = "Environment",
+ Description = "Deployment environment",
+ Enum = ["dev", "staging", "prod"],
+ EnumNames = ["Development", "Staging", "Production"],
+ Default = "staging"
+ };
+
+ // Act
+ string json = JsonSerializer.Serialize(schema, McpJsonUtilities.DefaultOptions);
+ var deserialized = JsonSerializer.Deserialize(json, McpJsonUtilities.DefaultOptions);
+
+ // Assert
+ Assert.NotNull(deserialized);
+ var result = Assert.IsType(deserialized);
+ Assert.Equal("string", result.Type);
+ Assert.Equal("Environment", result.Title);
+ Assert.Equal("Deployment environment", result.Description);
+ Assert.Equal(["dev", "staging", "prod"], result.Enum);
+ Assert.Equal(["Development", "Staging", "Production"], result.EnumNames);
+ Assert.Equal("staging", result.Default);
+ Assert.Contains("\"enumNames\":[\"Development\",\"Staging\",\"Production\"]", json);
+ }
+
+ [Fact]
+ public void Enum_WithEnumNames_Deserializes_As_EnumSchema()
+ {
+ // Arrange - JSON with enumNames should deserialize as (deprecated) EnumSchema
+ string json = """
+ {
+ "type": "string",
+ "title": "Environment",
+ "description": "Deployment environment",
+ "enum": ["dev", "staging", "prod"],
+ "enumNames": ["Development", "Staging", "Production"],
+ "default": "staging"
+ }
+ """;
+ // Act
+ var deserialized = JsonSerializer.Deserialize(json, McpJsonUtilities.DefaultOptions);
+
+ // Assert
+ Assert.NotNull(deserialized);
+ var result = Assert.IsType(deserialized);
+ Assert.Equal("string", result.Type);
+ Assert.Equal("Environment", result.Title);
+ Assert.Equal("Deployment environment", result.Description);
+ Assert.Equal(["dev", "staging", "prod"], result.Enum);
+ Assert.Equal(["Development", "Staging", "Production"], result.EnumNames);
+ Assert.Equal("staging", result.Default);
+ }
+#pragma warning restore MCP9001
}
diff --git a/tests/ModelContextProtocol.Tests/Protocol/PrimitiveSchemaDefinitionTests.cs b/tests/ModelContextProtocol.Tests/Protocol/PrimitiveSchemaDefinitionTests.cs
index 1e577f965..fc83ee5ed 100644
--- a/tests/ModelContextProtocol.Tests/Protocol/PrimitiveSchemaDefinitionTests.cs
+++ b/tests/ModelContextProtocol.Tests/Protocol/PrimitiveSchemaDefinitionTests.cs
@@ -1,74 +1,66 @@
using ModelContextProtocol.Protocol;
using System.Text.Json;
-#pragma warning disable CS0618 // Type or member is obsolete
-
namespace ModelContextProtocol.Tests.Protocol;
public static class PrimitiveSchemaDefinitionTests
{
[Fact]
- public static void StringSchema_UnknownArrayProperty_IsIgnored()
+ public static void StringSchema_UnknownProperties_AreIgnored()
{
- // This test verifies that the PrimitiveSchemaDefinition.Converter properly skips unknown properties
- // even when they contain complex structures like arrays or objects.
- //
- // In this unexpected JSON, "unknownArray" appears inside a string schema (where it doesn't belong).
- // The converter should gracefully ignore this unknown property and successfully deserialize
- // the rest of the schema.
-
- const string jsonWithUnknownArray = """
+ const string json = """
{
"type": "string",
- "title": "Test String",
- "minLength": 1,
- "maxLength": 100,
- "unknownArray": [
- {
- "nested": "value"
- },
- {
- "another": "object"
- }
- ]
+ "title": "Test",
+
+ "unknownNull": null,
+ "unknownEmptyObject": {},
+ "unknownObject": {"a": 1},
+ "unknownEmptyArray": [],
+ "unknownArray": [1, 2, 3],
+ "unknownNestedObject": {"b": {"c": "d", "e": ["f"]}},
+
+ "minLength": 5,
+ "maxLength": 50,
+ "format": "email"
}
""";
var result = JsonSerializer.Deserialize(
- jsonWithUnknownArray,
+ json,
McpJsonUtilities.DefaultOptions);
Assert.NotNull(result);
var stringSchema = Assert.IsType(result);
Assert.Equal("string", stringSchema.Type);
- Assert.Equal("Test String", stringSchema.Title);
- Assert.Equal(1, stringSchema.MinLength);
- Assert.Equal(100, stringSchema.MaxLength);
+ Assert.Equal("Test", stringSchema.Title);
+ Assert.Equal(5, stringSchema.MinLength);
+ Assert.Equal(50, stringSchema.MaxLength);
+ Assert.Equal("email", stringSchema.Format);
}
[Fact]
- public static void NumberSchema_UnknownObjectProperty_IsIgnored()
+ public static void NumberSchema_UnknownProperties_AreIgnored()
{
- // Test that unknown properties with nested objects are properly skipped
-
- const string jsonWithUnknownObject = """
+ const string json = """
{
"type": "number",
"description": "Test Number",
+
+ "unknownNull": null,
+ "unknownEmptyObject": {},
+ "unknownObject": {"a": 1},
+ "unknownEmptyArray": [],
+ "unknownArray": [1, 2, 3],
+ "unknownNestedObject": {"b": {"c": "d", "e": ["f"]}},
+
"minimum": 0,
- "maximum": 1000,
- "unknownObject": {
- "deeply": {
- "nested": {
- "value": "should be ignored"
- }
- }
- }
+ "maximum": 1000
}
""";
var result = JsonSerializer.Deserialize(
- jsonWithUnknownObject,
+ json,
McpJsonUtilities.DefaultOptions);
Assert.NotNull(result);
@@ -80,26 +72,26 @@ public static void NumberSchema_UnknownObjectProperty_IsIgnored()
}
[Fact]
- public static void BooleanSchema_UnknownMixedProperties_AreIgnored()
+ public static void BooleanSchema_UnknownProperties_AreIgnored()
{
- // Test multiple unknown properties with different types
-
- const string jsonWithMixedUnknown = """
+ const string json = """
{
"type": "boolean",
"title": "Test Boolean",
- "unknownString": "value",
- "unknownNumber": 42,
- "unknownArray": [1, 2, 3],
- "unknownObject": {"key": "value"},
- "unknownBool": true,
+
"unknownNull": null,
+ "unknownEmptyObject": {},
+ "unknownObject": {"a": 1},
+ "unknownEmptyArray": [],
+ "unknownArray": [1, 2, 3],
+ "unknownNestedObject": {"b": {"c": "d", "e": ["f"]}},
+
"default": false
}
""";
var result = JsonSerializer.Deserialize(
- jsonWithMixedUnknown,
+ json,
McpJsonUtilities.DefaultOptions);
Assert.NotNull(result);
@@ -110,265 +102,194 @@ public static void BooleanSchema_UnknownMixedProperties_AreIgnored()
}
[Fact]
- public static void EnumSchema_UnknownNestedArrays_AreIgnored()
+ public static void UntitledSingleSelectEnumSchema_UnknownProperties_AreIgnored()
{
- // Test complex unknown properties with arrays of objects
-
- const string jsonWithNestedArrays = """
+ const string json = """
{
"type": "string",
"enum": ["option1", "option2", "option3"],
- "enumNames": ["Name1", "Name2", "Name3"],
- "unknownComplex": [
- {
- "nested": [
- {"deep": "value1"},
- {"deep": "value2"}
- ]
- },
- {
- "nested": [
- {"deep": "value3"}
- ]
- }
- ],
+
+ "unknownNull": null,
+ "unknownEmptyObject": {},
+ "unknownObject": {"a": 1},
+ "unknownEmptyArray": [],
+ "unknownArray": [1, 2, 3],
+ "unknownNestedObject": {"b": {"c": "d", "e": ["f"]}},
+
"default": "option1"
}
""";
var result = JsonSerializer.Deserialize(
- jsonWithNestedArrays,
+ json,
McpJsonUtilities.DefaultOptions);
Assert.NotNull(result);
- var enumSchema = Assert.IsType(result);
+ var enumSchema = Assert.IsType(result);
Assert.Equal("string", enumSchema.Type);
Assert.Equal(3, enumSchema.Enum.Count);
Assert.Contains("option1", enumSchema.Enum);
Assert.Contains("option2", enumSchema.Enum);
Assert.Contains("option3", enumSchema.Enum);
- Assert.Equal(3, enumSchema.EnumNames!.Count);
- Assert.Contains("Name1", enumSchema.EnumNames);
- Assert.Contains("Name2", enumSchema.EnumNames);
- Assert.Contains("Name3", enumSchema.EnumNames);
Assert.Equal("option1", enumSchema.Default);
}
[Fact]
- public static void StringSchema_MultipleUnknownProperties_AllIgnored()
+ public static void TitledSingleSelectEnumSchema_UnknownProperties_AreIgnored()
{
- // Test that multiple unknown properties are all properly skipped
-
- const string jsonWithMultipleUnknown = """
+ const string json = """
{
"type": "string",
- "title": "Test",
- "unknownOne": {"a": 1},
- "minLength": 5,
- "unknownTwo": [1, 2, 3],
- "maxLength": 50,
- "unknownThree": {"b": {"c": "d"}},
- "format": "email"
- }
- """;
-
- var result = JsonSerializer.Deserialize(
- jsonWithMultipleUnknown,
- McpJsonUtilities.DefaultOptions);
-
- Assert.NotNull(result);
- var stringSchema = Assert.IsType(result);
- Assert.Equal("string", stringSchema.Type);
- Assert.Equal("Test", stringSchema.Title);
- Assert.Equal(5, stringSchema.MinLength);
- Assert.Equal(50, stringSchema.MaxLength);
- Assert.Equal("email", stringSchema.Format);
- }
+ "oneOf": [
+ {"const": "option1", "title": "Option 1"},
+ {"const": "option2", "title": "Option 2"}
+ ],
- [Fact]
- public static void IntegerSchema_UnknownArrayOfArrays_IsIgnored()
- {
- // Test deeply nested array structures in unknown properties
+ "unknownNull": null,
+ "unknownEmptyObject": {},
+ "unknownObject": {"a": 1},
+ "unknownEmptyArray": [],
+ "unknownArray": [1, 2, 3],
+ "unknownNestedObject": {"b": {"c": "d", "e": ["f"]}},
- const string jsonWithArrayOfArrays = """
- {
- "type": "integer",
- "minimum": 1,
- "maximum": 100,
- "unknownNested": [
- [
- [1, 2, 3],
- [4, 5, 6]
- ],
- [
- [7, 8, 9]
- ]
- ]
+ "default": "option2"
}
""";
var result = JsonSerializer.Deserialize(
- jsonWithArrayOfArrays,
+ json,
McpJsonUtilities.DefaultOptions);
-
Assert.NotNull(result);
- var numberSchema = Assert.IsType(result);
- Assert.Equal("integer", numberSchema.Type);
- Assert.Equal(1, numberSchema.Minimum);
- Assert.Equal(100, numberSchema.Maximum);
+ var enumSchema = Assert.IsType(result);
+ Assert.Equal("string", enumSchema.Type);
+ Assert.Equal(2, enumSchema.OneOf.Count);
+ Assert.Contains(enumSchema.OneOf, option => option.Const == "option1" && option.Title == "Option 1");
+ Assert.Contains(enumSchema.OneOf, option => option.Const == "option2" && option.Title == "Option 2");
+ Assert.Equal("option2", enumSchema.Default);
}
[Fact]
- public static void StringSchema_EmptyUnknownArray_IsIgnored()
+ public static void UntitledMultiSelectEnumSchema_UnknownProperties_AreIgnored()
{
- // Test empty arrays in unknown properties
-
- const string jsonWithEmptyArray = """
+ const string json = """
{
- "type": "string",
- "description": "Test",
- "unknownEmpty": [],
- "minLength": 0
- }
- """;
-
- var result = JsonSerializer.Deserialize(
- jsonWithEmptyArray,
- McpJsonUtilities.DefaultOptions);
-
- Assert.NotNull(result);
- var stringSchema = Assert.IsType(result);
- Assert.Equal("string", stringSchema.Type);
- Assert.Equal("Test", stringSchema.Description);
- Assert.Equal(0, stringSchema.MinLength);
- }
+ "type": "array",
+ "items": {
+ "unknownNull": null,
+ "unknownEmptyObject": {},
+ "unknownObject": {"a": 1},
+ "unknownEmptyArray": [],
+ "unknownArray": [1, 2, 3],
+ "unknownNestedObject": {"b": {"c": "d", "e": ["f"]}},
+
+ "enum": ["optionA", "optionB", "optionC"]
+ },
- [Fact]
- public static void NumberSchema_EmptyUnknownObject_IsIgnored()
- {
- // Test empty objects in unknown properties
+ "unknownNull": null,
+ "unknownEmptyObject": {},
+ "unknownObject": {"a": 1},
+ "unknownEmptyArray": [],
+ "unknownArray": [1, 2, 3],
+ "unknownNestedObject": {"b": {"c": "d", "e": ["f"]}},
- const string jsonWithEmptyObject = """
- {
- "type": "number",
- "title": "Test Number",
- "unknownEmpty": {},
- "minimum": 0.0,
- "maximum": 100.0
+ "default": ["optionA", "optionC"]
}
""";
var result = JsonSerializer.Deserialize(
- jsonWithEmptyObject,
+ json,
McpJsonUtilities.DefaultOptions);
-
Assert.NotNull(result);
- var numberSchema = Assert.IsType(result);
- Assert.Equal("number", numberSchema.Type);
- Assert.Equal("Test Number", numberSchema.Title);
- Assert.Equal(0.0, numberSchema.Minimum);
- Assert.Equal(100.0, numberSchema.Maximum);
+ var enumSchema = Assert.IsType(result);
+ Assert.Equal("array", enumSchema.Type);
+ Assert.Equal(3, enumSchema.Items.Enum.Count);
+ Assert.Contains("optionA", enumSchema.Items.Enum);
+ Assert.Contains("optionB", enumSchema.Items.Enum);
+ Assert.Contains("optionC", enumSchema.Items.Enum);
+ Assert.Equal(2, enumSchema.Default!.Count);
+ Assert.Contains("optionA", enumSchema.Default);
+ Assert.Contains("optionC", enumSchema.Default);
}
[Fact]
- public static void EnumSchema_UnknownPropertiesBetweenRequired_AreIgnored()
+ public static void TitledMultiSelectEnumSchema_UnknownProperties_AreIgnored()
{
- // Test unknown properties interspersed with required ones
-
- const string jsonWithInterspersedUnknown = """
+ const string json = """
{
- "unknownFirst": {"x": 1},
- "type": "string",
- "unknownSecond": [1, 2],
- "enum": ["a", "b"],
- "unknownThird": {"nested": {"value": true}},
- "enumNames": ["Alpha", "Beta"]
- }
- """;
-
- var result = JsonSerializer.Deserialize(
- jsonWithInterspersedUnknown,
- McpJsonUtilities.DefaultOptions);
-
- Assert.NotNull(result);
- var enumSchema = Assert.IsType(result);
- Assert.Equal("string", enumSchema.Type);
- Assert.Equal(2, enumSchema.Enum.Count);
- Assert.Contains("a", enumSchema.Enum);
- Assert.Contains("b", enumSchema.Enum);
- Assert.Equal(2, enumSchema.EnumNames!.Count);
- Assert.Contains("Alpha", enumSchema.EnumNames);
- Assert.Contains("Beta", enumSchema.EnumNames);
- }
+ "type": "array",
+ "items": {
+ "unknownNull": null,
+ "unknownEmptyObject": {},
+ "unknownObject": {"a": 1},
+ "unknownEmptyArray": [],
+ "unknownArray": [1, 2, 3],
+ "unknownNestedObject": {"b": {"c": "d", "e": ["f"]}},
+
+ "anyOf": [
+ {"const": "optionX", "title": "Option X"},
+ {"const": "optionY", "title": "Option Y"},
+ {"const": "optionZ", "title": "Option Z"}
+ ]
+ },
- [Fact]
- public static void BooleanSchema_VeryDeeplyNestedUnknown_IsIgnored()
- {
- // Test very deeply nested structures in unknown properties
+ "unknownNull": null,
+ "unknownEmptyObject": {},
+ "unknownObject": {"a": 1},
+ "unknownEmptyArray": [],
+ "unknownArray": [1, 2, 3],
+ "unknownNestedObject": {"b": {"c": "d", "e": ["f"]}},
- const string jsonWithVeryDeepNesting = """
- {
- "type": "boolean",
- "unknownDeep": {
- "level1": {
- "level2": {
- "level3": {
- "level4": {
- "level5": {
- "value": "deep"
- }
- }
- }
- }
- }
- },
- "default": true
+ "default": ["optionX", "optionZ"]
}
""";
var result = JsonSerializer.Deserialize(
- jsonWithVeryDeepNesting,
+ json,
McpJsonUtilities.DefaultOptions);
-
Assert.NotNull(result);
- var boolSchema = Assert.IsType(result);
- Assert.Equal("boolean", boolSchema.Type);
- Assert.True(boolSchema.Default);
+ var enumSchema = Assert.IsType(result);
+ Assert.Equal("array", enumSchema.Type);
+ Assert.Equal(3, enumSchema.Items.AnyOf.Count);
+ Assert.Contains(enumSchema.Items.AnyOf, option => option.Const == "optionX" && option.Title == "Option X");
+ Assert.Contains(enumSchema.Items.AnyOf, option => option.Const == "optionY" && option.Title == "Option Y");
+ Assert.Contains(enumSchema.Items.AnyOf, option => option.Const == "optionZ" && option.Title == "Option Z");
+ Assert.Equal(2, enumSchema.Default!.Count);
+ Assert.Contains("optionX", enumSchema.Default);
+ Assert.Contains("optionZ", enumSchema.Default);
}
+#pragma warning disable MCP9001 // LegacyTitledEnumSchema is deprecated but supported for backward compatibility
[Fact]
- public static void EnumSchema_Deserialization_PreservesKnownProperties()
+ public static void LegacyTitledEnumSchema_UnknownProperties_AreIgnored()
{
- // Test deserialization of enum schema with all properties
-
- const string enumSchemaJson = """
+ const string json = """
{
"type": "string",
- "title": "Test Enum",
- "description": "A test enum schema",
- "enum": ["option1", "option2", "option3"],
- "enumNames": ["Name1", "Name2", "Name3"],
+ "enum": ["option1", "option2"],
+
+ "unknownNull": null,
+ "unknownEmptyObject": {},
+ "unknownObject": {"a": 1},
+ "unknownEmptyArray": [],
+ "unknownArray": [1, 2, 3],
+ "unknownNestedObject": {"b": {"c": "d", "e": ["f"]}},
+
+ "enumNames": ["Option 1", "Option 2"],
"default": "option2"
}
""";
- var deserialized = JsonSerializer.Deserialize(
- enumSchemaJson,
+ var result = JsonSerializer.Deserialize(
+ json,
McpJsonUtilities.DefaultOptions);
-
- Assert.NotNull(deserialized);
- var enumSchema = Assert.IsType(deserialized);
+ Assert.NotNull(result);
+ var enumSchema = Assert.IsType(result);
Assert.Equal("string", enumSchema.Type);
- Assert.Equal("Test Enum", enumSchema.Title);
- Assert.Equal("A test enum schema", enumSchema.Description);
- Assert.Equal(3, enumSchema.Enum.Count);
+ Assert.Equal(2, enumSchema.Enum.Count);
Assert.Contains("option1", enumSchema.Enum);
Assert.Contains("option2", enumSchema.Enum);
- Assert.Contains("option3", enumSchema.Enum);
- Assert.Equal(3, enumSchema.EnumNames!.Count);
- Assert.Contains("Name1", enumSchema.EnumNames);
- Assert.Contains("Name2", enumSchema.EnumNames);
- Assert.Contains("Name3", enumSchema.EnumNames);
- Assert.Equal("option2", enumSchema.Default);
+ Assert.Contains("Option 1", enumSchema.EnumNames!);
+ Assert.Contains("Option 2", enumSchema.EnumNames!);
}
+#pragma warning restore MCP9001
}
diff --git a/tests/ModelContextProtocol.Tests/Server/McpServerExtensionsTests.cs b/tests/ModelContextProtocol.Tests/Server/McpServerExtensionsTests.cs
deleted file mode 100644
index 1ba3a5145..000000000
--- a/tests/ModelContextProtocol.Tests/Server/McpServerExtensionsTests.cs
+++ /dev/null
@@ -1,217 +0,0 @@
-using Microsoft.Extensions.AI;
-using ModelContextProtocol.Protocol;
-using ModelContextProtocol.Server;
-using Moq;
-using System.Text.Json;
-
-namespace ModelContextProtocol.Tests.Server;
-
-#pragma warning disable CS0618 // Type or member is obsolete
-
-public class McpServerExtensionsTests
-{
- [Fact]
- public async Task SampleAsync_Request_Throws_When_Not_McpServer()
- {
- var server = new Mock(MockBehavior.Strict).Object;
-
- var ex = await Assert.ThrowsAsync(async () => await server.SampleAsync(
- new CreateMessageRequestParams
- {
- Messages = [new SamplingMessage { Role = Role.User, Content = [new TextContentBlock { Text = "hi" }] }],
- MaxTokens = 1000
- },
- TestContext.Current.CancellationToken));
- Assert.Contains("Prefer using 'McpServer.SampleAsync' instead", ex.Message);
- }
-
- [Fact]
- public async Task SampleAsync_Messages_Throws_When_Not_McpServer()
- {
- var server = new Mock(MockBehavior.Strict).Object;
-
- var ex = await Assert.ThrowsAsync(async () => await server.SampleAsync(
- [new ChatMessage(ChatRole.User, "hi")], cancellationToken: TestContext.Current.CancellationToken));
- Assert.Contains("Prefer using 'McpServer.SampleAsync' instead", ex.Message);
- }
-
- [Fact]
- public void AsSamplingChatClient_Throws_When_Not_McpServer()
- {
- var server = new Mock(MockBehavior.Strict).Object;
-
- var ex = Assert.Throws(server.AsSamplingChatClient);
- Assert.Contains("Prefer using 'McpServer.AsSamplingChatClient' instead", ex.Message);
- }
-
- [Fact]
- public void AsClientLoggerProvider_Throws_When_Not_McpServer()
- {
- var server = new Mock(MockBehavior.Strict).Object;
-
- var ex = Assert.Throws(server.AsClientLoggerProvider);
- Assert.Contains("Prefer using 'McpServer.AsClientLoggerProvider' instead", ex.Message);
- }
-
- [Fact]
- public async Task RequestRootsAsync_Throws_When_Not_McpServer()
- {
- var server = new Mock(MockBehavior.Strict).Object;
-
- var ex = await Assert.ThrowsAsync(async () => await server.RequestRootsAsync(
- new ListRootsRequestParams(), TestContext.Current.CancellationToken));
- Assert.Contains("Prefer using 'McpServer.RequestRootsAsync' instead", ex.Message);
- }
-
- [Fact]
- public async Task ElicitAsync_Throws_When_Not_McpServer()
- {
- var server = new Mock(MockBehavior.Strict).Object;
-
- var ex = await Assert.ThrowsAsync(async () => await server.ElicitAsync(
- new ElicitRequestParams { Message = "hello" }, TestContext.Current.CancellationToken));
- Assert.Contains("Prefer using 'McpServer.ElicitAsync' instead", ex.Message);
- }
-
- [Fact]
- public async Task SampleAsync_Request_Forwards_To_McpServer_SendRequestAsync()
- {
- var mockServer = new Mock { CallBase = true };
-
- var resultPayload = new CreateMessageResult
- {
- Content = [new TextContentBlock { Text = "resp" }],
- Model = "test-model",
- Role = Role.Assistant,
- StopReason = "endTurn",
- };
-
- mockServer
- .Setup(s => s.ClientCapabilities)
- .Returns(new ClientCapabilities() { Sampling = new() });
-
- mockServer
- .Setup(s => s.SendRequestAsync(It.IsAny(), It.IsAny()))
- .ReturnsAsync(new JsonRpcResponse
- {
- Result = JsonSerializer.SerializeToNode(resultPayload, McpJsonUtilities.DefaultOptions),
- });
-
- IMcpServer server = mockServer.Object;
-
- var result = await server.SampleAsync(new CreateMessageRequestParams
- {
- Messages = [new SamplingMessage { Role = Role.User, Content = [new TextContentBlock { Text = "hi" }] }],
- MaxTokens = 1000
- }, TestContext.Current.CancellationToken);
-
- Assert.Equal("test-model", result.Model);
- Assert.Equal(Role.Assistant, result.Role);
- Assert.Equal("resp", Assert.IsType(result.Content[0]).Text);
- mockServer.Verify(s => s.SendRequestAsync(It.IsAny(), It.IsAny()), Times.Once);
- }
-
- [Fact]
- public async Task SampleAsync_Messages_Forwards_To_McpServer_SendRequestAsync()
- {
- var mockServer = new Mock { CallBase = true };
-
- var resultPayload = new CreateMessageResult
- {
- Content = [new TextContentBlock { Text = "resp" }],
- Model = "test-model",
- Role = Role.Assistant,
- StopReason = "endTurn",
- };
-
- const int CustomMaxSamplingOutputTokens = 500;
-
- mockServer
- .Setup(s => s.ClientCapabilities)
- .Returns(new ClientCapabilities() { Sampling = new() });
-
- mockServer
- .Setup(s => s.ServerOptions)
- .Returns(new McpServerOptions { MaxSamplingOutputTokens = CustomMaxSamplingOutputTokens });
-
- CreateMessageRequestParams? capturedRequest = null;
- mockServer
- .Setup(s => s.SendRequestAsync(It.IsAny(), It.IsAny()))
- .Callback((request, _) =>
- {
- capturedRequest = JsonSerializer.Deserialize(
- request.Params ?? throw new InvalidOperationException(),
- McpJsonUtilities.DefaultOptions);
- })
- .ReturnsAsync(new JsonRpcResponse
- {
- Result = JsonSerializer.SerializeToNode(resultPayload, McpJsonUtilities.DefaultOptions),
- });
-
- IMcpServer server = mockServer.Object;
-
- var chatResponse = await server.SampleAsync([new ChatMessage(ChatRole.User, "hi")], cancellationToken: TestContext.Current.CancellationToken);
-
- Assert.Equal("test-model", chatResponse.ModelId);
- var last = chatResponse.Messages.Last();
- Assert.Equal(ChatRole.Assistant, last.Role);
- Assert.Equal("resp", last.Text);
- mockServer.Verify(s => s.SendRequestAsync(It.IsAny(), It.IsAny()), Times.Once);
-
- // Verify that the default value was used
- Assert.NotNull(capturedRequest);
- Assert.Equal(CustomMaxSamplingOutputTokens, capturedRequest.MaxTokens);
- }
-
- [Fact]
- public async Task RequestRootsAsync_Forwards_To_McpServer_SendRequestAsync()
- {
- var mockServer = new Mock { CallBase = true };
-
- var resultPayload = new ListRootsResult { Roots = [new Root { Uri = "root://a" }] };
-
- mockServer
- .Setup(s => s.ClientCapabilities)
- .Returns(new ClientCapabilities() { Roots = new() });
-
- mockServer
- .Setup(s => s.SendRequestAsync(It.IsAny(), It.IsAny()))
- .ReturnsAsync(new JsonRpcResponse
- {
- Result = JsonSerializer.SerializeToNode(resultPayload, McpJsonUtilities.DefaultOptions),
- });
-
- IMcpServer server = mockServer.Object;
-
- var result = await server.RequestRootsAsync(new ListRootsRequestParams(), TestContext.Current.CancellationToken);
-
- Assert.Equal("root://a", result.Roots[0].Uri);
- mockServer.Verify(s => s.SendRequestAsync(It.IsAny(), It.IsAny()), Times.Once);
- }
-
- [Fact]
- public async Task ElicitAsync_Forwards_To_McpServer_SendRequestAsync()
- {
- var mockServer = new Mock { CallBase = true };
-
- var resultPayload = new ElicitResult { Action = "accept" };
-
- mockServer
- .Setup(s => s.ClientCapabilities)
- .Returns(new ClientCapabilities() { Elicitation = new() });
-
- mockServer
- .Setup(s => s.SendRequestAsync(It.IsAny(), It.IsAny()))
- .ReturnsAsync(new JsonRpcResponse
- {
- Result = JsonSerializer.SerializeToNode(resultPayload, McpJsonUtilities.DefaultOptions),
- });
-
- IMcpServer server = mockServer.Object;
-
- var result = await server.ElicitAsync(new ElicitRequestParams { Message = "hi" }, TestContext.Current.CancellationToken);
-
- Assert.Equal("accept", result.Action);
- mockServer.Verify(s => s.SendRequestAsync(It.IsAny(), It.IsAny()), Times.Once);
- }
-}