AI seeded comments from robch and then edited by stephentoub (#238)

* AI generated `///` comment updates

* Fix compilation issues

* Fix incorrect XML comment structure

* Update all Client comments

* Update all Configuration comments

* Update all Messages comments

* Update all Transport comments

* Update all Types comments

* Update all Server comments

* Undo some commenting

---------

Co-authored-by: Stephen Toub <stoub@microsoft.com>
This commit is contained in:
Rob Chambers 2025-04-09 22:30:44 -07:00 • committed by GitHub
commit 77871c5aac
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
144 changed files with 4017 additions and 1066 deletions

View file

@ -1,6 +1,3 @@
using ModelContextProtocol.Utils;
using System.Runtime.InteropServices;
namespace System.IO;
internal static class TextWriterExtensions

View file

@ -1,4 +1,4 @@
using System.Diagnostics.CodeAnalysis;
using System.Diagnostics.CodeAnalysis;
using System.Runtime.CompilerServices;
namespace ModelContextProtocol.Utils;
@ -38,4 +38,4 @@ internal static class Throw
[DoesNotReturn]
private static void ThrowArgumentNullException(string? parameterName) => throw new ArgumentNullException(parameterName);
}
}

View file

@ -18,7 +18,7 @@ using System.Security.Cryptography;
namespace Microsoft.AspNetCore.Builder;
/// <summary>
/// Extension methods for <see cref="IEndpointRouteBuilder"/> to add MCP endpoints.
/// Provides extension methods for <see cref="IEndpointRouteBuilder"/> to add MCP endpoints.
/// </summary>
public static class McpEndpointRouteBuilderExtensions
{

View file

@ -1,4 +1,4 @@
using Microsoft.Extensions.AI;
using Microsoft.Extensions.AI;
using ModelContextProtocol.Protocol.Types;
using ModelContextProtocol.Utils;
using ModelContextProtocol.Utils.Json;
@ -7,12 +7,24 @@ using System.Text.Json;
namespace ModelContextProtocol;
/// <summary>Provides helpers for conversions related to <see cref="AIContent"/>.</summary>
/// <summary>
/// Provides extension methods for converting between Model Context Protocol (MCP) types and Microsoft.Extensions.AI types.
/// </summary>
/// <remarks>
/// This class serves as an adapter layer between Model Context Protocol (MCP) types and the <see cref="AIContent"/> model types
/// from the Microsoft.Extensions.AI namespace.
/// </remarks>
public static class AIContentExtensions
{
/// <summary>Creates a <see cref="ChatMessage"/> from a <see cref="PromptMessage"/>.</summary>
/// <param name="promptMessage">The message to convert.</param>
/// <returns>The created <see cref="ChatMessage"/>.</returns>
/// <summary>
/// Converts a <see cref="PromptMessage"/> to a <see cref="ChatMessage"/> object.
/// </summary>
/// <param name="promptMessage">The prompt message to convert.</param>
/// <returns>A <see cref="ChatMessage"/> object created from the prompt message.</returns>
/// <remarks>
/// This method transforms a protocol-specific <see cref="PromptMessage"/> from the Model Context Protocol
/// into a standard <see cref="ChatMessage"/> object that can be used with AI client libraries.
/// </remarks>
public static ChatMessage ToChatMessage(this PromptMessage promptMessage)
{
Throw.IfNull(promptMessage);
@ -25,9 +37,15 @@ public static class AIContentExtensions
};
}
/// <summary>Creates <see cref="ChatMessage"/>s from a <see cref="GetPromptResult"/>.</summary>
/// <param name="promptResult">The messages to convert.</param>
/// <returns>The created <see cref="ChatMessage"/>.</returns>
/// <summary>
/// Converts a <see cref="GetPromptResult"/> to a list of <see cref="ChatMessage"/> objects.
/// </summary>
/// <param name="promptResult">The prompt result containing messages to convert.</param>
/// <returns>A list of <see cref="ChatMessage"/> objects created from the prompt messages.</returns>
/// <remarks>
/// This method transforms protocol-specific <see cref="PromptMessage"/> objects from a Model Context Protocol
/// prompt result into standard <see cref="ChatMessage"/> objects that can be used with AI client libraries.
/// </remarks>
public static IList<ChatMessage> ToChatMessages(this GetPromptResult promptResult)
{
Throw.IfNull(promptResult);
@ -35,9 +53,16 @@ public static class AIContentExtensions
return promptResult.Messages.Select(m => m.ToChatMessage()).ToList();
}
/// <summary>Gets <see cref="PromptMessage"/> instances for the specified <see cref="ChatMessage"/>.</summary>
/// <param name="chatMessage">The message for which to extract its contents as <see cref="PromptMessage"/> instances.</param>
/// <returns>The converted content.</returns>
/// <summary>
/// Converts a <see cref="ChatMessage"/> to a list of <see cref="PromptMessage"/> objects.
/// </summary>
/// <param name="chatMessage">The chat message to convert.</param>
/// <returns>A list of <see cref="PromptMessage"/> objects created from the chat message's contents.</returns>
/// <remarks>
/// This method transforms standard <see cref="ChatMessage"/> objects used with AI client libraries into
/// protocol-specific <see cref="PromptMessage"/> objects for the Model Context Protocol system.
/// Only representable content items are processed.
/// </remarks>
public static IList<PromptMessage> ToPromptMessages(this ChatMessage chatMessage)
{
Throw.IfNull(chatMessage);
@ -59,6 +84,10 @@ public static class AIContentExtensions
/// <summary>Creates a new <see cref="AIContent"/> from the content of a <see cref="Content"/>.</summary>
/// <param name="content">The <see cref="Content"/> to convert.</param>
/// <returns>The created <see cref="AIContent"/>.</returns>
/// <remarks>
/// This method converts Model Context Protocol content types to the equivalent Microsoft.Extensions.AI
/// content types, enabling seamless integration between the protocol and AI client libraries.
/// </remarks>
public static AIContent ToAIContent(this Content content)
{
Throw.IfNull(content);
@ -85,6 +114,10 @@ public static class AIContentExtensions
/// <summary>Creates a new <see cref="AIContent"/> from the content of a <see cref="ResourceContents"/>.</summary>
/// <param name="content">The <see cref="ResourceContents"/> to convert.</param>
/// <returns>The created <see cref="AIContent"/>.</returns>
/// <remarks>
/// This method converts Model Context Protocol resource types to the equivalent Microsoft.Extensions.AI
/// content types, enabling seamless integration between the protocol and AI client libraries.
/// </remarks>
public static AIContent ToAIContent(this ResourceContents content)
{
Throw.IfNull(content);
@ -105,6 +138,17 @@ public static class AIContentExtensions
/// <summary>Creates a list of <see cref="AIContent"/> from a sequence of <see cref="Content"/>.</summary>
/// <param name="contents">The <see cref="Content"/> instances to convert.</param>
/// <returns>The created <see cref="AIContent"/> instances.</returns>
/// <remarks>
/// <para>
/// This method converts a collection of Model Context Protocol content objects into a collection of
/// Microsoft.Extensions.AI content objects. It's useful when working with multiple content items, such as
/// when processing the contents of a message or response.
/// </para>
/// <para>
/// Each <see cref="Content"/> object is converted using <see cref="ToAIContent(Content)"/>,
/// preserving the type-specific conversion logic for text, images, audio, and resources.
/// </para>
/// </remarks>
public static IList<AIContent> ToAIContents(this IEnumerable<Content> contents)
{
Throw.IfNull(contents);
@ -114,7 +158,19 @@ public static class AIContentExtensions
/// <summary>Creates a list of <see cref="AIContent"/> from a sequence of <see cref="ResourceContents"/>.</summary>
/// <param name="contents">The <see cref="ResourceContents"/> instances to convert.</param>
/// <returns>The created <see cref="AIContent"/> instances.</returns>
/// <returns>A list of <see cref="AIContent"/> objects created from the resource contents.</returns>
/// <remarks>
/// <para>
/// This method converts a collection of Model Context Protocol resource objects into a collection of
/// Microsoft.Extensions.AI content objects. It's useful when working with multiple resources, such as
/// when processing the contents of a <see cref="ReadResourceResult"/>.
/// </para>
/// <para>
/// Each <see cref="ResourceContents"/> object is converted using <see cref="ToAIContent(ResourceContents)"/>,
/// preserving the type-specific conversion logic: text resources become <see cref="TextContent"/> objects and
/// binary resources become <see cref="DataContent"/> objects.
/// </para>
/// </remarks>
public static IList<AIContent> ToAIContents(this IEnumerable<ResourceContents> contents)
{
Throw.IfNull(contents);

View file

@ -1,28 +1,47 @@
using ModelContextProtocol.Protocol.Types;
using ModelContextProtocol.Protocol.Types;
namespace ModelContextProtocol.Client;
/// <summary>
/// Represents an instance of an MCP client connecting to a specific server.
/// Represents an instance of a Model Context Protocol (MCP) client that connects to and communicates with an MCP server.
/// </summary>
public interface IMcpClient : IMcpEndpoint
{
/// <summary>
/// Gets the capabilities supported by the connected server.
/// </summary>
/// <exception cref="InvalidOperationException">The client is not connected.</exception>
ServerCapabilities ServerCapabilities { get; }
/// <summary>
/// Gets the implementation information of the connected server.
/// </summary>
/// <remarks>
/// <para>
/// This property provides identification details about the connected server, including its name and version.
/// It is populated during the initialization handshake and is available after a successful connection.
/// </para>
/// <para>
/// This information can be useful for logging, debugging, compatibility checks, and displaying server
/// information to users.
/// </para>
/// </remarks>
/// <exception cref="InvalidOperationException">The client is not connected.</exception>
Implementation ServerInfo { get; }
/// <summary>
/// Gets any instructions describing how to use the connected server and its features.
/// </summary>
/// <remarks>
/// <para>
/// This property contains instructions provided by the server during initialization that explain
/// how to effectively use its capabilities. These instructions can include details about available
/// tools, expected input formats, limitations, or any other helpful information.
/// </para>
/// <para>
/// This can be used by clients to improve an LLM's understanding of available tools, prompts, and resources.
/// It can be thought of like a "hint" to the model and may be added to a system prompt.
/// </para>
/// </remarks>
string? ServerInstructions { get; }
}

View file

@ -1,4 +1,4 @@
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Logging;
using ModelContextProtocol.Logging;
using ModelContextProtocol.Protocol.Messages;
using ModelContextProtocol.Protocol.Transport;
@ -96,6 +96,9 @@ internal sealed class McpClient : McpEndpoint, IMcpClient
/// <inheritdoc/>
public override string EndpointName { get; }
/// <summary>
/// Asynchronously connects to an MCP server, establishes the transport connection, and completes the initialization handshake.
/// </summary>
public async Task ConnectAsync(CancellationToken cancellationToken = default)
{
_connectCts = CancellationTokenSource.CreateLinkedTokenSource(cancellationToken);
@ -185,4 +188,4 @@ internal sealed class McpClient : McpEndpoint, IMcpClient
}
}
}
}
}

View file

@ -1,4 +1,4 @@
using Microsoft.Extensions.AI;
using Microsoft.Extensions.AI;
using Microsoft.Extensions.Logging;
using ModelContextProtocol.Protocol.Messages;
using ModelContextProtocol.Protocol.Types;
@ -10,15 +10,37 @@ using System.Text.Json;
namespace ModelContextProtocol.Client;
/// <summary>Provides extension methods for interacting with an <see cref="IMcpClient"/>.</summary>
/// <summary>
/// Provides extension methods for interacting with an <see cref="IMcpClient"/>.
/// </summary>
/// <remarks>
/// <para>
/// 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.
/// </para>
/// </remarks>
public static class McpClientExtensions
{
/// <summary>
/// Sends a ping request to verify server connectivity.
/// </summary>
/// <param name="client">The client.</param>
/// <param name="client">The client instance used to communicate with the MCP server.</param>
/// <param name="cancellationToken">The <see cref="CancellationToken"/> to monitor for cancellation requests. The default is <see cref="CancellationToken.None"/>.</param>
/// <returns>A task that completes when the ping is successful.</returns>
/// <remarks>
/// <para>
/// This method is used to check if the MCP server is online and responding to requests.
/// It can be useful for health checking, ensuring the connection is established, or verifying
/// that the client has proper authorization to communicate with the server.
/// </para>
/// <para>
/// The ping operation is lightweight and does not require any parameters. A successful completion
/// of the task indicates that the server is operational and accessible.
/// </para>
/// </remarks>
/// <exception cref="ArgumentNullException"><paramref name="client"/> is <see langword="null"/>.</exception>
/// <exception cref="McpException">Thrown when the server cannot be reached or returns an error response.</exception>
public static Task PingAsync(this IMcpClient client, CancellationToken cancellationToken = default)
{
Throw.IfNull(client);
@ -34,10 +56,42 @@ public static class McpClientExtensions
/// <summary>
/// Retrieves a list of available tools from the server.
/// </summary>
/// <param name="client">The client.</param>
/// <param name="serializerOptions">The serializer options governing tool parameter serialization.</param>
/// <param name="client">The client instance used to communicate with the MCP server.</param>
/// <param name="serializerOptions">The serializer options governing tool parameter serialization. If null, the default options will be used.</param>
/// <param name="cancellationToken">The <see cref="CancellationToken"/> to monitor for cancellation requests. The default is <see cref="CancellationToken.None"/>.</param>
/// <returns>A list of all available tools.</returns>
/// <returns>A list of all available tools as <see cref="McpClientTool"/> instances.</returns>
/// <remarks>
/// <para>
/// This method fetches all available tools from the MCP server and returns them as a complete list.
/// It automatically handles pagination with cursors if the server responds with only a portion per request.
/// </para>
/// <para>
/// For servers with a large number of tools and that responds with paginated responses, consider using
/// <see cref="EnumerateToolsAsync"/> instead, as it streams tools as they arrive rather than loading them all at once.
/// </para>
/// <para>
/// The serializer options provided are flowed to each <see cref="McpClientTool"/> and will be used
/// when invoking tools in order to serialize any parameters.
/// </para>
/// </remarks>
/// <example>
/// <code>
/// // Get all tools available on the server
/// var tools = await mcpClient.ListToolsAsync();
///
/// // Use tools with an AI client
/// ChatOptions chatOptions = new()
/// {
/// Tools = [.. tools]
/// };
///
/// await foreach (var update in chatClient.GetStreamingResponseAsync(userMessage, chatOptions))
/// {
/// Console.Write(update);
/// }
/// </code>
/// </example>
/// <exception cref="ArgumentNullException"><paramref name="client"/> is <see langword="null"/>.</exception>
public static async Task<IList<McpClientTool>> ListToolsAsync(
this IMcpClient client,
JsonSerializerOptions? serializerOptions = null,
@ -75,14 +129,35 @@ public static class McpClientExtensions
/// <summary>
/// Creates an enumerable for asynchronously enumerating all available tools from the server.
/// </summary>
/// <param name="client">The client.</param>
/// <param name="serializerOptions">The serializer options governing tool parameter serialization.</param>
/// <param name="client">The client instance used to communicate with the MCP server.</param>
/// <param name="serializerOptions">The serializer options governing tool parameter serialization. If null, the default options will be used.</param>
/// <param name="cancellationToken">The <see cref="CancellationToken"/> to monitor for cancellation requests. The default is <see cref="CancellationToken.None"/>.</param>
/// <returns>An asynchronous sequence of all available tools.</returns>
/// <returns>An asynchronous sequence of all available tools as <see cref="McpClientTool"/> instances.</returns>
/// <remarks>
/// <para>
/// This method uses asynchronous enumeration to retrieve tools from the server, which allows processing tools
/// as they arrive rather than waiting for all tools to be retrieved. The method automatically handles pagination
/// with cursors if the server responds with tools split across multiple responses.
/// </para>
/// <para>
/// The serializer options provided are flowed to each <see cref="McpClientTool"/> and will be used
/// when invoking tools in order to serialize any parameters.
/// </para>
/// <para>
/// Every iteration through the returned <see cref="IAsyncEnumerable{McpClientTool}"/>
/// will result in requerying the server and yielding the sequence of available tools.
/// </para>
/// </remarks>
/// <example>
/// <code>
/// // Enumerate all tools available on the server
/// await foreach (var tool in client.EnumerateToolsAsync())
/// {
/// Console.WriteLine($"Tool: {tool.Name}");
/// }
/// </code>
/// </example>
/// <exception cref="ArgumentNullException"><paramref name="client"/> is <see langword="null"/>.</exception>
public static async IAsyncEnumerable<McpClientTool> EnumerateToolsAsync(
this IMcpClient client,
JsonSerializerOptions? serializerOptions = null,
@ -116,9 +191,20 @@ public static class McpClientExtensions
/// <summary>
/// Retrieves a list of available prompts from the server.
/// </summary>
/// <param name="client">The client.</param>
/// <param name="client">The client instance used to communicate with the MCP server.</param>
/// <param name="cancellationToken">The <see cref="CancellationToken"/> to monitor for cancellation requests. The default is <see cref="CancellationToken.None"/>.</param>
/// <returns>A list of all available prompts.</returns>
/// <returns>A list of all available prompts as <see cref="McpClientPrompt"/> instances.</returns>
/// <remarks>
/// <para>
/// This method fetches all available prompts from the MCP server and returns them as a complete list.
/// It automatically handles pagination with cursors if the server responds with only a portion per request.
/// </para>
/// <para>
/// For servers with a large number of prompts and that responds with paginated responses, consider using
/// <see cref="EnumeratePromptsAsync"/> instead, as it streams prompts as they arrive rather than loading them all at once.
/// </para>
/// </remarks>
/// <exception cref="ArgumentNullException"><paramref name="client"/> is <see langword="null"/>.</exception>
public static async Task<IList<McpClientPrompt>> ListPromptsAsync(
this IMcpClient client, CancellationToken cancellationToken = default)
{
@ -151,14 +237,31 @@ public static class McpClientExtensions
/// <summary>
/// Creates an enumerable for asynchronously enumerating all available prompts from the server.
/// </summary>
/// <param name="client">The client.</param>
/// <param name="client">The client instance used to communicate with the MCP server.</param>
/// <param name="cancellationToken">The <see cref="CancellationToken"/> to monitor for cancellation requests. The default is <see cref="CancellationToken.None"/>.</param>
/// <returns>An asynchronous sequence of all available prompts.</returns>
/// <returns>An asynchronous sequence of all available prompts as <see cref="McpClientPrompt"/> instances.</returns>
/// <remarks>
/// Every iteration through the returned <see cref="IAsyncEnumerable{Prompt}"/>
/// <para>
/// This method uses asynchronous enumeration to retrieve prompts from the server, which allows processing prompts
/// as they arrive rather than waiting for all prompts to be retrieved. The method automatically handles pagination
/// with cursors if the server responds with prompts split across multiple responses.
/// </para>
/// <para>
/// Every iteration through the returned <see cref="IAsyncEnumerable{McpClientPrompt}"/>
/// will result in requerying the server and yielding the sequence of available prompts.
/// </para>
/// </remarks>
public static async IAsyncEnumerable<Prompt> EnumeratePromptsAsync(
/// <example>
/// <code>
/// // Enumerate all prompts available on the server
/// await foreach (var prompt in client.EnumeratePromptsAsync())
/// {
/// Console.WriteLine($"Prompt: {prompt.Name}");
/// }
/// </code>
/// </example>
/// <exception cref="ArgumentNullException"><paramref name="client"/> is <see langword="null"/>.</exception>
public static async IAsyncEnumerable<McpClientPrompt> EnumeratePromptsAsync(
this IMcpClient client, [EnumeratorCancellation] CancellationToken cancellationToken = default)
{
Throw.IfNull(client);
@ -175,7 +278,7 @@ public static class McpClientExtensions
foreach (var prompt in promptResults.Prompts)
{
yield return prompt;
yield return new(client, prompt);
}
cursor = promptResults.NextCursor;
@ -184,14 +287,30 @@ public static class McpClientExtensions
}
/// <summary>
/// Retrieves a specific prompt with optional arguments.
/// Retrieves a specific prompt from the MCP server.
/// </summary>
/// <param name="client">The client.</param>
/// <param name="name">The name of the prompt to retrieve</param>
/// <param name="arguments">Optional arguments for the prompt</param>
/// <param name="client">The client instance used to communicate with the MCP server.</param>
/// <param name="name">The name of the prompt to retrieve.</param>
/// <param name="arguments">Optional arguments for the prompt. Keys are parameter names, and values are the argument values.</param>
/// <param name="serializerOptions">The serialization options governing argument serialization.</param>
/// <param name="cancellationToken">The <see cref="CancellationToken"/> to monitor for cancellation requests. The default is <see cref="CancellationToken.None"/>.</param>
/// <returns>A task containing the prompt's content and messages.</returns>
/// <returns>A task containing the prompt's result with content and messages.</returns>
/// <remarks>
/// <para>
/// This method sends a request to the MCP server to create the specified prompt with the provided arguments.
/// The server will process the arguments and return a prompt containing messages or other content.
/// </para>
/// <para>
/// Arguments are serialized into JSON and passed to the server, where they may be used to customize the
/// prompt's behavior or content. Each prompt may have different argument requirements.
/// </para>
/// <para>
/// The returned <see cref="GetPromptResult"/> contains a collection of <see cref="PromptMessage"/> objects,
/// which can be converted to <see cref="ChatMessage"/> objects using the <see cref="AIContentExtensions.ToChatMessages"/> method.
/// </para>
/// </remarks>
/// <exception cref="McpException">Thrown when the prompt does not exist, when required arguments are missing, or when the server encounters an error processing the prompt.</exception>
/// <exception cref="ArgumentNullException"><paramref name="client"/> is <see langword="null"/>.</exception>
public static Task<GetPromptResult> GetPromptAsync(
this IMcpClient client,
string name,
@ -201,6 +320,7 @@ public static class McpClientExtensions
{
Throw.IfNull(client);
Throw.IfNullOrWhiteSpace(name);
serializerOptions ??= McpJsonUtilities.DefaultOptions;
serializerOptions.MakeReadOnly();
@ -215,9 +335,20 @@ public static class McpClientExtensions
/// <summary>
/// Retrieves a list of available resource templates from the server.
/// </summary>
/// <param name="client">The client.</param>
/// <param name="client">The client instance used to communicate with the MCP server.</param>
/// <param name="cancellationToken">The <see cref="CancellationToken"/> to monitor for cancellation requests. The default is <see cref="CancellationToken.None"/>.</param>
/// <returns>A list of all available resource templates.</returns>
/// <returns>A list of all available resource templates as <see cref="ResourceTemplate"/> instances.</returns>
/// <remarks>
/// <para>
/// This method fetches all available resource templates from the MCP server and returns them as a complete list.
/// It automatically handles pagination with cursors if the server responds with only a portion per request.
/// </para>
/// <para>
/// For servers with a large number of resource templates and that responds with paginated responses, consider using
/// <see cref="EnumerateResourceTemplatesAsync"/> instead, as it streams templates as they arrive rather than loading them all at once.
/// </para>
/// </remarks>
/// <exception cref="ArgumentNullException"><paramref name="client"/> is <see langword="null"/>.</exception>
public static async Task<IList<ResourceTemplate>> ListResourceTemplatesAsync(
this IMcpClient client, CancellationToken cancellationToken = default)
{
@ -254,13 +385,30 @@ public static class McpClientExtensions
/// <summary>
/// Creates an enumerable for asynchronously enumerating all available resource templates from the server.
/// </summary>
/// <param name="client">The client.</param>
/// <param name="client">The client instance used to communicate with the MCP server.</param>
/// <param name="cancellationToken">The <see cref="CancellationToken"/> to monitor for cancellation requests. The default is <see cref="CancellationToken.None"/>.</param>
/// <returns>An asynchronous sequence of all available resource templates.</returns>
/// <returns>An asynchronous sequence of all available resource templates as <see cref="ResourceTemplate"/> instances.</returns>
/// <remarks>
/// <para>
/// This method uses asynchronous enumeration to retrieve resource templates from the server, which allows processing templates
/// as they arrive rather than waiting for all templates to be retrieved. The method automatically handles pagination
/// with cursors if the server responds with templates split across multiple responses.
/// </para>
/// <para>
/// Every iteration through the returned <see cref="IAsyncEnumerable{ResourceTemplate}"/>
/// will result in requerying the server and yielding the sequence of available resource templates.
/// </para>
/// </remarks>
/// <example>
/// <code>
/// // Enumerate all resource templates available on the server
/// await foreach (var template in client.EnumerateResourceTemplatesAsync())
/// {
/// Console.WriteLine($"Template: {template.Name}");
/// }
/// </code>
/// </example>
/// <exception cref="ArgumentNullException"><paramref name="client"/> is <see langword="null"/>.</exception>
public static async IAsyncEnumerable<ResourceTemplate> EnumerateResourceTemplatesAsync(
this IMcpClient client, [EnumeratorCancellation] CancellationToken cancellationToken = default)
{
@ -289,9 +437,32 @@ public static class McpClientExtensions
/// <summary>
/// Retrieves a list of available resources from the server.
/// </summary>
/// <param name="client">The client.</param>
/// <param name="client">The client instance used to communicate with the MCP server.</param>
/// <param name="cancellationToken">The <see cref="CancellationToken"/> to monitor for cancellation requests. The default is <see cref="CancellationToken.None"/>.</param>
/// <returns>A list of all available resources.</returns>
/// <returns>A list of all available resources as <see cref="Resource"/> instances.</returns>
/// <remarks>
/// <para>
/// This method fetches all available resources from the MCP server and returns them as a complete list.
/// It automatically handles pagination with cursors if the server responds with only a portion per request.
/// </para>
/// <para>
/// For servers with a large number of resources and that responds with paginated responses, consider using
/// <see cref="EnumerateResourcesAsync"/> instead, as it streams resources as they arrive rather than loading them all at once.
/// </para>
/// </remarks>
/// <example>
/// <code>
/// // Get all resources available on the server
/// var resources = await client.ListResourcesAsync();
///
/// // Display information about each resource
/// foreach (var resource in resources)
/// {
/// Console.WriteLine($"Resource URI: {resource.Uri}");
/// }
/// </code>
/// </example>
/// <exception cref="ArgumentNullException"><paramref name="client"/> is <see langword="null"/>.</exception>
public static async Task<IList<Resource>> ListResourcesAsync(
this IMcpClient client, CancellationToken cancellationToken = default)
{
@ -328,13 +499,30 @@ public static class McpClientExtensions
/// <summary>
/// Creates an enumerable for asynchronously enumerating all available resources from the server.
/// </summary>
/// <param name="client">The client.</param>
/// <param name="client">The client instance used to communicate with the MCP server.</param>
/// <param name="cancellationToken">The <see cref="CancellationToken"/> to monitor for cancellation requests. The default is <see cref="CancellationToken.None"/>.</param>
/// <returns>An asynchronous sequence of all available resources.</returns>
/// <returns>An asynchronous sequence of all available resources as <see cref="Resource"/> instances.</returns>
/// <remarks>
/// <para>
/// This method uses asynchronous enumeration to retrieve resources from the server, which allows processing resources
/// as they arrive rather than waiting for all resources to be retrieved. The method automatically handles pagination
/// with cursors if the server responds with resources split across multiple responses.
/// </para>
/// <para>
/// Every iteration through the returned <see cref="IAsyncEnumerable{Resource}"/>
/// will result in requerying the server and yielding the sequence of available resources.
/// </para>
/// </remarks>
/// <example>
/// <code>
/// // Enumerate all resources available on the server
/// await foreach (var resource in client.EnumerateResourcesAsync())
/// {
/// Console.WriteLine($"Resource URI: {resource.Uri}");
/// }
/// </code>
/// </example>
/// <exception cref="ArgumentNullException"><paramref name="client"/> is <see langword="null"/>.</exception>
public static async IAsyncEnumerable<Resource> EnumerateResourcesAsync(
this IMcpClient client, [EnumeratorCancellation] CancellationToken cancellationToken = default)
{
@ -363,9 +551,12 @@ public static class McpClientExtensions
/// <summary>
/// Reads a resource from the server.
/// </summary>
/// <param name="client">The client.</param>
/// <param name="client">The client instance used to communicate with the MCP server.</param>
/// <param name="uri">The uri of the resource.</param>
/// <param name="cancellationToken">The <see cref="CancellationToken"/> to monitor for cancellation requests. The default is <see cref="CancellationToken.None"/>.</param>
/// <exception cref="ArgumentNullException"><paramref name="client"/> is <see langword="null"/>.</exception>
/// <exception cref="ArgumentNullException"><paramref name="uri"/> is <see langword="null"/>.</exception>
/// <exception cref="ArgumentException"><paramref name="uri"/> is empty or composed entirely of whitespace.</exception>
public static Task<ReadResourceResult> ReadResourceAsync(
this IMcpClient client, string uri, CancellationToken cancellationToken = default)
{
@ -381,13 +572,51 @@ public static class McpClientExtensions
}
/// <summary>
/// Gets the completion options for a resource or prompt reference and (named) argument.
/// Reads a resource from the server.
/// </summary>
/// <param name="client">The client.</param>
/// <param name="reference">A resource (uri) or prompt (name) reference</param>
/// <param name="argumentName">Name of argument. Must be non-null and non-empty.</param>
/// <param name="argumentValue">Value of argument. Must be non-null.</param>
/// <param name="client">The client instance used to communicate with the MCP server.</param>
/// <param name="uri">The uri of the resource.</param>
/// <param name="cancellationToken">The <see cref="CancellationToken"/> to monitor for cancellation requests. The default is <see cref="CancellationToken.None"/>.</param>
/// <exception cref="ArgumentNullException"><paramref name="client"/> is <see langword="null"/>.</exception>
/// <exception cref="ArgumentNullException"><paramref name="uri"/> is <see langword="null"/>.</exception>
public static Task<ReadResourceResult> ReadResourceAsync(
this IMcpClient client, Uri uri, CancellationToken cancellationToken = default)
{
Throw.IfNull(client);
Throw.IfNull(uri);
return ReadResourceAsync(client, uri.ToString(), cancellationToken);
}
/// <summary>
/// Requests completion suggestions for a prompt argument or resource reference.
/// </summary>
/// <param name="client">The client instance used to communicate with the MCP server.</param>
/// <param name="reference">The reference object specifying the type and optional URI or name.</param>
/// <param name="argumentName">The name of the argument for which completions are requested.</param>
/// <param name="argumentValue">The current value of the argument, used to filter relevant completions.</param>
/// <param name="cancellationToken">The <see cref="CancellationToken"/> to monitor for cancellation requests. The default is <see cref="CancellationToken.None"/>.</param>
/// <returns>A <see cref="CompleteResult"/> containing completion suggestions.</returns>
/// <remarks>
/// <para>
/// This method allows clients to request auto-completion suggestions for arguments in a prompt template
/// or for resource references.
/// </para>
/// <para>
/// When working with prompt references, the server will return suggestions for the specified argument
/// that match or begin with the current argument value. This is useful for implementing intelligent
/// auto-completion in user interfaces.
/// </para>
/// <para>
/// When working with resource references, the server will return suggestions relevant to the specified
/// resource URI.
/// </para>
/// </remarks>
/// <exception cref="ArgumentNullException"><paramref name="client"/> is <see langword="null"/>.</exception>
/// <exception cref="ArgumentNullException"><paramref name="reference"/> is <see langword="null"/>.</exception>
/// <exception cref="ArgumentNullException"><paramref name="argumentName"/> is <see langword="null"/>.</exception>
/// <exception cref="ArgumentException"><paramref name="argumentName"/> is empty or composed entirely of whitespace.</exception>
/// <exception cref="McpException">The server returned an error response.</exception>
public static Task<CompleteResult> CompleteAsync(this IMcpClient client, Reference reference, string argumentName, string argumentValue, CancellationToken cancellationToken = default)
{
Throw.IfNull(client);
@ -412,11 +641,30 @@ public static class McpClientExtensions
}
/// <summary>
/// Subscribes to a resource on the server.
/// Subscribes to a resource on the server to receive notifications when it changes.
/// </summary>
/// <param name="client">The client.</param>
/// <param name="uri">The uri of the resource.</param>
/// <param name="client">The client instance used to communicate with the MCP server.</param>
/// <param name="uri">The URI of the resource to which to subscribe.</param>
/// <param name="cancellationToken">The <see cref="CancellationToken"/> to monitor for cancellation requests. The default is <see cref="CancellationToken.None"/>.</param>
/// <returns>A task that represents the asynchronous operation.</returns>
/// <remarks>
/// <para>
/// This method allows the client to register interest in a specific resource identified by its URI.
/// When the resource changes, the server will send notifications to the client, enabling real-time
/// updates without polling.
/// </para>
/// <para>
/// The subscription remains active until explicitly unsubscribed using <see cref="M:UnsubscribeFromResourceAsync"/>
/// or until the client disconnects from the server.
/// </para>
/// <para>
/// To handle resource change notifications, register an event handler for the appropriate notification events,
/// such as with <see cref="IMcpEndpoint.RegisterNotificationHandler"/>.
/// </para>
/// </remarks>
/// <exception cref="ArgumentNullException"><paramref name="client"/> is <see langword="null"/>.</exception>
/// <exception cref="ArgumentNullException"><paramref name="uri"/> is <see langword="null"/>.</exception>
/// <exception cref="ArgumentException"><paramref name="uri"/> is empty or composed entirely of whitespace.</exception>
public static Task SubscribeToResourceAsync(this IMcpClient client, string uri, CancellationToken cancellationToken = default)
{
Throw.IfNull(client);
@ -431,11 +679,61 @@ public static class McpClientExtensions
}
/// <summary>
/// Unsubscribes from a resource on the server.
/// Subscribes to a resource on the server to receive notifications when it changes.
/// </summary>
/// <param name="client">The client.</param>
/// <param name="uri">The uri of the resource.</param>
/// <param name="client">The client instance used to communicate with the MCP server.</param>
/// <param name="uri">The URI of the resource to which to subscribe.</param>
/// <param name="cancellationToken">The <see cref="CancellationToken"/> to monitor for cancellation requests. The default is <see cref="CancellationToken.None"/>.</param>
/// <returns>A task that represents the asynchronous operation.</returns>
/// <remarks>
/// <para>
/// This method allows the client to register interest in a specific resource identified by its URI.
/// When the resource changes, the server will send notifications to the client, enabling real-time
/// updates without polling.
/// </para>
/// <para>
/// The subscription remains active until explicitly unsubscribed using <see cref="M:UnsubscribeFromResourceAsync"/>
/// or until the client disconnects from the server.
/// </para>
/// <para>
/// To handle resource change notifications, register an event handler for the appropriate notification events,
/// such as with <see cref="IMcpEndpoint.RegisterNotificationHandler"/>.
/// </para>
/// </remarks>
/// <exception cref="ArgumentNullException"><paramref name="client"/> is <see langword="null"/>.</exception>
/// <exception cref="ArgumentNullException"><paramref name="uri"/> is <see langword="null"/>.</exception>
public static Task SubscribeToResourceAsync(this IMcpClient client, Uri uri, CancellationToken cancellationToken = default)
{
Throw.IfNull(client);
Throw.IfNull(uri);
return SubscribeToResourceAsync(client, uri.ToString(), cancellationToken);
}
/// <summary>
/// Unsubscribes from a resource on the server to stop receiving notifications about its changes.
/// </summary>
/// <param name="client">The client instance used to communicate with the MCP server.</param>
/// <param name="uri">The URI of the resource to unsubscribe from.</param>
/// <param name="cancellationToken">The <see cref="CancellationToken"/> to monitor for cancellation requests. The default is <see cref="CancellationToken.None"/>.</param>
/// <returns>A task that represents the asynchronous operation.</returns>
/// <remarks>
/// <para>
/// This method cancels a previous subscription to a resource, stopping the client from receiving
/// notifications when that resource changes.
/// </para>
/// <para>
/// The unsubscribe operation is idempotent, meaning it can be called multiple times for the same
/// resource without causing errors, even if there is no active subscription.
/// </para>
/// <para>
/// Due to the nature of the MCP protocol, it is possible the client may receive notifications after
/// unsubscribing if those notifications were issued by the server prior to the unsubscribe request being received.
/// </para>
/// </remarks>
/// <exception cref="ArgumentNullException"><paramref name="client"/> is <see langword="null"/>.</exception>
/// <exception cref="ArgumentNullException"><paramref name="uri"/> is <see langword="null"/>.</exception>
/// <exception cref="ArgumentException"><paramref name="uri"/> is empty or composed entirely of whitespace.</exception>
public static Task UnsubscribeFromResourceAsync(this IMcpClient client, string uri, CancellationToken cancellationToken = default)
{
Throw.IfNull(client);
@ -450,14 +748,66 @@ public static class McpClientExtensions
}
/// <summary>
/// Invokes a tool on the server with optional arguments.
/// Unsubscribes from a resource on the server to stop receiving notifications about its changes.
/// </summary>
/// <param name="client">The client.</param>
/// <param name="toolName">The name of the tool to call.</param>
/// <param name="arguments">Optional arguments for the tool.</param>
/// <param name="serializerOptions">The serialization options governing argument serialization.</param>
/// <param name="client">The client instance used to communicate with the MCP server.</param>
/// <param name="uri">The URI of the resource to unsubscribe from.</param>
/// <param name="cancellationToken">The <see cref="CancellationToken"/> to monitor for cancellation requests. The default is <see cref="CancellationToken.None"/>.</param>
/// <returns>A task containing the tool's response.</returns>
/// <returns>A task that represents the asynchronous operation.</returns>
/// <remarks>
/// <para>
/// This method cancels a previous subscription to a resource, stopping the client from receiving
/// notifications when that resource changes.
/// </para>
/// <para>
/// The unsubscribe operation is idempotent, meaning it can be called multiple times for the same
/// resource without causing errors, even if there is no active subscription.
/// </para>
/// <para>
/// Due to the nature of the MCP protocol, it is possible the client may receive notifications after
/// unsubscribing if those notifications were issued by the server prior to the unsubscribe request being received.
/// </para>
/// </remarks>
/// <exception cref="ArgumentNullException"><paramref name="client"/> is <see langword="null"/>.</exception>
/// <exception cref="ArgumentNullException"><paramref name="uri"/> is <see langword="null"/>.</exception>
public static Task UnsubscribeFromResourceAsync(this IMcpClient client, Uri uri, CancellationToken cancellationToken = default)
{
Throw.IfNull(client);
Throw.IfNull(uri);
return UnsubscribeFromResourceAsync(client, uri.ToString(), cancellationToken);
}
/// <summary>
/// Invokes a tool on the server
/// </summary>
/// <param name="client">The client instance used to communicate with the MCP server.</param>
/// <param name="toolName">The name of the tool to call on the server..</param>
/// <param name="arguments">Optional dictionary of arguments to pass to the tool. Each key represents a parameter name,
/// and its associated value represents the argument value.
/// </param>
/// <param name="serializerOptions">
/// The JSON serialization options governing argument serialization. If <see langword="null"/>, the default serialization options will be used.
/// </param>
/// <param name="cancellationToken">The <see cref="CancellationToken"/> to monitor for cancellation requests. The default is <see cref="CancellationToken.None"/>.</param>
/// <returns>
/// A task containing the <see cref="CallToolResponse"/> from the tool execution. The response includes
/// the tool's output content, which may be structured data, text, or an error message.
/// </returns>
/// <exception cref="ArgumentNullException"><paramref name="client"/> is <see langword="null"/>.</exception>
/// <exception cref="ArgumentNullException"><paramref name="toolName"/> is <see langword="null"/>.</exception>
/// <exception cref="McpException">The server could not find the requested tool, or the server encountered an error while processing the request.</exception>
/// <example>
/// <code>
/// // Call a simple echo tool with a string argument
/// var result = await client.CallToolAsync(
/// "echo",
/// new Dictionary&lt;string, object?&gt;
/// {
/// ["message"] = "Hello MCP!"
/// });
/// </code>
/// </example>
public static Task<CallToolResponse> CallToolAsync(
this IMcpClient client,
string toolName,
@ -485,6 +835,7 @@ public static class McpClientExtensions
/// </summary>
/// <param name="requestParams"></param>
/// <returns>The created pair of messages and options.</returns>
/// <exception cref="ArgumentNullException"><paramref name="requestParams"/> is <see langword="null"/>.</exception>
internal static (IList<ChatMessage> Messages, ChatOptions? Options) ToChatClientArguments(
this CreateMessageRequestParams requestParams)
{
@ -537,6 +888,7 @@ public static class McpClientExtensions
/// <summary>Converts the contents of a <see cref="ChatResponse"/> into a <see cref="CreateMessageResult"/>.</summary>
/// <param name="chatResponse">The <see cref="ChatResponse"/> whose contents should be extracted.</param>
/// <returns>The created <see cref="CreateMessageResult"/>.</returns>
/// <exception cref="ArgumentNullException"><paramref name="chatResponse"/> is <see langword="null"/>.</exception>
internal static CreateMessageResult ToCreateMessageResult(this ChatResponse chatResponse)
{
Throw.IfNull(chatResponse);
@ -584,7 +936,18 @@ public static class McpClientExtensions
/// satisfy sampling requests using the specified <see cref="IChatClient"/>.
/// </summary>
/// <param name="chatClient">The <see cref="IChatClient"/> with which to satisfy sampling requests.</param>
/// <returns>The created handler delegate.</returns>
/// <returns>The created handler delegate that can be assigned to <see cref="SamplingCapability.SamplingHandler"/>.</returns>
/// <remarks>
/// <para>
/// This method creates a function that converts MCP message requests into chat client calls, enabling
/// an MCP client to generate text or other content using an actual AI model via the provided chat client.
/// </para>
/// <para>
/// The handler can process text messages, image messages, and resource messages as defined in the
/// Model Context Protocol.
/// </para>
/// </remarks>
/// <exception cref="ArgumentNullException"><paramref name="chatClient"/> is <see langword="null"/>.</exception>
public static Func<CreateMessageRequestParams?, IProgress<ProgressNotificationValue>, CancellationToken, Task<CreateMessageResult>> CreateSamplingHandler(
this IChatClient chatClient)
{
@ -616,11 +979,29 @@ public static class McpClientExtensions
}
/// <summary>
/// Configures the minimum logging level for the server.
/// Sets the logging level for the server to control which log messages are sent to the client.
/// </summary>
/// <param name="client">The client.</param>
/// <param name="level">The minimum log level of messages to be generated.</param>
/// <param name="client">The client instance used to communicate with the MCP server.</param>
/// <param name="level">The minimum severity level of log messages to receive from the server.</param>
/// <param name="cancellationToken">The <see cref="CancellationToken"/> to monitor for cancellation requests. The default is <see cref="CancellationToken.None"/>.</param>
/// <returns>A task representing the asynchronous operation.</returns>
/// <remarks>
/// <para>
/// After this request is processed, the server will send log messages at or above the specified
/// logging level as notifications to the client. For example, if <see cref="LoggingLevel.Warning"/> is set,
/// the client will receive <see cref="LoggingLevel.Warning"/>, <see cref="LoggingLevel.Error"/>,
/// <see cref="LoggingLevel.Critical"/>, <see cref="LoggingLevel.Alert"/>, and <see cref="LoggingLevel.Emergency"/>
/// level messages.
/// </para>
/// <para>
/// To receive all log messages, set the level to <see cref="LoggingLevel.Debug"/>.
/// </para>
/// <para>
/// Log messages are delivered as notifications to the client and can be captured by registering
/// appropriate event handlers with the client implementation, such as with <see cref="IMcpEndpoint.RegisterNotificationHandler"/>.
/// </para>
/// </remarks>
/// <exception cref="ArgumentNullException"><paramref name="client"/> is <see langword="null"/>.</exception>
public static Task SetLoggingLevel(this IMcpClient client, LoggingLevel level, CancellationToken cancellationToken = default)
{
Throw.IfNull(client);
@ -634,11 +1015,28 @@ public static class McpClientExtensions
}
/// <summary>
/// Configures the minimum logging level for the server.
/// Sets the logging level for the server to control which log messages are sent to the client.
/// </summary>
/// <param name="client">The client.</param>
/// <param name="level">The minimum log level of messages to be generated.</param>
/// <param name="client">The client instance used to communicate with the MCP server.</param>
/// <param name="level">The minimum severity level of log messages to receive from the server.</param>
/// <param name="cancellationToken">The <see cref="CancellationToken"/> to monitor for cancellation requests. The default is <see cref="CancellationToken.None"/>.</param>
/// <returns>A task representing the asynchronous operation.</returns>
/// <remarks>
/// <para>
/// After this request is processed, the server will send log messages at or above the specified
/// logging level as notifications to the client. For example, if <see cref="LogLevel.Warning"/> is set,
/// the client will receive <see cref="LogLevel.Warning"/>, <see cref="LogLevel.Error"/>,
/// and <see cref="LogLevel.Critical"/> level messages.
/// </para>
/// <para>
/// To receive all log messages, set the level to <see cref="LogLevel.Trace"/>.
/// </para>
/// <para>
/// Log messages are delivered as notifications to the client and can be captured by registering
/// appropriate event handlers with the client implementation, such as with <see cref="IMcpEndpoint.RegisterNotificationHandler"/>.
/// </para>
/// </remarks>
/// <exception cref="ArgumentNullException"><paramref name="client"/> is <see langword="null"/>.</exception>
public static Task SetLoggingLevel(this IMcpClient client, LogLevel level, CancellationToken cancellationToken = default) =>
SetLoggingLevel(client, McpServer.ToLoggingLevel(level), cancellationToken);

View file

@ -1,4 +1,4 @@
using ModelContextProtocol.Logging;
using ModelContextProtocol.Logging;
using ModelContextProtocol.Protocol.Transport;
using ModelContextProtocol.Utils;
using Microsoft.Extensions.Logging;
@ -6,7 +6,14 @@ using Microsoft.Extensions.Logging.Abstractions;
namespace ModelContextProtocol.Client;
/// <summary>Provides factory methods for creating MCP clients.</summary>
/// <summary>
/// Provides factory methods for creating Model Context Protocol (MCP) clients.
/// </summary>
/// <remarks>
/// This factory class is the primary way to instantiate <see cref="IMcpClient"/> instances
/// that connect to MCP servers. It handles the creation and connection
/// of appropriate implementations through the supplied transport.
/// </remarks>
public static class McpClientFactory
{
/// <summary>Creates an <see cref="IMcpClient"/>, connecting it to the specified server.</summary>

View file

@ -1,31 +1,59 @@
using ModelContextProtocol.Protocol.Types;
using ModelContextProtocol.Protocol.Types;
namespace ModelContextProtocol.Client;
/// <summary>
/// Configuration options for the MCP client. This is passed to servers during the initialization sequence, letting them know about the client's capabilities and
/// protocol version.
/// <see href="https://spec.modelcontextprotocol.io/specification/2024-11-05/basic/lifecycle/">See the protocol specification for details on capability negotiation</see>
/// Provides configuration options for creating <see cref="IMcpClient"/> instances.
/// </summary>
/// <remarks>
/// These options are typically passed to <see cref="McpClientFactory.CreateAsync"/> when creating a client.
/// They define client capabilities, protocol version, and other client-specific settings.
/// </remarks>
public class McpClientOptions
{
/// <summary>
/// Information about this client implementation.
/// Gets or sets information about this client implementation, including its name and version.
/// </summary>
/// <remarks>
/// <para>
/// This information is sent to the server during initialization to identify the client.
/// It's often displayed in server logs and can be used for debugging and compatibility checks.
/// </para>
/// <para>
/// When not specified, information sourced from the current process will be used.
/// </para>
/// </remarks>
public Implementation? ClientInfo { get; set; }
/// <summary>
/// Client capabilities to advertise to the server.
/// Gets or sets the client capabilities to advertise to the server.
/// </summary>
public ClientCapabilities? Capabilities { get; set; }
/// <summary>
/// Protocol version to request from the server.
/// Gets or sets the protocol version to request from the server, using a date-based versioning scheme.
/// </summary>
/// <remarks>
/// The protocol version is a key part of the initialization handshake. The client and server must
/// agree on a compatible protocol version to communicate successfully. If the server doesn't support
/// the requested version, it will respond with a version mismatch error.
/// </remarks>
public string ProtocolVersion { get; set; } = "2024-11-05";
/// <summary>
/// Timeout for initialization sequence.
/// Gets or sets a timeout for the client-server initialization handshake sequence.
/// </summary>
/// <remarks>
/// <para>
/// This timeout determines how long the client will wait for the server to respond during
/// the initialization protocol handshake. If the server doesn't respond within this timeframe,
/// an exception will be thrown.
/// </para>
/// <para>
/// Setting an appropriate timeout prevents the client from hanging indefinitely when
/// connecting to unresponsive servers.
/// </para>
/// <para>The default value is 60 seconds.</para>
/// </remarks>
public TimeSpan InitializationTimeout { get; set; } = TimeSpan.FromSeconds(60);
}

View file

@ -1,9 +1,23 @@
using ModelContextProtocol.Protocol.Types;
using ModelContextProtocol.Protocol.Types;
using System.Text.Json;
namespace ModelContextProtocol.Client;
/// <summary>Provides an invocable prompt.</summary>
/// <summary>
/// Represents a named prompt that can be retrieved from an MCP server and invoked with arguments.
/// </summary>
/// <remarks>
/// <para>
/// This class provides a client-side wrapper around a prompt defined on an MCP server. It allows
/// retrieving the prompt's content by sending a request to the server with optional arguments.
/// Instances of this class are typically obtained by calling <see cref="McpClientExtensions.ListPromptsAsync"/>
/// or <see cref="McpClientExtensions.EnumeratePromptsAsync"/>.
/// </para>
/// <para>
/// Each prompt has a name and optionally a description, and it can be invoked with arguments
/// to produce customized prompt content from the server.
/// </para>
/// </remarks>
public sealed class McpClientPrompt
{
private readonly IMcpClient _client;
@ -14,16 +28,42 @@ public sealed class McpClientPrompt
ProtocolPrompt = prompt;
}
/// <summary>Gets the protocol <see cref="Prompt"/> type for this instance.</summary>
/// <summary>Gets the underlying protocol <see cref="Prompt"/> type for this instance.</summary>
/// <remarks>
/// <para>
/// This property provides direct access to the underlying protocol representation of the prompt,
/// which can be useful for advanced scenarios or when implementing custom MCP client extensions.
/// </para>
/// <para>
/// For most common use cases, you can use the more convenient <see cref="Name"/> and
/// <see cref="Description"/> properties instead of accessing the <see cref="ProtocolPrompt"/> directly.
/// </para>
/// </remarks>
public Prompt ProtocolPrompt { get; }
/// <summary>Gets the name of the prompt.</summary>
public string Name => ProtocolPrompt.Name;
/// <summary>Gets a description of the prompt.</summary>
public string? Description => ProtocolPrompt.Description;
/// <summary>
/// Retrieves a specific prompt with optional arguments.
/// Gets this prompt's content by sending a request to the server with optional arguments.
/// </summary>
/// <param name="arguments">Optional arguments for the prompt</param>
/// <param name="arguments">Optional arguments to pass to the prompt. Keys are parameter names, and values are the argument values.</param>
/// <param name="serializerOptions">The serialization options governing argument serialization.</param>
/// <param name="cancellationToken">The <see cref="CancellationToken"/> to monitor for cancellation requests. The default is <see cref="CancellationToken.None"/>.</param>
/// <returns>A task containing the prompt's content and messages.</returns>
/// <returns>A <see cref="ValueTask"/> containing the prompt's result with content and messages.</returns>
/// <remarks>
/// <para>
/// This method sends a request to the MCP server to execute this prompt with the provided arguments.
/// The server will process the request and return a result containing messages or other content.
/// </para>
/// <para>
/// This is a convenience method that internally calls <see cref="McpClientExtensions.GetPromptAsync"/>
/// with this prompt's name and arguments.
/// </para>
/// </remarks>
public async ValueTask<GetPromptResult> GetAsync(
IEnumerable<KeyValuePair<string, object?>>? arguments = null,
JsonSerializerOptions? serializerOptions = null,
@ -35,10 +75,4 @@ public sealed class McpClientPrompt
return await _client.GetPromptAsync(ProtocolPrompt.Name, argDict, serializerOptions, cancellationToken: cancellationToken).ConfigureAwait(false);
}
/// <summary>Gets the name of the prompt.</summary>
public string Name => ProtocolPrompt.Name;
/// <summary>Gets a description of the prompt.</summary>
public string? Description => ProtocolPrompt.Description;
}

View file

@ -1,4 +1,4 @@
using ModelContextProtocol.Protocol.Types;
using ModelContextProtocol.Protocol.Types;
using ModelContextProtocol.Utils.Json;
using Microsoft.Extensions.AI;
using System.Text.Json;
@ -6,7 +6,24 @@ using System.Collections.ObjectModel;
namespace ModelContextProtocol.Client;
/// <summary>Provides an AI function that calls a tool through <see cref="IMcpClient"/>.</summary>
/// <summary>
/// Provides an <see cref="AIFunction"/> that calls a tool via an <see cref="IMcpClient"/>.
/// </summary>
/// <remarks>
/// <para>
/// The <see cref="McpClientTool"/> class encapsulates an <see cref="IMcpClient"/> along with a description of
/// a tool available via that client, allowing it to be invoked as an <see cref="AIFunction"/>. This enables integration
/// with AI models that support function calling capabilities.
/// </para>
/// <para>
/// Tools retrieved from an MCP server can be customized for model presentation using methods like
/// <see cref="WithName"/> and <see cref="WithDescription"/> without changing the underlying tool functionality.
/// </para>
/// <para>
/// Typically, you would get instances of this class by calling the <see cref="McpClientExtensions.ListToolsAsync"/>
/// or <see cref="McpClientExtensions.EnumerateToolsAsync"/> extension methods on an <see cref="IMcpClient"/> instance.
/// </para>
/// </remarks>
public sealed class McpClientTool : AIFunction
{
/// <summary>Additional properties exposed from tools.</summary>
@ -30,31 +47,15 @@ public sealed class McpClientTool : AIFunction
}
/// <summary>
/// Creates a new instance of the tool with the specified name.
/// This is useful for optimizing the tool name for specific models or for prefixing the tool name with a (usually server-derived) namespace to avoid conflicts.
/// The server will still be called with the original tool name, so no mapping is required.
/// Gets the protocol <see cref="Tool"/> type for this instance.
/// </summary>
/// <param name="name">The model-facing name to give the tool.</param>
/// <returns>Copy of this McpClientTool with the provided name</returns>
public McpClientTool WithName(string name)
{
return new McpClientTool(_client, ProtocolTool, JsonSerializerOptions, name, _description);
}
/// <summary>
/// Creates a new instance of the tool with the specified description.
/// This can be used to provide modified or additional (e.g. examples) context to the model about the tool.
/// This will in general require a hard-coded mapping in the client.
/// It is not recommended to use this without running evaluations to ensure the model actually benefits from the custom description.
/// </summary>
/// <param name="description">The description to give the tool.</param>
/// <returns>Copy of this McpClientTool with the provided description</returns>
public McpClientTool WithDescription(string description)
{
return new McpClientTool(_client, ProtocolTool, JsonSerializerOptions, _name, description);
}
/// <summary>Gets the protocol <see cref="Tool"/> type for this instance.</summary>
/// <remarks>
/// This property provides direct access to the underlying protocol representation of the tool,
/// which can be useful for advanced scenarios or when implementing custom MCP client extensions.
/// It contains the original metadata about the tool as provided by the server, including its
/// name, description, and schema information before any customizations applied through methods
/// like <see cref="WithName"/> or <see cref="WithDescription"/>.
/// </remarks>
public Tool ProtocolTool { get; }
/// <inheritdoc/>
@ -79,4 +80,59 @@ public sealed class McpClientTool : AIFunction
CallToolResponse result = await _client.CallToolAsync(ProtocolTool.Name, arguments, JsonSerializerOptions, cancellationToken: cancellationToken).ConfigureAwait(false);
return JsonSerializer.SerializeToElement(result, McpJsonUtilities.JsonContext.Default.CallToolResponse);
}
/// <summary>
/// Creates a new instance of the tool but modified to return the specified name from its <see cref="Name"/> property.
/// </summary>
/// <param name="name">The model-facing name to give the tool.</param>
/// <returns>A new instance of <see cref="McpClientTool"/> with the provided name.</returns>
/// <remarks>
/// <para>
/// This is useful for optimizing the tool name for specific models or for prefixing the tool name
/// with a namespace to avoid conflicts.
/// </para>
/// <para>
/// Changing the name can help with:
/// </para>
/// <list type="bullet">
/// <item>Making the tool name more intuitive for the model</item>
/// <item>Preventing name collisions when using tools from multiple sources</item>
/// <item>Creating specialized versions of a general tool for specific contexts</item>
/// </list>
/// <para>
/// When invoking <see cref="AIFunction.InvokeAsync"/>, the MCP server will still be called with
/// the original tool name, so no mapping is required on the server side. This new name only affects
/// the value returned from this instance's <see cref="AITool.Name"/>.
/// </para>
/// </remarks>
public McpClientTool WithName(string name)
{
return new McpClientTool(_client, ProtocolTool, JsonSerializerOptions, name, _description);
}
/// <summary>
/// Creates a new instance of the tool but modified to return the specified description from its <see cref="Description"/> property.
/// </summary>
/// <param name="description">The description to give the tool.</param>
/// <remarks>
/// <para>
/// Changing the description can help the model better understand the tool's purpose or provide more
/// context about how the tool should be used. This is particularly useful when:
/// </para>
/// <list type="bullet">
/// <item>The original description is too technical or lacks clarity for the model</item>
/// <item>You want to add example usage scenarios to improve the model's understanding</item>
/// <item>You need to tailor the tool's description for specific model requirements</item>
/// </list>
/// <para>
/// When invoking <see cref="AIFunction.InvokeAsync"/>, the MCP server will still be called with
/// the original tool description, so no mapping is required on the server side. This new description only affects
/// the value returned from this instance's <see cref="AITool.Description"/>.
/// </para>
/// </remarks>
/// <returns>A new instance of <see cref="McpClientTool"/> with the provided description.</returns>
public McpClientTool WithDescription(string description)
{
return new McpClientTool(_client, ProtocolTool, JsonSerializerOptions, _name, description);
}
}

View file

@ -1,9 +1,12 @@
using ModelContextProtocol.Utils;
using ModelContextProtocol.Utils;
namespace Microsoft.Extensions.DependencyInjection;
/// <summary>
/// Default implementation of <see cref="IMcpServerBuilder"/>.
/// Default implementation of <see cref="IMcpServerBuilder"/> that enables fluent configuration
/// of the Model Context Protocol (MCP) server. This builder is returned by the
/// <see cref="McpServerServiceCollectionExtensions.AddMcpServer"/> extension method and
/// provides access to the service collection for registering additional MCP components.
/// </summary>
internal sealed class DefaultMcpServerBuilder : IMcpServerBuilder
{
@ -13,8 +16,9 @@ internal sealed class DefaultMcpServerBuilder : IMcpServerBuilder
/// <summary>
/// Initializes a new instance of the <see cref="DefaultMcpServerBuilder"/> class.
/// </summary>
/// <param name="services"></param>
/// <exception cref="ArgumentNullException"></exception>
/// <param name="services">The service collection to which MCP server services will be added. This collection
/// is exposed through the <see cref="Services"/> property to allow additional configuration.</param>
/// <exception cref="ArgumentNullException">Thrown when <paramref name="services"/> is null.</exception>
public DefaultMcpServerBuilder(IServiceCollection services)
{
Throw.IfNull(services);

View file

@ -1,14 +1,25 @@
using ModelContextProtocol.Server;
using ModelContextProtocol.Server;
namespace Microsoft.Extensions.DependencyInjection;
/// <summary>
/// Builder for configuring <see cref="IMcpServer"/> instances.
/// Provides a builder for configuring <see cref="IMcpServer"/> instances.
/// </summary>
/// <remarks>
/// <para>
/// The <see cref="IMcpServerBuilder"/> interface provides a fluent API for configuring Model Context Protocol (MCP) servers
/// when using dependency injection. It exposes methods for registering tools, prompts, custom request handlers,
/// and server transports, allowing for comprehensive server configuration through a chain of method calls.
/// </para>
/// <para>
/// The builder is obtained from the <see cref="McpServerServiceCollectionExtensions.AddMcpServer"/> extension
/// method and provides access to the underlying service collection via the <see cref="Services"/> property.
/// </para>
/// </remarks>
public interface IMcpServerBuilder
{
/// <summary>
/// Gets the service collection.
/// Gets the associated service collection.
/// </summary>
IServiceCollection Services { get; }
}

View file

@ -1,4 +1,4 @@
using Microsoft.Extensions.DependencyInjection.Extensions;
using Microsoft.Extensions.DependencyInjection.Extensions;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Options;
using ModelContextProtocol.Hosting;
@ -24,6 +24,7 @@ public static partial class McpServerBuilderExtensions
/// <summary>Adds <see cref="McpServerTool"/> instances to the service collection backing <paramref name="builder"/>.</summary>
/// <typeparam name="TToolType">The tool type.</typeparam>
/// <param name="builder">The builder instance.</param>
/// <returns>The builder provided in <paramref name="builder"/>.</returns>
/// <exception cref="ArgumentNullException"><paramref name="builder"/> is <see langword="null"/>.</exception>
/// <remarks>
/// This method discovers all instance and static methods (public and non-public) on the specified <typeparamref name="TToolType"/>
@ -54,6 +55,7 @@ public static partial class McpServerBuilderExtensions
/// <summary>Adds <see cref="McpServerTool"/> instances to the service collection backing <paramref name="builder"/>.</summary>
/// <param name="builder">The builder instance.</param>
/// <param name="toolTypes">Types with marked methods to add as tools to the server.</param>
/// <returns>The builder provided in <paramref name="builder"/>.</returns>
/// <exception cref="ArgumentNullException"><paramref name="builder"/> is <see langword="null"/>.</exception>
/// <exception cref="ArgumentNullException"><paramref name="toolTypes"/> is <see langword="null"/>.</exception>
/// <remarks>
@ -90,8 +92,29 @@ public static partial class McpServerBuilderExtensions
/// Adds types marked with the <see cref="McpServerToolTypeAttribute"/> attribute from the given assembly as tools to the server.
/// </summary>
/// <param name="builder">The builder instance.</param>
/// <param name="toolAssembly">The assembly to load the types from. Null to get the current assembly</param>
/// <param name="toolAssembly">The assembly to load the types from. If <see langword="null"/>, the calling assembly will be used.</param>
/// <returns>The builder provided in <paramref name="builder"/>.</returns>
/// <exception cref="ArgumentNullException"><paramref name="builder"/> is <see langword="null"/>.</exception>
/// <remarks>
/// <para>
/// This method scans the specified assembly (or the calling assembly if none is provided) for classes
/// marked with the <see cref="McpServerToolTypeAttribute"/>. It then discovers all methods within those
/// classes that are marked with the <see cref="McpServerToolAttribute"/> and registers them as <see cref="McpServerTool"/>s
/// in the <paramref name="builder"/>'s <see cref="IServiceCollection"/>.
/// </para>
/// <para>
/// The method automatically handles both static and instance methods. For instance methods, a new instance
/// of the containing class will be constructed for each invocation of the tool.
/// </para>
/// <para>
/// Tools registered through this method can be discovered by clients using the <c>list_tools</c> request
/// and invoked using the <c>call_tool</c> request.
/// </para>
/// <para>
/// Note that this method performs reflection at runtime and may not work in Native AOT scenarios. For
/// Native AOT compatibility, consider using the generic <see cref="WithTools{TToolType}"/> method instead.
/// </para>
/// </remarks>
[RequiresUnreferencedCode(WithToolsRequiresUnreferencedCodeMessage)]
public static IMcpServerBuilder WithToolsFromAssembly(this IMcpServerBuilder builder, Assembly? toolAssembly = null)
{
@ -114,6 +137,7 @@ public static partial class McpServerBuilderExtensions
/// <summary>Adds <see cref="McpServerPrompt"/> instances to the service collection backing <paramref name="builder"/>.</summary>
/// <typeparam name="TPromptType">The prompt type.</typeparam>
/// <param name="builder">The builder instance.</param>
/// <returns>The builder provided in <paramref name="builder"/>.</returns>
/// <exception cref="ArgumentNullException"><paramref name="builder"/> is <see langword="null"/>.</exception>
/// <remarks>
/// This method discovers all instance and static methods (public and non-public) on the specified <typeparamref name="TPromptType"/>
@ -144,6 +168,7 @@ public static partial class McpServerBuilderExtensions
/// <summary>Adds <see cref="McpServerPrompt"/> instances to the service collection backing <paramref name="builder"/>.</summary>
/// <param name="builder">The builder instance.</param>
/// <param name="promptTypes">Types with marked methods to add as prompts to the server.</param>
/// <returns>The builder provided in <paramref name="builder"/>.</returns>
/// <exception cref="ArgumentNullException"><paramref name="builder"/> is <see langword="null"/>.</exception>
/// <exception cref="ArgumentNullException"><paramref name="promptTypes"/> is <see langword="null"/>.</exception>
/// <remarks>
@ -180,8 +205,29 @@ public static partial class McpServerBuilderExtensions
/// Adds types marked with the <see cref="McpServerPromptTypeAttribute"/> attribute from the given assembly as prompts to the server.
/// </summary>
/// <param name="builder">The builder instance.</param>
/// <param name="promptAssembly">The assembly to load the types from. Null to get the current assembly</param>
/// <param name="promptAssembly">The assembly to load the types from. If <see langword="null"/>, the calling assembly will be used.</param>
/// <returns>The builder provided in <paramref name="builder"/>.</returns>
/// <exception cref="ArgumentNullException"><paramref name="builder"/> is <see langword="null"/>.</exception>
/// <remarks>
/// <para>
/// This method scans the specified assembly (or the calling assembly if none is provided) for classes
/// marked with the <see cref="McpServerPromptTypeAttribute"/>. It then discovers all methods within those
/// classes that are marked with the <see cref="McpServerPromptAttribute"/> and registers them as <see cref="McpServerPrompt"/>s
/// in the <paramref name="builder"/>'s <see cref="IServiceCollection"/>.
/// </para>
/// <para>
/// The method automatically handles both static and instance methods. For instance methods, a new instance
/// of the containing class will be constructed for each invocation of the prompt.
/// </para>
/// <para>
/// Prompts registered through this method can be discovered by clients using the <c>list_prompts</c> request
/// and invoked using the <c>call_prompt</c> request.
/// </para>
/// <para>
/// Note that this method performs reflection at runtime and may not work in Native AOT scenarios. For
/// Native AOT compatibility, consider using the generic <see cref="WithPrompts{TPromptType}"/> method instead.
/// </para>
/// </remarks>
[RequiresUnreferencedCode(WithPromptsRequiresUnreferencedCodeMessage)]
public static IMcpServerBuilder WithPromptsFromAssembly(this IMcpServerBuilder builder, Assembly? promptAssembly = null)
{
@ -198,10 +244,26 @@ public static partial class McpServerBuilderExtensions
#region Handlers
/// <summary>
/// Sets the handler for list resource templates requests.
/// Configures a handler for listing resource templates available from the Model Context Protocol server.
/// </summary>
/// <param name="builder">The builder instance.</param>
/// <param name="handler">The handler.</param>
/// <param name="handler">The handler function that processes resource template list requests.</param>
/// <returns>The builder provided in <paramref name="builder"/>.</returns>
/// <exception cref="ArgumentNullException"><paramref name="builder"/> is <see langword="null"/>.</exception>
/// <remarks>
/// <para>
/// This handler is responsible for providing clients with information about available resource templates
/// that can be used to construct resource URIs.
/// </para>
/// <para>
/// Resource templates describe the structure of resource URIs that the server can handle. They include
/// URI templates (according to RFC 6570) that clients can use to construct valid resource URIs.
/// </para>
/// <para>
/// This handler is typically paired with <see cref="WithReadResourceHandler"/> to provide a complete
/// resource system where templates define the URI patterns and the read handler provides the actual content.
/// </para>
/// </remarks>
public static IMcpServerBuilder WithListResourceTemplatesHandler(this IMcpServerBuilder builder, Func<RequestContext<ListResourceTemplatesRequestParams>, CancellationToken, Task<ListResourceTemplatesResult>> handler)
{
Throw.IfNull(builder);
@ -211,10 +273,30 @@ public static partial class McpServerBuilderExtensions
}
/// <summary>
/// Sets the handler for list tools requests.
/// Configures a handler for listing tools available from the Model Context Protocol server.
/// </summary>
/// <param name="builder">The builder instance.</param>
/// <param name="handler">The handler.</param>
/// <param name="handler">The handler that processes list tools requests.</param>
/// <returns>The builder provided in <paramref name="builder"/>.</returns>
/// <exception cref="ArgumentNullException"><paramref name="builder"/> is <see langword="null"/>.</exception>
/// <remarks>
/// <para>
/// This handler is called when a client requests a list of available tools. It should return all tools
/// that can be invoked through the server, including their names, descriptions, and parameter specifications.
/// The handler can optionally support pagination via the cursor mechanism for large or dynamically-generated
/// tool collections.
/// </para>
/// <para>
/// When tools are also defined using <see cref="McpServerTool"/> collection, both sets of tools
/// will be combined in the response to clients. This allows for a mix of programmatically defined
/// tools and dynamically generated tools.
/// </para>
/// <para>
/// This method is typically paired with <see cref="WithCallToolHandler"/> to provide a complete tools implementation,
/// where <see cref="WithListToolsHandler"/> advertises available tools and <see cref="WithCallToolHandler"/>
/// executes them when invoked by clients.
/// </para>
/// </remarks>
public static IMcpServerBuilder WithListToolsHandler(this IMcpServerBuilder builder, Func<RequestContext<ListToolsRequestParams>, CancellationToken, Task<ListToolsResult>> handler)
{
Throw.IfNull(builder);
@ -224,10 +306,17 @@ public static partial class McpServerBuilderExtensions
}
/// <summary>
/// Sets the handler for call tool requests.
/// Configures a handler for calling tools available from the Model Context Protocol server.
/// </summary>
/// <param name="builder">The builder instance.</param>
/// <param name="handler">The handler.</param>
/// <param name="handler">The handler function that processes tool calls.</param>
/// <returns>The builder provided in <paramref name="builder"/>.</returns>
/// <exception cref="ArgumentNullException"><paramref name="builder"/> is <see langword="null"/>.</exception>
/// <remarks>
/// The call tool handler is responsible for executing custom tools and returning their results to clients.
/// This method is typically paired with <see cref="WithListToolsHandler"/> to provide a complete tools implementation,
/// where <see cref="WithListToolsHandler"/> advertises available tools and this handler executes them.
/// </remarks>
public static IMcpServerBuilder WithCallToolHandler(this IMcpServerBuilder builder, Func<RequestContext<CallToolRequestParams>, CancellationToken, Task<CallToolResponse>> handler)
{
Throw.IfNull(builder);
@ -237,10 +326,30 @@ public static partial class McpServerBuilderExtensions
}
/// <summary>
/// Sets the handler for list prompts requests.
/// Configures a handler for listing prompts available from the Model Context Protocol server.
/// </summary>
/// <param name="builder">The builder instance.</param>
/// <param name="handler">The handler.</param>
/// <param name="handler">The handler that processes list prompts requests.</param>
/// <returns>The builder provided in <paramref name="builder"/>.</returns>
/// <exception cref="ArgumentNullException"><paramref name="builder"/> is <see langword="null"/>.</exception>
/// <remarks>
/// <para>
/// This handler is called when a client requests a list of available prompts. It should return all prompts
/// that can be invoked through the server, including their names, descriptions, and parameter specifications.
/// The handler can optionally support pagination via the cursor mechanism for large or dynamically-generated
/// prompt collections.
/// </para>
/// <para>
/// When prompts are also defined using <see cref="McpServerPrompt"/> collection, both sets of prompts
/// will be combined in the response to clients. This allows for a mix of programmatically defined
/// prompts and dynamically generated prompts.
/// </para>
/// <para>
/// This method is typically paired with <see cref="WithGetPromptHandler"/> to provide a complete prompts implementation,
/// where <see cref="WithListPromptsHandler"/> advertises available prompts and <see cref="WithGetPromptHandler"/>
/// produces them when invoked by clients.
/// </para>
/// </remarks>
public static IMcpServerBuilder WithListPromptsHandler(this IMcpServerBuilder builder, Func<RequestContext<ListPromptsRequestParams>, CancellationToken, Task<ListPromptsResult>> handler)
{
Throw.IfNull(builder);
@ -250,10 +359,12 @@ public static partial class McpServerBuilderExtensions
}
/// <summary>
/// Sets the handler for get prompt requests.
/// Configures a handler for getting a prompt available from the Model Context Protocol server.
/// </summary>
/// <param name="builder">The builder instance.</param>
/// <param name="handler">The handler.</param>
/// <param name="handler">The handler function that processes prompt requests.</param>
/// <returns>The builder provided in <paramref name="builder"/>.</returns>
/// <exception cref="ArgumentNullException"><paramref name="builder"/> is <see langword="null"/>.</exception>
public static IMcpServerBuilder WithGetPromptHandler(this IMcpServerBuilder builder, Func<RequestContext<GetPromptRequestParams>, CancellationToken, Task<GetPromptResult>> handler)
{
Throw.IfNull(builder);
@ -263,10 +374,18 @@ public static partial class McpServerBuilderExtensions
}
/// <summary>
/// Sets the handler for list resources requests.
/// Configures a handler for listing resources available from the Model Context Protocol server.
/// </summary>
/// <param name="builder">The builder instance.</param>
/// <param name="handler">The handler.</param>
/// <param name="handler">The handler function that processes resource list requests.</param>
/// <returns>The builder provided in <paramref name="builder"/>.</returns>
/// <exception cref="ArgumentNullException"><paramref name="builder"/> is <see langword="null"/>.</exception>
/// <remarks>
/// <para>
/// This handler is typically paired with <see cref="WithReadResourceHandler"/> to provide a complete resources implementation,
/// where this handler advertises available resources and the read handler provides their content when requested.
/// </para>
/// </remarks>
public static IMcpServerBuilder WithListResourcesHandler(this IMcpServerBuilder builder, Func<RequestContext<ListResourcesRequestParams>, CancellationToken, Task<ListResourcesResult>> handler)
{
Throw.IfNull(builder);
@ -276,10 +395,16 @@ public static partial class McpServerBuilderExtensions
}
/// <summary>
/// Sets the handler for read resources requests.
/// Configures a handler for reading a resource available from the Model Context Protocol server.
/// </summary>
/// <param name="builder">The builder instance.</param>
/// <param name="handler">The handler.</param>
/// <param name="handler">The handler function that processes resource read requests.</param>
/// <returns>The builder provided in <paramref name="builder"/>.</returns>
/// <exception cref="ArgumentNullException"><paramref name="builder"/> is <see langword="null"/>.</exception>
/// <remarks>
/// This handler is typically paired with <see cref="WithListResourcesHandler"/> to provide a complete resources implementation,
/// where the list handler advertises available resources and the read handler provides their content when requested.
/// </remarks>
public static IMcpServerBuilder WithReadResourceHandler(this IMcpServerBuilder builder, Func<RequestContext<ReadResourceRequestParams>, CancellationToken, Task<ReadResourceResult>> handler)
{
Throw.IfNull(builder);
@ -289,10 +414,16 @@ public static partial class McpServerBuilderExtensions
}
/// <summary>
/// Sets the handler for completion complete requests.
/// Configures a handler for auto-completion suggestions for prompt arguments or resource references available from the Model Context Protocol server.
/// </summary>
/// <param name="builder">The builder instance.</param>
/// <param name="handler">The handler.</param>
/// <param name="handler">The handler function that processes completion requests.</param>
/// <returns>The builder provided in <paramref name="builder"/>.</returns>
/// <exception cref="ArgumentNullException"><paramref name="builder"/> is <see langword="null"/>.</exception>
/// <remarks>
/// The completion handler is invoked when clients request suggestions for argument values.
/// This enables auto-complete functionality for both prompt arguments and resource references.
/// </remarks>
public static IMcpServerBuilder WithCompleteHandler(this IMcpServerBuilder builder, Func<RequestContext<CompleteRequestParams>, CancellationToken, Task<CompleteResult>> handler)
{
Throw.IfNull(builder);
@ -302,10 +433,27 @@ public static partial class McpServerBuilderExtensions
}
/// <summary>
/// Sets the handler for subscribe to resources messages.
/// Configures a handler for resource subscription requests.
/// </summary>
/// <param name="builder">The builder instance.</param>
/// <param name="handler">The handler.</param>
/// <param name="handler">The handler function that processes resource subscription requests.</param>
/// <returns>The builder provided in <paramref name="builder"/>.</returns>
/// <exception cref="ArgumentNullException"><paramref name="builder"/> is <see langword="null"/>.</exception>
/// <remarks>
/// <para>
/// The subscribe handler is responsible for registering client interest in specific resources. When a resource
/// changes, the server can notify all subscribed clients about the change.
/// </para>
/// <para>
/// This handler is typically paired with <see cref="WithUnsubscribeFromResourcesHandler"/> to provide a complete
/// subscription management system. Resource subscriptions allow clients to maintain up-to-date information without
/// needing to poll resources constantly.
/// </para>
/// <para>
/// After registering a subscription, it's the server's responsibility to track which client is subscribed to which
/// resources and to send appropriate notifications through the connection when resources change.
/// </para>
/// </remarks>
public static IMcpServerBuilder WithSubscribeToResourcesHandler(this IMcpServerBuilder builder, Func<RequestContext<SubscribeRequestParams>, CancellationToken, Task<EmptyResult>> handler)
{
Throw.IfNull(builder);
@ -315,10 +463,27 @@ public static partial class McpServerBuilderExtensions
}
/// <summary>
/// Sets the handler for subscribe to resources messages.
/// Configures a handler for resource unsubscription requests.
/// </summary>
/// <param name="builder">The builder instance.</param>
/// <param name="handler">The handler.</param>
/// <param name="handler">The handler function that processes resource unsubscription requests.</param>
/// <returns>The builder provided in <paramref name="builder"/>.</returns>
/// <exception cref="ArgumentNullException"><paramref name="builder"/> is <see langword="null"/>.</exception>
/// <remarks>
/// <para>
/// The unsubscribe handler is responsible for removing client interest in specific resources. When a client
/// no longer needs to receive notifications about resource changes, it can send an unsubscribe request.
/// </para>
/// <para>
/// This handler is typically paired with <see cref="WithSubscribeToResourcesHandler"/> to provide a complete
/// subscription management system. The unsubscribe operation is idempotent, meaning it can be called multiple
/// times for the same resource without causing errors, even if there is no active subscription.
/// </para>
/// <para>
/// After removing a subscription, the server should stop sending notifications to the client about changes
/// to the specified resource.
/// </para>
/// </remarks>
public static IMcpServerBuilder WithUnsubscribeFromResourcesHandler(this IMcpServerBuilder builder, Func<RequestContext<UnsubscribeRequestParams>, CancellationToken, Task<EmptyResult>> handler)
{
Throw.IfNull(builder);
@ -328,13 +493,28 @@ public static partial class McpServerBuilderExtensions
}
/// <summary>
/// Sets the handler for setting the logging level.
/// Configures a handler for processing logging level change requests from clients.
/// </summary>
/// <param name="builder">The builder instance.</param>
/// <param name="handler">The handler.</param>
/// <param name="builder">The server builder instance.</param>
/// <param name="handler">The handler that processes requests to change the logging level.</param>
/// <returns>The builder provided in <paramref name="builder"/>.</returns>
/// <exception cref="ArgumentNullException"><paramref name="builder"/> is <see langword="null"/>.</exception>
/// <remarks>
/// <para>
/// When a client sends a <c>logging/setLevel</c> request, this handler will be invoked to process
/// the requested level change. The server typically adjusts its internal logging level threshold
/// and may begin sending log messages at or above the specified level to the client.
/// </para>
/// <para>
/// Regardless of whether a handler is provided, an <see cref="IMcpServer"/> should itself handle
/// such notifications by updating its <see cref="IMcpServer.LoggingLevel"/> property to return the
/// most recently set level.
/// </para>
/// </remarks>
public static IMcpServerBuilder WithSetLoggingLevelHandler(this IMcpServerBuilder builder, Func<RequestContext<SetLevelRequestParams>, CancellationToken, Task<EmptyResult>> handler)
{
Throw.IfNull(builder);
builder.Services.Configure<McpServerHandlers>(s => s.SetLoggingLevelHandler = handler);
return builder;
}
@ -342,9 +522,23 @@ public static partial class McpServerBuilderExtensions
#region Transports
/// <summary>
/// Adds a server transport that uses stdin/stdout for communication.
/// Adds a server transport that uses standard input (stdin) and standard output (stdout) for communication.
/// </summary>
/// <param name="builder">The builder instance.</param>
/// <returns>The builder provided in <paramref name="builder"/>.</returns>
/// <exception cref="ArgumentNullException"><paramref name="builder"/> is <see langword="null"/>.</exception>
/// <remarks>
/// <para>
/// This method configures the server to communicate using the standard input and output streams,
/// which is commonly used when the Model Context Protocol server is launched locally by a client process.
/// </para>
/// <para>
/// When using this transport, the server runs as a single-session service that exits when the
/// stdin stream is closed. This makes it suitable for scenarios where the server should terminate
/// when the parent process disconnects.
/// </para>
/// </remarks>
/// <exception cref="ArgumentNullException"><paramref name="builder"/> is <see langword="null"/>.</exception>
public static IMcpServerBuilder WithStdioServerTransport(this IMcpServerBuilder builder)
{
Throw.IfNull(builder);
@ -361,6 +555,10 @@ public static partial class McpServerBuilderExtensions
/// <param name="builder">The builder instance.</param>
/// <param name="inputStream">The input <see cref="Stream"/> to use as standard input.</param>
/// <param name="outputStream">The output <see cref="Stream"/> to use as standard output.</param>
/// <returns>The builder provided in <paramref name="builder"/>.</returns>
/// <exception cref="ArgumentNullException"><paramref name="builder"/> is <see langword="null"/>.</exception>
/// <exception cref="ArgumentNullException"><paramref name="inputStream"/> is <see langword="null"/>.</exception>
/// <exception cref="ArgumentNullException"><paramref name="outputStream"/> is <see langword="null"/>.</exception>
public static IMcpServerBuilder WithStreamServerTransport(
this IMcpServerBuilder builder,
Stream inputStream,

View file

@ -1,4 +1,4 @@
using ModelContextProtocol.Server;
using ModelContextProtocol.Server;
using Microsoft.Extensions.Options;
using ModelContextProtocol.Utils;

View file

@ -1,20 +1,21 @@
using ModelContextProtocol.Server;
using ModelContextProtocol.Server;
using Microsoft.Extensions.Options;
using Microsoft.Extensions.DependencyInjection.Extensions;
namespace Microsoft.Extensions.DependencyInjection;
/// <summary>
/// Extension to host an MCP server
/// Provides extension methods for configuring MCP servers with dependency injection.
/// </summary>
public static class McpServerServiceCollectionExtensions
{
/// <summary>
/// Adds the MCP server to the service collection with default options.
/// Adds the Model Context Protocol (MCP) server to the service collection with default options.
/// </summary>
/// <param name="services"></param>
/// <param name="configureOptions"></param>
/// <returns></returns>
/// <param name="services">The <see cref="IServiceCollection"/> to add the server to.</param>
/// <param name="configureOptions">Optional callback to configure the <see cref="McpServerOptions"/>.</param>
/// <returns>An <see cref="IMcpServerBuilder"/> that can be used to further configure the MCP server.</returns>
public static IMcpServerBuilder AddMcpServer(this IServiceCollection services, Action<McpServerOptions>? configureOptions = null)
{
services.AddOptions();

View file

@ -1,19 +1,68 @@
using ModelContextProtocol.Protocol.Messages;
using ModelContextProtocol.Client;
using ModelContextProtocol.Protocol.Messages;
using ModelContextProtocol.Server;
namespace ModelContextProtocol;
/// <summary>Represents a client or server MCP endpoint.</summary>
/// <summary>
/// Represents a client or server Model Context Protocol (MCP) endpoint.
/// </summary>
/// <remarks>
/// <para>
/// The MCP endpoint provides the core communication functionality used by both clients and servers:
/// <list type="bullet">
/// <item>Sending JSON-RPC requests and receiving responses.</item>
/// <item>Sending notifications to the connected endpoint.</item>
/// <item>Registering handlers for receiving notifications.</item>
/// </list>
/// </para>
/// <para>
/// <see cref="IMcpEndpoint"/> serves as the base interface for both <see cref="IMcpClient"/> and
/// <see cref="IMcpServer"/> interfaces, providing the common functionality needed for MCP protocol
/// communication. Most applications will use these more specific interfaces rather than working with
/// <see cref="IMcpEndpoint"/> directly.
/// </para>
/// <para>
/// All MCP endpoints should be properly disposed after use as they implement <see cref="IAsyncDisposable"/>.
/// </para>
/// </remarks>
public interface IMcpEndpoint : IAsyncDisposable
{
/// <summary>Sends a JSON-RPC request to the connected endpoint.</summary>
/// <summary>
/// Sends a JSON-RPC request to the connected endpoint and waits for a response.
/// </summary>
/// <param name="request">The JSON-RPC request to send.</param>
/// <param name="cancellationToken">The <see cref="CancellationToken"/> to monitor for cancellation requests. The default is <see cref="CancellationToken.None"/>.</param>
/// <returns>A task containing the client's response.</returns>
/// <returns>A task containing the endpoint's response.</returns>
/// <exception cref="McpException">The transport is not connected, or another error occurs during request processing.</exception>
/// <remarks>
/// This method provides low-level access to send raw JSON-RPC requests. For most use cases,
/// consider using the strongly-typed extension methods that provide a more convenient API.
/// </remarks>
Task<JsonRpcResponse> SendRequestAsync(JsonRpcRequest request, CancellationToken cancellationToken = default);
/// <summary>Sends a message to the connected endpoint.</summary>
/// <param name="message">The message.</param>
/// <summary>
/// Sends a JSON-RPC message to the connected endpoint.
/// </summary>
/// <param name="message">
/// The JSON-RPC message to send. This can be any type that implements IJsonRpcMessage, such as
/// JsonRpcRequest, JsonRpcResponse, JsonRpcNotification, or JsonRpcError.
/// </param>
/// <param name="cancellationToken">The <see cref="CancellationToken"/> to monitor for cancellation requests. The default is <see cref="CancellationToken.None"/>.</param>
/// <returns>A task that represents the asynchronous send operation.</returns>
/// <exception cref="McpException">The transport is not connected.</exception>
/// <exception cref="ArgumentNullException"><paramref name="message"/> is <see langword="null"/>.</exception>
/// <remarks>
/// <para>
/// This method provides low-level access to send any JSON-RPC message. For specific message types,
/// consider using the higher-level methods such as <see cref="SendRequestAsync"/> or extension methods
/// like <see cref="McpEndpointExtensions.SendNotificationAsync(IMcpEndpoint, string, CancellationToken)"/>,
/// which provide a simpler API.
/// </para>
/// <para>
/// The method will serialize the message and transmit it using the underlying transport mechanism.
/// </para>
/// </remarks>
Task SendMessageAsync(IJsonRpcMessage message, CancellationToken cancellationToken = default);
/// <summary>Registers a handler to be invoked when a notification for the specified method is received.</summary>

View file

@ -1,4 +1,4 @@
using ModelContextProtocol.Protocol.Messages;
using ModelContextProtocol.Protocol.Messages;
using Microsoft.Extensions.Logging;
namespace ModelContextProtocol.Logging;

View file

@ -1,4 +1,6 @@
using ModelContextProtocol.Protocol.Messages;
using ModelContextProtocol.Client;
using ModelContextProtocol.Protocol.Messages;
using ModelContextProtocol.Server;
using ModelContextProtocol.Utils;
using ModelContextProtocol.Utils.Json;
using System.Text.Json;
@ -7,7 +9,19 @@ using System.Text.Json.Serialization.Metadata;
namespace ModelContextProtocol;
/// <summary>Provides extension methods for interacting with an <see cref="IMcpEndpoint"/>.</summary>
/// <summary>
/// Provides extension methods for interacting with an <see cref="IMcpEndpoint"/>.
/// </summary>
/// <remarks>
/// <para>
/// This class provides strongly-typed methods for working with the Model Context Protocol (MCP) endpoints,
/// simplifying JSON-RPC communication by handling serialization and deserialization of parameters and results.
/// </para>
/// <para>
/// These extension methods are designed to be used with both client (<see cref="IMcpClient"/>) and
/// server (<see cref="IMcpServer"/>) implementations of the <see cref="IMcpEndpoint"/> interface.
/// </para>
/// </remarks>
public static class McpEndpointExtensions
{
/// <summary>
@ -83,11 +97,19 @@ public static class McpEndpointExtensions
}
/// <summary>
/// Sends a notification to the server with no parameters.
/// Sends a parameterless notification to the connected endpoint.
/// </summary>
/// <param name="client">The client.</param>
/// <param name="client">The MCP client or server instance.</param>
/// <param name="method">The notification method name.</param>
/// <param name="cancellationToken">The <see cref="CancellationToken"/> to monitor for cancellation requests. The default is <see cref="CancellationToken.None"/>.</param>
/// <returns>A task that represents the asynchronous send operation.</returns>
/// <remarks>
/// <para>
/// This method sends a notification without any parameters. Notifications are one-way messages
/// that don't expect a response. They are commonly used for events, status updates, or to signal
/// changes in state.
/// </para>
/// </remarks>
public static Task SendNotificationAsync(this IMcpEndpoint client, string method, CancellationToken cancellationToken = default)
{
Throw.IfNull(client);
@ -96,13 +118,29 @@ public static class McpEndpointExtensions
}
/// <summary>
/// Sends a notification to the server with parameters.
/// Sends a notification with parameters to the connected endpoint.
/// </summary>
/// <typeparam name="TParameters">The type of the notification parameters to serialize.</typeparam>
/// <param name="endpoint">The MCP client or server instance.</param>
/// <param name="method">The JSON-RPC method name to invoke.</param>
/// <param name="parameters">Object representing the request parameters.</param>
/// <param name="serializerOptions">The options governing request serialization.</param>
/// <param name="method">The JSON-RPC method name for the notification.</param>
/// <param name="parameters">Object representing the notification parameters.</param>
/// <param name="serializerOptions">The options governing parameter serialization. If null, default options are used.</param>
/// <param name="cancellationToken">The <see cref="CancellationToken"/> to monitor for cancellation requests. The default is <see cref="CancellationToken.None"/>.</param>
/// <returns>A task that represents the asynchronous send operation.</returns>
/// <remarks>
/// <para>
/// This method sends a notification with parameters to the connected endpoint. Notifications are one-way
/// messages that don't expect a response, commonly used for events, status updates, or signaling changes.
/// </para>
/// <para>
/// The parameters object is serialized to JSON according to the provided serializer options or the default
/// options if none are specified.
/// </para>
/// <para>
/// The Model Context Protocol defines several standard notification methods in <see cref="NotificationMethods"/>,
/// but custom methods can also be used for application-specific notifications.
/// </para>
/// </remarks>
public static Task SendNotificationAsync<TParameters>(
this IMcpEndpoint endpoint,
string method,
@ -140,13 +178,25 @@ public static class McpEndpointExtensions
return endpoint.SendMessageAsync(new JsonRpcNotification { Method = method, Params = parametersJson }, cancellationToken);
}
/// <summary>Notifies the connected endpoint of progress.</summary>
/// <summary>
/// Notifies the connected endpoint of progress for a long-running operation.
/// </summary>
/// <param name="endpoint">The endpoint issuing the notification.</param>
/// <param name="progressToken">The <see cref="ProgressToken"/> identifying the operation.</param>
/// <param name="progress">The progress update to send.</param>
/// <param name="progressToken">The <see cref="ProgressToken"/> identifying the operation for which progress is being reported.</param>
/// <param name="progress">The progress update to send, containing information such as percentage complete or status message.</param>
/// <param name="cancellationToken">The <see cref="CancellationToken"/> to monitor for cancellation requests. The default is <see cref="CancellationToken.None"/>.</param>
/// <returns>A task representing the completion of the operation.</returns>
/// <returns>A task representing the completion of the notification operation (not the operation being tracked).</returns>
/// <exception cref="ArgumentNullException"><paramref name="endpoint"/> is <see langword="null"/>.</exception>
/// <remarks>
/// <para>
/// This method sends a progress notification to the connected endpoint using the Model Context Protocol's
/// standardized progress notification format. Progress updates are identified by a <see cref="ProgressToken"/>
/// that allows the recipient to correlate multiple updates with a specific long-running operation.
/// </para>
/// <para>
/// Progress notifications are sent asynchronously and don't block the operation from continuing.
/// </para>
/// </remarks>
public static Task NotifyProgressAsync(
this IMcpEndpoint endpoint,
ProgressToken progressToken,

View file

@ -1,7 +1,7 @@
namespace ModelContextProtocol;
namespace ModelContextProtocol;
/// <summary>
/// An exception that is thrown when an MCP configuration or protocol error occurs.
/// Represents an exception that is thrown when a Model Context Protocol (MCP) error occurs.
/// </summary>
public class McpException : Exception
{
@ -30,27 +30,40 @@ public class McpException : Exception
}
/// <summary>
/// Initializes a new instance of the <see cref="McpException"/> class with a specified error message and a reference to the inner exception that is the cause of this exception.
/// Initializes a new instance of the <see cref="McpException"/> class with a specified error message and JSON-RPC error code.
/// </summary>
/// <param name="message">The message that describes the error.</param>
/// <param name="errorCode">A JSON-RPC error code, if this exception represents a JSON-RPC error.</param>
/// <param name="errorCode">A JSON-RPC error code from <see cref="Protocol.Messages.ErrorCodes"/> class.</param>
public McpException(string message, int? errorCode) : this(message, null, errorCode)
{
}
/// <summary>
/// Initializes a new instance of the <see cref="McpException"/> class with a specified error message and a reference to the inner exception that is the cause of this exception.
/// Initializes a new instance of the <see cref="McpException"/> class with a specified error message, inner exception, and JSON-RPC error code.
/// </summary>
/// <param name="message">The message that describes the error.</param>
/// <param name="innerException">The exception that is the cause of the current exception, or a null reference if no inner exception is specified.</param>
/// <param name="errorCode">A JSON-RPC error code, if this exception represents a JSON-RPC error.</param>
/// <param name="errorCode">A JSON-RPC error code from <see cref="Protocol.Messages.ErrorCodes"/> class.</param>
public McpException(string message, Exception? innerException, int? errorCode) : base(message, innerException)
{
ErrorCode = errorCode;
}
/// <summary>
/// Gets the error code if this exception was caused by a JSON-RPC error response.
/// Gets the JSON-RPC error code associated with this exception.
/// </summary>
/// <value>
/// A standard JSON-RPC error code, or <see langword="null"/> if the exception wasn't caused by a JSON-RPC error.
/// </value>
/// <remarks>
/// This property contains a standard JSON-RPC error code as defined in the MCP specification. Common error codes include:
/// <list type="bullet">
/// <item><description>-32700: Parse error - Invalid JSON received</description></item>
/// <item><description>-32600: Invalid request - The JSON is not a valid Request object</description></item>
/// <item><description>-32601: Method not found - The method does not exist or is not available</description></item>
/// <item><description>-32602: Invalid params - Invalid method parameters</description></item>
/// <item><description>-32603: Internal error - Internal JSON-RPC error</description></item>
/// </list>
/// </remarks>
public int? ErrorCode { get; }
}

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