Add McpClient/Session methods using .Protocol types (#1063)

This commit is contained in:
Stephen Toub 2025-12-05 13:45:12 -05:00 • committed by GitHub
commit 4a84412331
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
53 changed files with 1116 additions and 639 deletions

View file

@ -14,7 +14,7 @@ public sealed class InteractiveTools
[McpServerTool, Description("A simple game where the user has to guess a number between 1 and 10.")] [McpServerTool, Description("A simple game where the user has to guess a number between 1 and 10.")]
public async Task<string> GuessTheNumber( public async Task<string> GuessTheNumber(
McpServer server, // Get the McpServer from DI container McpServer server, // Get the McpServer from DI container
CancellationToken token CancellationToken cancellationToken
) )
{ {
// Check if the client supports elicitation // Check if the client supports elicitation
@ -37,7 +37,7 @@ public sealed class InteractiveTools
{ {
Message = "Do you want to play a game?", Message = "Do you want to play a game?",
RequestedSchema = playSchema RequestedSchema = playSchema
}, token); }, cancellationToken);
// Check if user wants to play // Check if user wants to play
if (playResponse.Action != "accept" || playResponse.Content?["Answer"].ValueKind != JsonValueKind.True) if (playResponse.Action != "accept" || playResponse.Content?["Answer"].ValueKind != JsonValueKind.True)
@ -64,7 +64,7 @@ public sealed class InteractiveTools
{ {
Message = "What is your name?", Message = "What is your name?",
RequestedSchema = nameSchema RequestedSchema = nameSchema
}, token); }, cancellationToken);
if (nameResponse.Action != "accept") if (nameResponse.Action != "accept")
{ {
@ -100,7 +100,7 @@ public sealed class InteractiveTools
{ {
Message = message, Message = message,
RequestedSchema = guessSchema RequestedSchema = guessSchema
}, token); }, cancellationToken);
if (guessResponse.Action != "accept") if (guessResponse.Action != "accept")
{ {
@ -128,7 +128,7 @@ public sealed class InteractiveTools
[McpServerTool, Description("Example tool demonstrating various enum schema types")] [McpServerTool, Description("Example tool demonstrating various enum schema types")]
public async Task<string> EnumExamples( public async Task<string> EnumExamples(
McpServer server, McpServer server,
CancellationToken token CancellationToken cancellationToken
) )
{ {
// Example 1: UntitledSingleSelectEnumSchema - Simple enum without display titles // Example 1: UntitledSingleSelectEnumSchema - Simple enum without display titles
@ -150,7 +150,7 @@ public sealed class InteractiveTools
{ {
Message = "Select a priority level:", Message = "Select a priority level:",
RequestedSchema = prioritySchema RequestedSchema = prioritySchema
}, token); }, cancellationToken);
if (priorityResponse.Action != "accept") if (priorityResponse.Action != "accept")
{ {
@ -184,7 +184,7 @@ public sealed class InteractiveTools
{ {
Message = "Select the issue severity:", Message = "Select the issue severity:",
RequestedSchema = severitySchema RequestedSchema = severitySchema
}, token); }, cancellationToken);
if (severityResponse.Action != "accept") if (severityResponse.Action != "accept")
{ {
@ -218,7 +218,7 @@ public sealed class InteractiveTools
{ {
Message = "Select up to 3 tags:", Message = "Select up to 3 tags:",
RequestedSchema = tagsSchema RequestedSchema = tagsSchema
}, token); }, cancellationToken);
if (tagsResponse.Action != "accept") if (tagsResponse.Action != "accept")
{ {
@ -257,7 +257,7 @@ public sealed class InteractiveTools
{ {
Message = "Select desired features:", Message = "Select desired features:",
RequestedSchema = featuresSchema RequestedSchema = featuresSchema
}, token); }, cancellationToken);
if (featuresResponse.Action != "accept") if (featuresResponse.Action != "accept")
{ {

View file

@ -69,7 +69,7 @@ Clients should check if the server supports logging by checking the <xref:ModelC
[!code-csharp[](samples/client/Program.cs?name=snippet_LoggingCapabilities)] [!code-csharp[](samples/client/Program.cs?name=snippet_LoggingCapabilities)]
If the server supports logging, the client should set the level of log messages it wishes to receive with If the server supports logging, the client should set the level of log messages it wishes to receive with
the <xref:ModelContextProtocol.Client.McpClient.SetLoggingLevel*> method on <xref:ModelContextProtocol.Client.McpClient>. If the client does not set a logging level, the server might choose the <xref:ModelContextProtocol.Client.McpClient.SetLoggingLevelAsync*> method on <xref:ModelContextProtocol.Client.McpClient>. If the client does not set a logging level, the server might choose
to send all log messages or none&mdash;this is not specified in the protocol. So it's important that the client to send all log messages or none&mdash;this is not specified in the protocol. So it's important that the client
sets a logging level to ensure it receives the desired log messages and only those messages. sets a logging level to ensure it receives the desired log messages and only those messages.

View file

@ -30,7 +30,7 @@ if (firstArgument is not null)
if (Enum.TryParse<LoggingLevel>(firstArgument, true, out var loggingLevel)) if (Enum.TryParse<LoggingLevel>(firstArgument, true, out var loggingLevel))
{ {
// <snippet_LoggingLevel> // <snippet_LoggingLevel>
await mcpClient.SetLoggingLevel(loggingLevel); await mcpClient.SetLoggingLevelAsync(loggingLevel);
// </snippet_LoggingLevel> // </snippet_LoggingLevel>
} }
else else

View file

@ -70,7 +70,7 @@ if (tools.Any(t => t.Name == "get_alerts"))
/// </summary> /// </summary>
/// <param name="authorizationUrl">The authorization URL to open in the browser.</param> /// <param name="authorizationUrl">The authorization URL to open in the browser.</param>
/// <param name="redirectUri">The redirect URI where the authorization code will be sent.</param> /// <param name="redirectUri">The redirect URI where the authorization code will be sent.</param>
/// <param name="cancellationToken">The cancellation token.</param> /// <param name="cancellationToken">The <see cref="CancellationToken"/> to monitor for cancellation requests. The default is <see cref="CancellationToken.None"/>.</param>
/// <returns>The authorization code extracted from the callback, or null if the operation failed.</returns> /// <returns>The authorization code extracted from the callback, or null if the operation failed.</returns>
static async Task<string?> HandleAuthorizationUrlAsync(Uri authorizationUrl, Uri redirectUri, CancellationToken cancellationToken) static async Task<string?> HandleAuthorizationUrlAsync(Uri authorizationUrl, Uri redirectUri, CancellationToken cancellationToken)
{ {

View file

@ -161,6 +161,7 @@ public static class AIContentExtensions
/// This method transforms a protocol-specific <see cref="PromptMessage"/> from the Model Context Protocol /// 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. /// into a standard <see cref="ChatMessage"/> object that can be used with AI client libraries.
/// </remarks> /// </remarks>
/// <exception cref="ArgumentNullException"><paramref name="promptMessage"/> is <see langword="null"/>.</exception>
public static ChatMessage ToChatMessage(this PromptMessage promptMessage) public static ChatMessage ToChatMessage(this PromptMessage promptMessage)
{ {
Throw.IfNull(promptMessage); Throw.IfNull(promptMessage);
@ -187,6 +188,7 @@ public static class AIContentExtensions
/// <see cref="ChatRole.Tool"/> message containing a <see cref="FunctionResultContent"/> with result as a /// <see cref="ChatRole.Tool"/> message containing a <see cref="FunctionResultContent"/> with result as a
/// serialized <see cref="JsonElement"/>. /// serialized <see cref="JsonElement"/>.
/// </remarks> /// </remarks>
/// <exception cref="ArgumentNullException"><paramref name="result"/> or <paramref name="callId"/> is <see langword="null"/>.</exception>
public static ChatMessage ToChatMessage(this CallToolResult result, string callId) public static ChatMessage ToChatMessage(this CallToolResult result, string callId)
{ {
Throw.IfNull(result); Throw.IfNull(result);
@ -207,6 +209,7 @@ public static class AIContentExtensions
/// This method transforms protocol-specific <see cref="PromptMessage"/> objects from a Model Context Protocol /// 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. /// prompt result into standard <see cref="ChatMessage"/> objects that can be used with AI client libraries.
/// </remarks> /// </remarks>
/// <exception cref="ArgumentNullException"><paramref name="promptResult"/> is <see langword="null"/>.</exception>
public static IList<ChatMessage> ToChatMessages(this GetPromptResult promptResult) public static IList<ChatMessage> ToChatMessages(this GetPromptResult promptResult)
{ {
Throw.IfNull(promptResult); Throw.IfNull(promptResult);
@ -224,6 +227,7 @@ public static class AIContentExtensions
/// protocol-specific <see cref="PromptMessage"/> objects for the Model Context Protocol system. /// protocol-specific <see cref="PromptMessage"/> objects for the Model Context Protocol system.
/// Only representable content items are processed. /// Only representable content items are processed.
/// </remarks> /// </remarks>
/// <exception cref="ArgumentNullException"><paramref name="chatMessage"/> is <see langword="null"/>.</exception>
public static IList<PromptMessage> ToPromptMessages(this ChatMessage chatMessage) public static IList<PromptMessage> ToPromptMessages(this ChatMessage chatMessage)
{ {
Throw.IfNull(chatMessage); Throw.IfNull(chatMessage);
@ -251,6 +255,7 @@ public static class AIContentExtensions
/// This method converts Model Context Protocol content types to the equivalent Microsoft.Extensions.AI /// 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. /// content types, enabling seamless integration between the protocol and AI client libraries.
/// </remarks> /// </remarks>
/// <exception cref="ArgumentNullException"><paramref name="content"/> is <see langword="null"/>.</exception>
public static AIContent? ToAIContent(this ContentBlock content) public static AIContent? ToAIContent(this ContentBlock content)
{ {
Throw.IfNull(content); Throw.IfNull(content);
@ -294,6 +299,8 @@ public static class AIContentExtensions
/// This method converts Model Context Protocol resource types to the equivalent Microsoft.Extensions.AI /// 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. /// content types, enabling seamless integration between the protocol and AI client libraries.
/// </remarks> /// </remarks>
/// <exception cref="ArgumentNullException"><paramref name="content"/> is <see langword="null"/>.</exception>
/// <exception cref="NotSupportedException">The resource type is not supported.</exception>
public static AIContent ToAIContent(this ResourceContents content) public static AIContent ToAIContent(this ResourceContents content)
{ {
Throw.IfNull(content); Throw.IfNull(content);
@ -325,6 +332,7 @@ public static class AIContentExtensions
/// preserving the type-specific conversion logic for text, images, audio, and resources. /// preserving the type-specific conversion logic for text, images, audio, and resources.
/// </para> /// </para>
/// </remarks> /// </remarks>
/// <exception cref="ArgumentNullException"><paramref name="contents"/> is <see langword="null"/>.</exception>
public static IList<AIContent> ToAIContents(this IEnumerable<ContentBlock> contents) public static IList<AIContent> ToAIContents(this IEnumerable<ContentBlock> contents)
{ {
Throw.IfNull(contents); Throw.IfNull(contents);
@ -347,6 +355,7 @@ public static class AIContentExtensions
/// binary resources become <see cref="DataContent"/> objects. /// binary resources become <see cref="DataContent"/> objects.
/// </para> /// </para>
/// </remarks> /// </remarks>
/// <exception cref="ArgumentNullException"><paramref name="contents"/> is <see langword="null"/>.</exception>
public static IList<AIContent> ToAIContents(this IEnumerable<ResourceContents> contents) public static IList<AIContent> ToAIContents(this IEnumerable<ResourceContents> contents)
{ {
Throw.IfNull(contents); Throw.IfNull(contents);
@ -357,8 +366,11 @@ public static class AIContentExtensions
/// <summary>Creates a new <see cref="ContentBlock"/> from the content of an <see cref="AIContent"/>.</summary> /// <summary>Creates a new <see cref="ContentBlock"/> from the content of an <see cref="AIContent"/>.</summary>
/// <param name="content">The <see cref="AIContent"/> to convert.</param> /// <param name="content">The <see cref="AIContent"/> to convert.</param>
/// <returns>The created <see cref="ContentBlock"/>.</returns> /// <returns>The created <see cref="ContentBlock"/>.</returns>
/// <exception cref="ArgumentNullException"><paramref name="content"/> is <see langword="null"/>.</exception>
public static ContentBlock ToContentBlock(this AIContent content) public static ContentBlock ToContentBlock(this AIContent content)
{ {
Throw.IfNull(content);
ContentBlock contentBlock = content switch ContentBlock contentBlock = content switch
{ {
TextContent textContent => new TextContentBlock TextContent textContent => new TextContentBlock

View file

@ -6,7 +6,7 @@ namespace ModelContextProtocol.Authentication;
/// </summary> /// </summary>
/// <param name="authorizationUri">The authorization URL that the user needs to visit.</param> /// <param name="authorizationUri">The authorization URL that the user needs to visit.</param>
/// <param name="redirectUri">The redirect URI where the authorization code will be sent.</param> /// <param name="redirectUri">The redirect URI where the authorization code will be sent.</param>
/// <param name="cancellationToken">The cancellation token.</param> /// <param name="cancellationToken">The <see cref="CancellationToken"/> to monitor for cancellation requests. The default is <see cref="CancellationToken.None"/>.</param>
/// <returns>A task that represents the asynchronous operation. The task result contains the authorization code if successful, or null if the operation failed or was cancelled.</returns> /// <returns>A task that represents the asynchronous operation. The task result contains the authorization code if successful, or null if the operation failed or was cancelled.</returns>
/// <remarks> /// <remarks>
/// <para> /// <para>

View file

@ -102,7 +102,7 @@ internal sealed partial class ClientOAuthProvider
/// </summary> /// </summary>
/// <param name="authorizationUrl">The authorization URL to handle.</param> /// <param name="authorizationUrl">The authorization URL to handle.</param>
/// <param name="redirectUri">The redirect URI where the authorization code will be sent.</param> /// <param name="redirectUri">The redirect URI where the authorization code will be sent.</param>
/// <param name="cancellationToken">The cancellation token.</param> /// <param name="cancellationToken">The <see cref="CancellationToken"/> to monitor for cancellation requests. The default is <see cref="CancellationToken.None"/>.</param>
/// <returns>The authorization code entered by the user, or null if none was provided.</returns> /// <returns>The authorization code entered by the user, or null if none was provided.</returns>
private static Task<string?> DefaultAuthorizationUrlHandler(Uri authorizationUrl, Uri redirectUri, CancellationToken cancellationToken) private static Task<string?> DefaultAuthorizationUrlHandler(Uri authorizationUrl, Uri redirectUri, CancellationToken cancellationToken)
{ {
@ -135,7 +135,7 @@ internal sealed partial class ClientOAuthProvider
/// </summary> /// </summary>
/// <param name="scheme">The authentication scheme to use.</param> /// <param name="scheme">The authentication scheme to use.</param>
/// <param name="resourceUri">The URI of the resource requiring authentication.</param> /// <param name="resourceUri">The URI of the resource requiring authentication.</param>
/// <param name="cancellationToken">A token to cancel the operation.</param> /// <param name="cancellationToken">The <see cref="CancellationToken"/> to monitor for cancellation requests. The default is <see cref="CancellationToken.None"/>.</param>
/// <returns>An authentication token string, or null if no token could be obtained for the specified scheme.</returns> /// <returns>An authentication token string, or null if no token could be obtained for the specified scheme.</returns>
public async Task<string?> GetCredentialAsync(string scheme, Uri resourceUri, CancellationToken cancellationToken = default) public async Task<string?> GetCredentialAsync(string scheme, Uri resourceUri, CancellationToken cancellationToken = default)
{ {
@ -168,7 +168,7 @@ internal sealed partial class ClientOAuthProvider
/// </summary> /// </summary>
/// <param name="scheme">The authentication scheme that was used when the unauthorized response was received.</param> /// <param name="scheme">The authentication scheme that was used when the unauthorized response was received.</param>
/// <param name="response">The HTTP response that contained the 401 status code.</param> /// <param name="response">The HTTP response that contained the 401 status code.</param>
/// <param name="cancellationToken">A token to cancel the operation.</param> /// <param name="cancellationToken">The <see cref="CancellationToken"/> to monitor for cancellation requests. The default is <see cref="CancellationToken.None"/>.</param>
/// <returns> /// <returns>
/// A result object indicating if the provider was able to handle the unauthorized response, /// A result object indicating if the provider was able to handle the unauthorized response,
/// and the authentication scheme that should be used for the next attempt, if any. /// and the authentication scheme that should be used for the next attempt, if any.
@ -186,7 +186,7 @@ internal sealed partial class ClientOAuthProvider
/// Performs OAuth authorization by selecting an appropriate authorization server and completing the OAuth flow. /// Performs OAuth authorization by selecting an appropriate authorization server and completing the OAuth flow.
/// </summary> /// </summary>
/// <param name="response">The 401 Unauthorized response containing authentication challenge.</param> /// <param name="response">The 401 Unauthorized response containing authentication challenge.</param>
/// <param name="cancellationToken">The cancellation token.</param> /// <param name="cancellationToken">The <see cref="CancellationToken"/> to monitor for cancellation requests. The default is <see cref="CancellationToken.None"/>.</param>
/// <returns>A result object indicating whether authorization was successful.</returns> /// <returns>A result object indicating whether authorization was successful.</returns>
private async Task PerformOAuthAuthorizationAsync( private async Task PerformOAuthAuthorizationAsync(
HttpResponseMessage response, HttpResponseMessage response,
@ -473,7 +473,7 @@ internal sealed partial class ClientOAuthProvider
/// Fetches the protected resource metadata from the provided URL. /// Fetches the protected resource metadata from the provided URL.
/// </summary> /// </summary>
/// <param name="metadataUrl">The URL to fetch the metadata from.</param> /// <param name="metadataUrl">The URL to fetch the metadata from.</param>
/// <param name="cancellationToken">A token to cancel the operation.</param> /// <param name="cancellationToken">The <see cref="CancellationToken"/> to monitor for cancellation requests. The default is <see cref="CancellationToken.None"/>.</param>
/// <returns>The fetched ProtectedResourceMetadata, or null if it couldn't be fetched.</returns> /// <returns>The fetched ProtectedResourceMetadata, or null if it couldn't be fetched.</returns>
private async Task<ProtectedResourceMetadata?> FetchProtectedResourceMetadataAsync(Uri metadataUrl, CancellationToken cancellationToken = default) private async Task<ProtectedResourceMetadata?> FetchProtectedResourceMetadataAsync(Uri metadataUrl, CancellationToken cancellationToken = default)
{ {
@ -488,7 +488,7 @@ internal sealed partial class ClientOAuthProvider
/// Performs dynamic client registration with the authorization server. /// Performs dynamic client registration with the authorization server.
/// </summary> /// </summary>
/// <param name="authServerMetadata">The authorization server metadata.</param> /// <param name="authServerMetadata">The authorization server metadata.</param>
/// <param name="cancellationToken">Cancellation token.</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> /// <returns>A task representing the asynchronous operation.</returns>
private async Task PerformDynamicClientRegistrationAsync( private async Task PerformDynamicClientRegistrationAsync(
AuthorizationServerMetadata authServerMetadata, AuthorizationServerMetadata authServerMetadata,
@ -609,7 +609,7 @@ internal sealed partial class ClientOAuthProvider
/// </summary> /// </summary>
/// <param name="response">The HTTP response containing the WWW-Authenticate header.</param> /// <param name="response">The HTTP response containing the WWW-Authenticate header.</param>
/// <param name="serverUrl">The server URL to verify against the resource metadata.</param> /// <param name="serverUrl">The server URL to verify against the resource metadata.</param>
/// <param name="cancellationToken">A token to cancel the operation.</param> /// <param name="cancellationToken">The <see cref="CancellationToken"/> to monitor for cancellation requests. The default is <see cref="CancellationToken.None"/>.</param>
/// <returns>The resource metadata if the resource matches the server, otherwise throws an exception.</returns> /// <returns>The resource metadata if the resource matches the server, otherwise throws an exception.</returns>
/// <exception cref="InvalidOperationException">Thrown when the response is not a 401, lacks a WWW-Authenticate header, /// <exception cref="InvalidOperationException">Thrown when the response is not a 401, lacks a WWW-Authenticate header,
/// lacks a resource_metadata parameter, the metadata can't be fetched, or the resource URI doesn't match the server URL.</exception> /// lacks a resource_metadata parameter, the metadata can't be fetched, or the resource URI doesn't match the server URL.</exception>

View file

@ -41,6 +41,7 @@ public sealed class HttpClientTransport : IClientTransport, IAsyncDisposable
/// <see langword="true"/> to dispose of <paramref name="httpClient"/> when the transport is disposed; /// <see langword="true"/> to dispose of <paramref name="httpClient"/> when the transport is disposed;
/// <see langword="false"/> if the caller is retaining ownership of the <paramref name="httpClient"/>'s lifetime. /// <see langword="false"/> if the caller is retaining ownership of the <paramref name="httpClient"/>'s lifetime.
/// </param> /// </param>
/// <exception cref="ArgumentNullException"><paramref name="transportOptions"/> or <paramref name="httpClient"/> is <see langword="null"/>.</exception>
public HttpClientTransport(HttpClientTransportOptions transportOptions, HttpClient httpClient, ILoggerFactory? loggerFactory = null, bool ownsHttpClient = false) public HttpClientTransport(HttpClientTransportOptions transportOptions, HttpClient httpClient, ILoggerFactory? loggerFactory = null, bool ownsHttpClient = false)
{ {
Throw.IfNull(transportOptions); Throw.IfNull(transportOptions);

View file

@ -1,8 +1,6 @@
using Microsoft.Extensions.AI; using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Logging;
using ModelContextProtocol.Protocol; using ModelContextProtocol.Protocol;
using ModelContextProtocol.Server; using ModelContextProtocol.Server;
using System.Runtime.CompilerServices;
using System.Text.Json; using System.Text.Json;
using System.Text.Json.Nodes; using System.Text.Json.Nodes;
@ -22,7 +20,7 @@ public abstract partial class McpClient : McpSession
/// <param name="loggerFactory">A logger factory for creating loggers for clients.</param> /// <param name="loggerFactory">A logger factory for creating loggers for clients.</param>
/// <param name="cancellationToken">The <see cref="CancellationToken"/> to monitor for cancellation requests. The default is <see cref="CancellationToken.None"/>.</param> /// <param name="cancellationToken">The <see cref="CancellationToken"/> to monitor for cancellation requests. The default is <see cref="CancellationToken.None"/>.</param>
/// <returns>An <see cref="McpClient"/> that's connected to the specified server.</returns> /// <returns>An <see cref="McpClient"/> that's connected to the specified server.</returns>
/// <exception cref="ArgumentNullException"><paramref name="clientTransport"/> or <paramref name="clientOptions"/> is <see langword="null"/>.</exception> /// <exception cref="ArgumentNullException"><paramref name="clientTransport"/> is <see langword="null"/>.</exception>
public static async Task<McpClient> CreateAsync( public static async Task<McpClient> CreateAsync(
IClientTransport clientTransport, IClientTransport clientTransport,
McpClientOptions? clientOptions = null, McpClientOptions? clientOptions = null,
@ -41,7 +39,12 @@ public abstract partial class McpClient : McpSession
} }
catch catch
{ {
await clientSession.DisposeAsync().ConfigureAwait(false); try
{
await clientSession.DisposeAsync().ConfigureAwait(false);
}
catch { } // allow the original exception to propagate
throw; throw;
} }
@ -55,9 +58,9 @@ public abstract partial class McpClient : McpSession
/// <param name="resumeOptions">The metadata captured from the original session that should be applied when resuming.</param> /// <param name="resumeOptions">The metadata captured from the original session that should be applied when resuming.</param>
/// <param name="clientOptions">Optional client settings that should mirror those used to create the original session.</param> /// <param name="clientOptions">Optional client settings that should mirror those used to create the original session.</param>
/// <param name="loggerFactory">An optional logger factory for diagnostics.</param> /// <param name="loggerFactory">An optional logger factory for diagnostics.</param>
/// <param name="cancellationToken">Token used when establishing the transport connection.</param> /// <param name="cancellationToken">The <see cref="CancellationToken"/> to monitor for cancellation requests. The default is <see cref="CancellationToken.None"/>.</param>
/// <returns>An <see cref="McpClient"/> bound to the resumed session.</returns> /// <returns>An <see cref="McpClient"/> bound to the resumed session.</returns>
/// <exception cref="ArgumentNullException">Thrown when <paramref name="clientTransport"/> or <paramref name="resumeOptions"/> is <see langword="null"/>.</exception> /// <exception cref="ArgumentNullException"><paramref name="clientTransport"/>, <paramref name="resumeOptions"/>, <see cref="ResumeClientSessionOptions.ServerCapabilities"/>, or <see cref="ResumeClientSessionOptions.ServerInfo"/> is <see langword="null"/>.</exception>
public static async Task<McpClient> ResumeSessionAsync( public static async Task<McpClient> ResumeSessionAsync(
IClientTransport clientTransport, IClientTransport clientTransport,
ResumeClientSessionOptions resumeOptions, ResumeClientSessionOptions resumeOptions,
@ -71,10 +74,10 @@ public abstract partial class McpClient : McpSession
Throw.IfNull(resumeOptions.ServerInfo); Throw.IfNull(resumeOptions.ServerInfo);
var transport = await clientTransport.ConnectAsync(cancellationToken).ConfigureAwait(false); var transport = await clientTransport.ConnectAsync(cancellationToken).ConfigureAwait(false);
var endpointName = clientTransport.Name;
var clientSession = new McpClientImpl(transport, endpointName, clientOptions, loggerFactory); McpClientImpl clientSession = new(transport, clientTransport.Name, clientOptions, loggerFactory);
clientSession.ResumeSession(resumeOptions); clientSession.ResumeSession(resumeOptions);
return clientSession; return clientSession;
} }
@ -87,9 +90,31 @@ public abstract partial class McpClient : McpSession
/// <exception cref="McpException">The server cannot be reached or returned an error response.</exception> /// <exception cref="McpException">The server cannot be reached or returned an error response.</exception>
public ValueTask<PingResult> PingAsync(RequestOptions? options = null, CancellationToken cancellationToken = default) public ValueTask<PingResult> PingAsync(RequestOptions? options = null, CancellationToken cancellationToken = default)
{ {
return PingAsync(
new PingRequestParams
{
Meta = options?.GetMetaForRequest()
},
cancellationToken);
}
/// <summary>
/// Sends a ping request to verify server connectivity.
/// </summary>
/// <param name="requestParams">The request parameters to send in the request.</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 ping result.</returns>
/// <exception cref="ArgumentNullException"><paramref name="requestParams"/> is <see langword="null"/>.</exception>
/// <exception cref="McpException">The server cannot be reached or returned an error response.</exception>
public ValueTask<PingResult> PingAsync(
PingRequestParams requestParams,
CancellationToken cancellationToken = default)
{
Throw.IfNull(requestParams);
return SendRequestAsync( return SendRequestAsync(
RequestMethods.Ping, RequestMethods.Ping,
new PingRequestParams { Meta = options?.Meta }, requestParams,
McpJsonUtilities.JsonContext.Default.PingRequestParams, McpJsonUtilities.JsonContext.Default.PingRequestParams,
McpJsonUtilities.JsonContext.Default.PingResult, McpJsonUtilities.JsonContext.Default.PingResult,
cancellationToken: cancellationToken); cancellationToken: cancellationToken);
@ -106,29 +131,49 @@ public abstract partial class McpClient : McpSession
CancellationToken cancellationToken = default) CancellationToken cancellationToken = default)
{ {
List<McpClientTool>? tools = null; List<McpClientTool>? tools = null;
string? cursor = null; ListToolsRequestParams requestParams = new() { Meta = options?.GetMetaForRequest() };
do do
{ {
var toolResults = await SendRequestAsync( var toolResults = await ListToolsAsync(requestParams, cancellationToken).ConfigureAwait(false);
RequestMethods.ToolsList, tools ??= new(toolResults.Tools.Count);
new() { Cursor = cursor, Meta = options?.Meta },
McpJsonUtilities.JsonContext.Default.ListToolsRequestParams,
McpJsonUtilities.JsonContext.Default.ListToolsResult,
cancellationToken: cancellationToken).ConfigureAwait(false);
tools ??= new List<McpClientTool>(toolResults.Tools.Count);
foreach (var tool in toolResults.Tools) foreach (var tool in toolResults.Tools)
{ {
tools.Add(new McpClientTool(this, tool, options?.JsonSerializerOptions)); tools.Add(new(this, tool, options?.JsonSerializerOptions));
} }
cursor = toolResults.NextCursor; requestParams.Cursor = toolResults.NextCursor;
} }
while (cursor is not null); while (requestParams.Cursor is not null);
return tools; return tools;
} }
/// <summary>
/// Retrieves a list of available tools from the server.
/// </summary>
/// <param name="requestParams">The request parameters to send in the request.</param>
/// <param name="cancellationToken">The <see cref="CancellationToken"/> to monitor for cancellation requests. The default is <see cref="CancellationToken.None"/>.</param>
/// <returns>The result of the request as provided by the server.</returns>
/// <exception cref="ArgumentNullException"><paramref name="requestParams"/> is <see langword="null"/>.</exception>
/// <remarks>
/// The <see cref="ListToolsAsync(RequestOptions?, CancellationToken)"/> overload retrieves all tools by automatically handling pagination.
/// This overload works with the lower-level <see cref="ListToolsRequestParams"/> and <see cref="ListToolsResult"/>, returning the raw result from the server.
/// Any pagination needs to be managed by the caller.
/// </remarks>
public ValueTask<ListToolsResult> ListToolsAsync(
ListToolsRequestParams requestParams,
CancellationToken cancellationToken = default)
{
Throw.IfNull(requestParams);
return SendRequestAsync(
RequestMethods.ToolsList,
requestParams,
McpJsonUtilities.JsonContext.Default.ListToolsRequestParams,
McpJsonUtilities.JsonContext.Default.ListToolsResult,
cancellationToken: cancellationToken);
}
/// <summary> /// <summary>
/// Retrieves a list of available prompts from the server. /// Retrieves a list of available prompts from the server.
/// </summary> /// </summary>
@ -140,29 +185,49 @@ public abstract partial class McpClient : McpSession
CancellationToken cancellationToken = default) CancellationToken cancellationToken = default)
{ {
List<McpClientPrompt>? prompts = null; List<McpClientPrompt>? prompts = null;
string? cursor = null; ListPromptsRequestParams requestParams = new() { Meta = options?.GetMetaForRequest() };
do do
{ {
var promptResults = await SendRequestAsync( var promptResults = await ListPromptsAsync(requestParams, cancellationToken).ConfigureAwait(false);
RequestMethods.PromptsList, prompts ??= new(promptResults.Prompts.Count);
new() { Cursor = cursor, Meta = options?.Meta },
McpJsonUtilities.JsonContext.Default.ListPromptsRequestParams,
McpJsonUtilities.JsonContext.Default.ListPromptsResult,
cancellationToken: cancellationToken).ConfigureAwait(false);
prompts ??= new List<McpClientPrompt>(promptResults.Prompts.Count);
foreach (var prompt in promptResults.Prompts) foreach (var prompt in promptResults.Prompts)
{ {
prompts.Add(new McpClientPrompt(this, prompt)); prompts.Add(new(this, prompt));
} }
cursor = promptResults.NextCursor; requestParams.Cursor = promptResults.NextCursor;
} }
while (cursor is not null); while (requestParams.Cursor is not null);
return prompts; return prompts;
} }
/// <summary>
/// Retrieves a list of available prompts from the server.
/// </summary>
/// <param name="requestParams">The request parameters to send in the request.</param>
/// <param name="cancellationToken">The <see cref="CancellationToken"/> to monitor for cancellation requests. The default is <see cref="CancellationToken.None"/>.</param>
/// <returns>The result of the request as provided by the server.</returns>
/// <exception cref="ArgumentNullException"><paramref name="requestParams"/> is <see langword="null"/>.</exception>
/// <remarks>
/// The <see cref="ListPromptsAsync(RequestOptions?, CancellationToken)"/> overload retrieves all prompts by automatically handling pagination.
/// This overload works with the lower-level <see cref="ListPromptsRequestParams"/> and <see cref="ListPromptsResult"/>, returning the raw result from the server.
/// Any pagination needs to be managed by the caller.
/// </remarks>
public ValueTask<ListPromptsResult> ListPromptsAsync(
ListPromptsRequestParams requestParams,
CancellationToken cancellationToken = default)
{
Throw.IfNull(requestParams);
return SendRequestAsync(
RequestMethods.PromptsList,
requestParams,
McpJsonUtilities.JsonContext.Default.ListPromptsRequestParams,
McpJsonUtilities.JsonContext.Default.ListPromptsResult,
cancellationToken: cancellationToken);
}
/// <summary> /// <summary>
/// Retrieves a specific prompt from the MCP server. /// Retrieves a specific prompt from the MCP server.
/// </summary> /// </summary>
@ -171,6 +236,8 @@ public abstract partial class McpClient : McpSession
/// <param name="options">Optional request options including metadata, serialization settings, and progress tracking.</param> /// <param name="options">Optional request options including metadata, serialization settings, and progress tracking.</param>
/// <param name="cancellationToken">The <see cref="CancellationToken"/> to monitor for cancellation requests. The default is <see cref="CancellationToken.None"/>.</param> /// <param name="cancellationToken">The <see cref="CancellationToken"/> to monitor for cancellation requests. The default is <see cref="CancellationToken.None"/>.</param>
/// <returns>A task containing the prompt's result with content and messages.</returns> /// <returns>A task containing the prompt's result with content and messages.</returns>
/// <exception cref="ArgumentNullException"><paramref name="name"/> is <see langword="null"/>.</exception>
/// <exception cref="ArgumentException"><paramref name="name"/> is empty or composed entirely of whitespace.</exception>
public ValueTask<GetPromptResult> GetPromptAsync( public ValueTask<GetPromptResult> GetPromptAsync(
string name, string name,
IReadOnlyDictionary<string, object?>? arguments = null, IReadOnlyDictionary<string, object?>? arguments = null,
@ -182,9 +249,32 @@ public abstract partial class McpClient : McpSession
var serializerOptions = options?.JsonSerializerOptions ?? McpJsonUtilities.DefaultOptions; var serializerOptions = options?.JsonSerializerOptions ?? McpJsonUtilities.DefaultOptions;
serializerOptions.MakeReadOnly(); serializerOptions.MakeReadOnly();
return GetPromptAsync(
new GetPromptRequestParams
{
Name = name,
Arguments = ToArgumentsDictionary(arguments, serializerOptions),
Meta = options?.GetMetaForRequest(),
},
cancellationToken);
}
/// <summary>
/// Retrieves a list of available prompts from the server.
/// </summary>
/// <param name="requestParams">The request parameters to send in the request.</param>
/// <param name="cancellationToken">The <see cref="CancellationToken"/> to monitor for cancellation requests. The default is <see cref="CancellationToken.None"/>.</param>
/// <returns>The result of the request as provided by the server.</returns>
/// <exception cref="ArgumentNullException"><paramref name="requestParams"/> is <see langword="null"/>.</exception>
public ValueTask<GetPromptResult> GetPromptAsync(
GetPromptRequestParams requestParams,
CancellationToken cancellationToken = default)
{
Throw.IfNull(requestParams);
return SendRequestAsync( return SendRequestAsync(
RequestMethods.PromptsGet, RequestMethods.PromptsGet,
new() { Name = name, Arguments = ToArgumentsDictionary(arguments, serializerOptions), Meta = options?.Meta }, requestParams,
McpJsonUtilities.JsonContext.Default.GetPromptRequestParams, McpJsonUtilities.JsonContext.Default.GetPromptRequestParams,
McpJsonUtilities.JsonContext.Default.GetPromptResult, McpJsonUtilities.JsonContext.Default.GetPromptResult,
cancellationToken: cancellationToken); cancellationToken: cancellationToken);
@ -201,30 +291,49 @@ public abstract partial class McpClient : McpSession
CancellationToken cancellationToken = default) CancellationToken cancellationToken = default)
{ {
List<McpClientResourceTemplate>? resourceTemplates = null; List<McpClientResourceTemplate>? resourceTemplates = null;
ListResourceTemplatesRequestParams requestParams = new() { Meta = options?.GetMetaForRequest() };
string? cursor = null;
do do
{ {
var templateResults = await SendRequestAsync( var templateResults = await ListResourceTemplatesAsync(requestParams, cancellationToken).ConfigureAwait(false);
RequestMethods.ResourcesTemplatesList, resourceTemplates ??= new(templateResults.ResourceTemplates.Count);
new() { Cursor = cursor, Meta = options?.Meta },
McpJsonUtilities.JsonContext.Default.ListResourceTemplatesRequestParams,
McpJsonUtilities.JsonContext.Default.ListResourceTemplatesResult,
cancellationToken: cancellationToken).ConfigureAwait(false);
resourceTemplates ??= new List<McpClientResourceTemplate>(templateResults.ResourceTemplates.Count);
foreach (var template in templateResults.ResourceTemplates) foreach (var template in templateResults.ResourceTemplates)
{ {
resourceTemplates.Add(new McpClientResourceTemplate(this, template)); resourceTemplates.Add(new(this, template));
} }
cursor = templateResults.NextCursor; requestParams.Cursor = templateResults.NextCursor;
} }
while (cursor is not null); while (requestParams.Cursor is not null);
return resourceTemplates; return resourceTemplates;
} }
/// <summary>
/// Retrieves a list of available resource templates from the server.
/// </summary>
/// <param name="requestParams">The request parameters to send in the request.</param>
/// <param name="cancellationToken">The <see cref="CancellationToken"/> to monitor for cancellation requests. The default is <see cref="CancellationToken.None"/>.</param>
/// <returns>The result of the request as provided by the server.</returns>
/// <exception cref="ArgumentNullException"><paramref name="requestParams"/> is <see langword="null"/>.</exception>
/// <remarks>
/// The <see cref="ListResourceTemplatesAsync(RequestOptions?, CancellationToken)"/> overload retrieves all resource templates by automatically handling pagination.
/// This overload works with the lower-level <see cref="ListResourceTemplatesRequestParams"/> and <see cref="ListResourceTemplatesResult"/>, returning the raw result from the server.
/// Any pagination needs to be managed by the caller.
/// </remarks>
public ValueTask<ListResourceTemplatesResult> ListResourceTemplatesAsync(
ListResourceTemplatesRequestParams requestParams,
CancellationToken cancellationToken = default)
{
Throw.IfNull(requestParams);
return SendRequestAsync(
RequestMethods.ResourcesTemplatesList,
requestParams,
McpJsonUtilities.JsonContext.Default.ListResourceTemplatesRequestParams,
McpJsonUtilities.JsonContext.Default.ListResourceTemplatesResult,
cancellationToken: cancellationToken);
}
/// <summary> /// <summary>
/// Retrieves a list of available resources from the server. /// Retrieves a list of available resources from the server.
/// </summary> /// </summary>
@ -236,47 +345,62 @@ public abstract partial class McpClient : McpSession
CancellationToken cancellationToken = default) CancellationToken cancellationToken = default)
{ {
List<McpClientResource>? resources = null; List<McpClientResource>? resources = null;
ListResourcesRequestParams requestParams = new() { Meta = options?.GetMetaForRequest() };
string? cursor = null;
do do
{ {
var resourceResults = await SendRequestAsync( var resourceResults = await ListResourcesAsync(requestParams, cancellationToken).ConfigureAwait(false);
RequestMethods.ResourcesList, resources ??= new(resourceResults.Resources.Count);
new() { Cursor = cursor, Meta = options?.Meta },
McpJsonUtilities.JsonContext.Default.ListResourcesRequestParams,
McpJsonUtilities.JsonContext.Default.ListResourcesResult,
cancellationToken: cancellationToken).ConfigureAwait(false);
resources ??= new List<McpClientResource>(resourceResults.Resources.Count);
foreach (var resource in resourceResults.Resources) foreach (var resource in resourceResults.Resources)
{ {
resources.Add(new McpClientResource(this, resource)); resources.Add(new(this, resource));
} }
cursor = resourceResults.NextCursor; requestParams.Cursor = resourceResults.NextCursor;
} }
while (cursor is not null); while (requestParams.Cursor is not null);
return resources; return resources;
} }
/// <summary>
/// Retrieves a list of available resources from the server.
/// </summary>
/// <param name="requestParams">The request parameters to send in the request.</param>
/// <param name="cancellationToken">The <see cref="CancellationToken"/> to monitor for cancellation requests. The default is <see cref="CancellationToken.None"/>.</param>
/// <returns>The result of the request as provided by the server.</returns>
/// <exception cref="ArgumentNullException"><paramref name="requestParams"/> is <see langword="null"/>.</exception>
/// <remarks>
/// The <see cref="ListResourcesAsync(RequestOptions?, CancellationToken)"/> overload retrieves all resources by automatically handling pagination.
/// This overload works with the lower-level <see cref="ListResourcesRequestParams"/> and <see cref="ListResourcesResult"/>, returning the raw result from the server.
/// Any pagination needs to be managed by the caller.
/// </remarks>
public ValueTask<ListResourcesResult> ListResourcesAsync(
ListResourcesRequestParams requestParams,
CancellationToken cancellationToken = default)
{
Throw.IfNull(requestParams);
return SendRequestAsync(
RequestMethods.ResourcesList,
requestParams,
McpJsonUtilities.JsonContext.Default.ListResourcesRequestParams,
McpJsonUtilities.JsonContext.Default.ListResourcesResult,
cancellationToken: cancellationToken);
}
/// <summary> /// <summary>
/// Reads a resource from the server. /// Reads a resource from the server.
/// </summary> /// </summary>
/// <param name="uri">The URI of the resource.</param> /// <param name="uri">The URI of the resource.</param>
/// <param name="options">Optional request options including metadata, serialization settings, and progress tracking.</param> /// <param name="options">Optional request options including metadata, serialization settings, and progress tracking.</param>
/// <param name="cancellationToken">The <see cref="CancellationToken"/> to monitor for cancellation requests. The default is <see cref="CancellationToken.None"/>.</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="uri"/> is <see langword="null"/>.</exception>
public ValueTask<ReadResourceResult> ReadResourceAsync( public ValueTask<ReadResourceResult> ReadResourceAsync(
string uri, RequestOptions? options = null, CancellationToken cancellationToken = default) Uri uri, RequestOptions? options = null, CancellationToken cancellationToken = default)
{ {
Throw.IfNullOrWhiteSpace(uri); Throw.IfNull(uri);
return SendRequestAsync( return ReadResourceAsync(uri.AbsoluteUri, options, cancellationToken);
RequestMethods.ResourcesRead,
new() { Uri = uri, Meta = options?.Meta },
McpJsonUtilities.JsonContext.Default.ReadResourceRequestParams,
McpJsonUtilities.JsonContext.Default.ReadResourceResult,
cancellationToken: cancellationToken);
} }
/// <summary> /// <summary>
@ -285,12 +409,18 @@ public abstract partial class McpClient : McpSession
/// <param name="uri">The URI of the resource.</param> /// <param name="uri">The URI of the resource.</param>
/// <param name="options">Optional request options including metadata, serialization settings, and progress tracking.</param> /// <param name="options">Optional request options including metadata, serialization settings, and progress tracking.</param>
/// <param name="cancellationToken">The <see cref="CancellationToken"/> to monitor for cancellation requests. The default is <see cref="CancellationToken.None"/>.</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="uri"/> is <see langword="null"/>.</exception>
/// <exception cref="ArgumentException"><paramref name="uri"/> is empty or composed entirely of whitespace.</exception>
public ValueTask<ReadResourceResult> ReadResourceAsync( public ValueTask<ReadResourceResult> ReadResourceAsync(
Uri uri, RequestOptions? options = null, CancellationToken cancellationToken = default) string uri, RequestOptions? options = null, CancellationToken cancellationToken = default)
{ {
Throw.IfNull(uri); Throw.IfNullOrWhiteSpace(uri);
return ReadResourceAsync(uri.ToString(), options, cancellationToken); return ReadResourceAsync(new ReadResourceRequestParams
{
Uri = uri,
Meta = options?.GetMetaForRequest(),
}, cancellationToken);
} }
/// <summary> /// <summary>
@ -300,15 +430,39 @@ public abstract partial class McpClient : McpSession
/// <param name="arguments">Arguments to use to format <paramref name="uriTemplate"/>.</param> /// <param name="arguments">Arguments to use to format <paramref name="uriTemplate"/>.</param>
/// <param name="options">Optional request options including metadata, serialization settings, and progress tracking.</param> /// <param name="options">Optional request options including metadata, serialization settings, and progress tracking.</param>
/// <param name="cancellationToken">The <see cref="CancellationToken"/> to monitor for cancellation requests. The default is <see cref="CancellationToken.None"/>.</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="uriTemplate"/> or <paramref name="arguments"/> is <see langword="null"/>.</exception>
/// <exception cref="ArgumentException"><paramref name="uriTemplate"/> is empty or composed entirely of whitespace.</exception>
public ValueTask<ReadResourceResult> ReadResourceAsync( public ValueTask<ReadResourceResult> ReadResourceAsync(
string uriTemplate, IReadOnlyDictionary<string, object?> arguments, RequestOptions? options = null, CancellationToken cancellationToken = default) string uriTemplate, IReadOnlyDictionary<string, object?> arguments, RequestOptions? options = null, CancellationToken cancellationToken = default)
{ {
Throw.IfNullOrWhiteSpace(uriTemplate); Throw.IfNullOrWhiteSpace(uriTemplate);
Throw.IfNull(arguments); Throw.IfNull(arguments);
return ReadResourceAsync(
new ReadResourceRequestParams
{
Uri = UriTemplate.FormatUri(uriTemplate, arguments),
Meta = options?.GetMetaForRequest(),
},
cancellationToken);
}
/// <summary>
/// Reads a resource from the server.
/// </summary>
/// <param name="requestParams">The request parameters to send in the request.</param>
/// <param name="cancellationToken">The <see cref="CancellationToken"/> to monitor for cancellation requests. The default is <see cref="CancellationToken.None"/>.</param>
/// <returns>The result of the request.</returns>
/// <exception cref="ArgumentNullException"><paramref name="requestParams"/> is <see langword="null"/>.</exception>
public ValueTask<ReadResourceResult> ReadResourceAsync(
ReadResourceRequestParams requestParams,
CancellationToken cancellationToken = default)
{
Throw.IfNull(requestParams);
return SendRequestAsync( return SendRequestAsync(
RequestMethods.ResourcesRead, RequestMethods.ResourcesRead,
new() { Uri = UriTemplate.FormatUri(uriTemplate, arguments), Meta = options?.Meta }, requestParams,
McpJsonUtilities.JsonContext.Default.ReadResourceRequestParams, McpJsonUtilities.JsonContext.Default.ReadResourceRequestParams,
McpJsonUtilities.JsonContext.Default.ReadResourceResult, McpJsonUtilities.JsonContext.Default.ReadResourceResult,
cancellationToken: cancellationToken); cancellationToken: cancellationToken);
@ -320,25 +474,64 @@ public abstract partial class McpClient : McpSession
/// <param name="reference">The reference object specifying the type and optional URI or name.</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="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="argumentValue">The current value of the argument, used to filter relevant completions.</param>
/// <param name="options">Optional request options including metadata, serialization settings, and progress tracking.</param>
/// <param name="cancellationToken">The <see cref="CancellationToken"/> to monitor for cancellation requests. The default is <see cref="CancellationToken.None"/>.</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> /// <returns>A <see cref="CompleteResult"/> containing completion suggestions.</returns>
public ValueTask<CompleteResult> CompleteAsync(Reference reference, string argumentName, string argumentValue, CancellationToken cancellationToken = default) /// <exception cref="ArgumentNullException"><paramref name="reference"/> or <paramref name="argumentName"/> is <see langword="null"/>.</exception>
/// <exception cref="ArgumentException"><paramref name="argumentName"/> is empty or composed entirely of whitespace.</exception>
public ValueTask<CompleteResult> CompleteAsync(
Reference reference, string argumentName, string argumentValue,
RequestOptions? options = null, CancellationToken cancellationToken = default)
{ {
Throw.IfNull(reference); Throw.IfNull(reference);
Throw.IfNullOrWhiteSpace(argumentName); Throw.IfNullOrWhiteSpace(argumentName);
return SendRequestAsync( return CompleteAsync(
RequestMethods.CompletionComplete, new CompleteRequestParams
new()
{ {
Ref = reference, Ref = reference,
Argument = new Argument { Name = argumentName, Value = argumentValue } Argument = new() { Name = argumentName, Value = argumentValue },
Meta = options?.GetMetaForRequest(),
}, },
cancellationToken);
}
/// <summary>
/// Requests completion suggestions for a prompt argument or resource reference.
/// </summary>
/// <param name="requestParams">The request parameters to send in the request.</param>
/// <param name="cancellationToken">The <see cref="CancellationToken"/> to monitor for cancellation requests. The default is <see cref="CancellationToken.None"/>.</param>
/// <returns>The result of the request.</returns>
/// <exception cref="ArgumentNullException"><paramref name="requestParams"/> is <see langword="null"/>.</exception>
public ValueTask<CompleteResult> CompleteAsync(
CompleteRequestParams requestParams,
CancellationToken cancellationToken = default)
{
Throw.IfNull(requestParams);
return SendRequestAsync(
RequestMethods.CompletionComplete,
requestParams,
McpJsonUtilities.JsonContext.Default.CompleteRequestParams, McpJsonUtilities.JsonContext.Default.CompleteRequestParams,
McpJsonUtilities.JsonContext.Default.CompleteResult, McpJsonUtilities.JsonContext.Default.CompleteResult,
cancellationToken: cancellationToken); cancellationToken: cancellationToken);
} }
/// <summary>
/// Unsubscribes from a resource on the server to stop receiving notifications about its changes.
/// </summary>
/// <param name="uri">The URI of the resource to which to subscribe.</param>
/// <param name="options">Optional request options including metadata, serialization settings, and progress tracking.</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>
/// <exception cref="ArgumentNullException"><paramref name="uri"/> is <see langword="null"/>.</exception>
public Task SubscribeToResourceAsync(Uri uri, RequestOptions? options = null, CancellationToken cancellationToken = default)
{
Throw.IfNull(uri);
return SubscribeToResourceAsync(uri.AbsoluteUri, options, cancellationToken);
}
/// <summary> /// <summary>
/// Subscribes to a resource on the server to receive notifications when it changes. /// Subscribes to a resource on the server to receive notifications when it changes.
/// </summary> /// </summary>
@ -346,51 +539,42 @@ public abstract partial class McpClient : McpSession
/// <param name="options">Optional request options including metadata, serialization settings, and progress tracking.</param> /// <param name="options">Optional request options including metadata, serialization settings, and progress tracking.</param>
/// <param name="cancellationToken">The <see cref="CancellationToken"/> to monitor for cancellation requests. The default is <see cref="CancellationToken.None"/>.</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> /// <returns>A task that represents the asynchronous operation.</returns>
/// <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 Task SubscribeToResourceAsync(string uri, RequestOptions? options = null, CancellationToken cancellationToken = default) public Task SubscribeToResourceAsync(string uri, RequestOptions? options = null, CancellationToken cancellationToken = default)
{ {
Throw.IfNullOrWhiteSpace(uri); Throw.IfNullOrWhiteSpace(uri);
return SubscribeToResourceAsync(
new SubscribeRequestParams
{
Uri = uri,
Meta = options?.GetMetaForRequest(),
},
cancellationToken);
}
/// <summary>
/// Subscribes to a resource on the server to receive notifications when it changes.
/// </summary>
/// <param name="requestParams">The request parameters to send in the request.</param>
/// <param name="cancellationToken">The <see cref="CancellationToken"/> to monitor for cancellation requests. The default is <see cref="CancellationToken.None"/>.</param>
/// <returns>The result of the request.</returns>
/// <exception cref="ArgumentNullException"><paramref name="requestParams"/> is <see langword="null"/>.</exception>
public Task SubscribeToResourceAsync(
SubscribeRequestParams requestParams,
CancellationToken cancellationToken = default)
{
Throw.IfNull(requestParams);
return SendRequestAsync( return SendRequestAsync(
RequestMethods.ResourcesSubscribe, RequestMethods.ResourcesSubscribe,
new() { Uri = uri, Meta = options?.Meta }, requestParams,
McpJsonUtilities.JsonContext.Default.SubscribeRequestParams, McpJsonUtilities.JsonContext.Default.SubscribeRequestParams,
McpJsonUtilities.JsonContext.Default.EmptyResult, McpJsonUtilities.JsonContext.Default.EmptyResult,
cancellationToken: cancellationToken).AsTask(); cancellationToken: cancellationToken).AsTask();
} }
/// <summary>
/// Subscribes to a resource on the server to receive notifications when it changes.
/// </summary>
/// <param name="uri">The URI of the resource to which to subscribe.</param>
/// <param name="options">Optional request options including metadata, serialization settings, and progress tracking.</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>
public Task SubscribeToResourceAsync(Uri uri, RequestOptions? options = null, CancellationToken cancellationToken = default)
{
Throw.IfNull(uri);
return SubscribeToResourceAsync(uri.ToString(), options, cancellationToken);
}
/// <summary>
/// Unsubscribes from a resource on the server to stop receiving notifications about its changes.
/// </summary>
/// <param name="uri">The URI of the resource to unsubscribe from.</param>
/// <param name="options">Optional request options including metadata, serialization settings, and progress tracking.</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>
public Task UnsubscribeFromResourceAsync(string uri, RequestOptions? options = null, CancellationToken cancellationToken = default)
{
Throw.IfNullOrWhiteSpace(uri);
return SendRequestAsync(
RequestMethods.ResourcesUnsubscribe,
new() { Uri = uri, Meta = options?.Meta },
McpJsonUtilities.JsonContext.Default.UnsubscribeRequestParams,
McpJsonUtilities.JsonContext.Default.EmptyResult,
cancellationToken: cancellationToken).AsTask();
}
/// <summary> /// <summary>
/// Unsubscribes from a resource on the server to stop receiving notifications about its changes. /// Unsubscribes from a resource on the server to stop receiving notifications about its changes.
/// </summary> /// </summary>
@ -398,11 +582,55 @@ public abstract partial class McpClient : McpSession
/// <param name="options">Optional request options including metadata, serialization settings, and progress tracking.</param> /// <param name="options">Optional request options including metadata, serialization settings, and progress tracking.</param>
/// <param name="cancellationToken">The <see cref="CancellationToken"/> to monitor for cancellation requests. The default is <see cref="CancellationToken.None"/>.</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> /// <returns>A task that represents the asynchronous operation.</returns>
/// <exception cref="ArgumentNullException"><paramref name="uri"/> is <see langword="null"/>.</exception>
public Task UnsubscribeFromResourceAsync(Uri uri, RequestOptions? options = null, CancellationToken cancellationToken = default) public Task UnsubscribeFromResourceAsync(Uri uri, RequestOptions? options = null, CancellationToken cancellationToken = default)
{ {
Throw.IfNull(uri); Throw.IfNull(uri);
return UnsubscribeFromResourceAsync(uri.ToString(), options, cancellationToken); return UnsubscribeFromResourceAsync(uri.AbsoluteUri, options, cancellationToken);
}
/// <summary>
/// Unsubscribes from a resource on the server to stop receiving notifications about its changes.
/// </summary>
/// <param name="uri">The URI of the resource to unsubscribe from.</param>
/// <param name="options">Optional request options including metadata, serialization settings, and progress tracking.</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>
/// <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 Task UnsubscribeFromResourceAsync(string uri, RequestOptions? options = null, CancellationToken cancellationToken = default)
{
Throw.IfNullOrWhiteSpace(uri);
return UnsubscribeFromResourceAsync(
new UnsubscribeRequestParams
{
Uri = uri,
Meta = options?.GetMetaForRequest()
},
cancellationToken);
}
/// <summary>
/// Unsubscribes from a resource on the server to stop receiving notifications about its changes.
/// </summary>
/// <param name="requestParams">The request parameters to send in the request.</param>
/// <param name="cancellationToken">The <see cref="CancellationToken"/> to monitor for cancellation requests. The default is <see cref="CancellationToken.None"/>.</param>
/// <returns>The result of the request.</returns>
/// <exception cref="ArgumentNullException"><paramref name="requestParams"/> is <see langword="null"/>.</exception>
public Task UnsubscribeFromResourceAsync(
UnsubscribeRequestParams requestParams,
CancellationToken cancellationToken = default)
{
Throw.IfNull(requestParams);
return SendRequestAsync(
RequestMethods.ResourcesUnsubscribe,
requestParams,
McpJsonUtilities.JsonContext.Default.UnsubscribeRequestParams,
McpJsonUtilities.JsonContext.Default.EmptyResult,
cancellationToken: cancellationToken).AsTask();
} }
/// <summary> /// <summary>
@ -412,8 +640,9 @@ public abstract partial class McpClient : McpSession
/// <param name="arguments">An optional dictionary of arguments to pass to the tool.</param> /// <param name="arguments">An optional dictionary of arguments to pass to the tool.</param>
/// <param name="progress">An optional progress reporter for server notifications.</param> /// <param name="progress">An optional progress reporter for server notifications.</param>
/// <param name="options">Optional request options including metadata, serialization settings, and progress tracking.</param> /// <param name="options">Optional request options including metadata, serialization settings, and progress tracking.</param>
/// <param name="cancellationToken">A cancellation token.</param> /// <param name="cancellationToken">The <see cref="CancellationToken"/> to monitor for cancellation requests. The default is <see cref="CancellationToken.None"/>.</param>
/// <returns>The <see cref="CallToolResult"/> from the tool execution.</returns> /// <returns>The <see cref="CallToolResult"/> from the tool execution.</returns>
/// <exception cref="ArgumentNullException"><paramref name="toolName"/> is <see langword="null"/>.</exception>
public ValueTask<CallToolResult> CallToolAsync( public ValueTask<CallToolResult> CallToolAsync(
string toolName, string toolName,
IReadOnlyDictionary<string, object?>? arguments = null, IReadOnlyDictionary<string, object?>? arguments = null,
@ -422,25 +651,23 @@ public abstract partial class McpClient : McpSession
CancellationToken cancellationToken = default) CancellationToken cancellationToken = default)
{ {
Throw.IfNull(toolName); Throw.IfNull(toolName);
var serializerOptions = options?.JsonSerializerOptions ?? McpJsonUtilities.DefaultOptions; var serializerOptions = options?.JsonSerializerOptions ?? McpJsonUtilities.DefaultOptions;
serializerOptions.MakeReadOnly(); serializerOptions.MakeReadOnly();
if (progress is not null) if (progress is null)
{ {
return SendRequestWithProgressAsync(toolName, arguments, progress, options?.Meta, serializerOptions, cancellationToken); return CallToolAsync(
new CallToolRequestParams
{
Name = toolName,
Arguments = ToArgumentsDictionary(arguments, serializerOptions),
Meta = options?.GetMetaForRequest(),
},
cancellationToken);
} }
return SendRequestAsync( return SendRequestWithProgressAsync(toolName, arguments, progress, options?.GetMetaForRequest(), serializerOptions, cancellationToken);
RequestMethods.ToolsCall,
new()
{
Name = toolName,
Arguments = ToArgumentsDictionary(arguments, serializerOptions),
Meta = options?.Meta,
},
McpJsonUtilities.JsonContext.Default.CallToolRequestParams,
McpJsonUtilities.JsonContext.Default.CallToolResult,
cancellationToken: cancellationToken);
async ValueTask<CallToolResult> SendRequestWithProgressAsync( async ValueTask<CallToolResult> SendRequestWithProgressAsync(
string toolName, string toolName,
@ -464,38 +691,39 @@ public abstract partial class McpClient : McpSession
return default; return default;
}).ConfigureAwait(false); }).ConfigureAwait(false);
var metaWithProgress = meta is not null ? new JsonObject(meta) : new JsonObject(); JsonObject metaWithProgress = meta is not null ? new(meta) : [];
metaWithProgress["progressToken"] = progressToken.ToString(); metaWithProgress["progressToken"] = progressToken.ToString();
return await SendRequestAsync( return await CallToolAsync(
RequestMethods.ToolsCall,
new() new()
{ {
Name = toolName, Name = toolName,
Arguments = ToArgumentsDictionary(arguments, serializerOptions), Arguments = ToArgumentsDictionary(arguments, serializerOptions),
Meta = metaWithProgress, Meta = metaWithProgress,
}, },
McpJsonUtilities.JsonContext.Default.CallToolRequestParams, cancellationToken).ConfigureAwait(false);
McpJsonUtilities.JsonContext.Default.CallToolResult,
cancellationToken: cancellationToken).ConfigureAwait(false);
} }
} }
/// <summary> /// <summary>
/// Sets the logging level for the server to control which log messages are sent to the client. /// Invokes a tool on the server.
/// </summary> /// </summary>
/// <param name="level">The minimum severity level of log messages to receive from the server.</param> /// <param name="requestParams">The request parameters to send in the request.</param>
/// <param name="options">Optional request options including metadata, serialization settings, and progress tracking.</param>
/// <param name="cancellationToken">The <see cref="CancellationToken"/> to monitor for cancellation requests. The default is <see cref="CancellationToken.None"/>.</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> /// <returns>The result of the request.</returns>
public Task SetLoggingLevel(LoggingLevel level, RequestOptions? options = null, CancellationToken cancellationToken = default) /// <exception cref="ArgumentNullException"><paramref name="requestParams"/> is <see langword="null"/>.</exception>
public ValueTask<CallToolResult> CallToolAsync(
CallToolRequestParams requestParams,
CancellationToken cancellationToken = default)
{ {
Throw.IfNull(requestParams);
return SendRequestAsync( return SendRequestAsync(
RequestMethods.LoggingSetLevel, RequestMethods.ToolsCall,
new() { Level = level, Meta = options?.Meta }, requestParams,
McpJsonUtilities.JsonContext.Default.SetLevelRequestParams, McpJsonUtilities.JsonContext.Default.CallToolRequestParams,
McpJsonUtilities.JsonContext.Default.EmptyResult, McpJsonUtilities.JsonContext.Default.CallToolResult,
cancellationToken: cancellationToken).AsTask(); cancellationToken: cancellationToken);
} }
/// <summary> /// <summary>
@ -505,8 +733,47 @@ public abstract partial class McpClient : McpSession
/// <param name="options">Optional request options including metadata, serialization settings, and progress tracking.</param> /// <param name="options">Optional request options including metadata, serialization settings, and progress tracking.</param>
/// <param name="cancellationToken">The <see cref="CancellationToken"/> to monitor for cancellation requests. The default is <see cref="CancellationToken.None"/>.</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> /// <returns>A task representing the asynchronous operation.</returns>
public Task SetLoggingLevel(LogLevel level, RequestOptions? options = null, CancellationToken cancellationToken = default) => public Task SetLoggingLevelAsync(LogLevel level, RequestOptions? options = null, CancellationToken cancellationToken = default) =>
SetLoggingLevel(McpServerImpl.ToLoggingLevel(level), options, cancellationToken); SetLoggingLevelAsync(McpServerImpl.ToLoggingLevel(level), options, cancellationToken);
/// <summary>
/// Sets the logging level for the server to control which log messages are sent to the client.
/// </summary>
/// <param name="level">The minimum severity level of log messages to receive from the server.</param>
/// <param name="options">Optional request options including metadata, serialization settings, and progress tracking.</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>
public Task SetLoggingLevelAsync(LoggingLevel level, RequestOptions? options = null, CancellationToken cancellationToken = default)
{
return SetLoggingLevelAsync(
new SetLevelRequestParams
{
Level = level,
Meta = options?.GetMetaForRequest()
},
cancellationToken);
}
/// <summary>
/// Sets the logging level for the server to control which log messages are sent to the client.
/// </summary>
/// <param name="requestParams">The request parameters to send in the request.</param>
/// <param name="cancellationToken">The <see cref="CancellationToken"/> to monitor for cancellation requests. The default is <see cref="CancellationToken.None"/>.</param>
/// <returns>The result of the request.</returns>
/// <exception cref="ArgumentNullException"><paramref name="requestParams"/> is <see langword="null"/>.</exception>
public Task SetLoggingLevelAsync(
SetLevelRequestParams requestParams,
CancellationToken cancellationToken = default)
{
Throw.IfNull(requestParams);
return SendRequestAsync(
RequestMethods.LoggingSetLevel,
requestParams,
McpJsonUtilities.JsonContext.Default.SetLevelRequestParams,
McpJsonUtilities.JsonContext.Default.EmptyResult,
cancellationToken: cancellationToken).AsTask();
}
/// <summary>Converts a dictionary with <see cref="object"/> values to a dictionary with <see cref="JsonElement"/> values.</summary> /// <summary>Converts a dictionary with <see cref="object"/> values to a dictionary with <see cref="JsonElement"/> values.</summary>
private static Dictionary<string, JsonElement>? ToArgumentsDictionary( private static Dictionary<string, JsonElement>? ToArgumentsDictionary(

View file

@ -157,7 +157,7 @@ internal sealed partial class McpClientImpl : McpClient
{ {
// Send initialize request // Send initialize request
string requestProtocol = _options.ProtocolVersion ?? McpSessionHandler.LatestProtocolVersion; string requestProtocol = _options.ProtocolVersion ?? McpSessionHandler.LatestProtocolVersion;
var initializeResponse = await this.SendRequestAsync( var initializeResponse = await SendRequestAsync(
RequestMethods.Initialize, RequestMethods.Initialize,
new InitializeRequestParams new InitializeRequestParams
{ {

View file

@ -10,7 +10,7 @@ namespace ModelContextProtocol.Client;
/// <para> /// <para>
/// This class provides a client-side wrapper around a prompt defined on an MCP server. It allows /// 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. /// 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="McpClient.ListPromptsAsync"/>. /// Instances of this class are typically obtained by calling <see cref="McpClient.ListPromptsAsync(RequestOptions?, CancellationToken)"/>.
/// </para> /// </para>
/// <para> /// <para>
/// Each prompt has a name and optionally a description, and it can be invoked with arguments /// Each prompt has a name and optionally a description, and it can be invoked with arguments
@ -29,7 +29,7 @@ public sealed class McpClientPrompt
/// <remarks> /// <remarks>
/// <para> /// <para>
/// This constructor enables reusing cached prompt definitions across different <see cref="McpClient"/> instances /// This constructor enables reusing cached prompt definitions across different <see cref="McpClient"/> instances
/// without needing to call <see cref="McpClient.ListPromptsAsync"/> on every reconnect. This is particularly useful /// without needing to call <see cref="McpClient.ListPromptsAsync(RequestOptions?, CancellationToken)"/> on every reconnect. This is particularly useful
/// in scenarios where prompt definitions are stable and network round-trips should be minimized. /// in scenarios where prompt definitions are stable and network round-trips should be minimized.
/// </para> /// </para>
/// <para> /// <para>
@ -83,7 +83,8 @@ public sealed class McpClientPrompt
/// The server will process the request and return a result containing messages or other content. /// The server will process the request and return a result containing messages or other content.
/// </para> /// </para>
/// <para> /// <para>
/// This is a convenience method that internally calls <see cref="McpClient.GetPromptAsync"/> /// This is a convenience method that internally calls
/// <see cref="McpClient.GetPromptAsync(string, IReadOnlyDictionary{string, object?}?, RequestOptions?, CancellationToken)"/>
/// with this prompt's name and arguments. /// with this prompt's name and arguments.
/// </para> /// </para>
/// </remarks> /// </remarks>

View file

@ -9,7 +9,7 @@ namespace ModelContextProtocol.Client;
/// <para> /// <para>
/// This class provides a client-side wrapper around a resource defined on an MCP server. It allows /// This class provides a client-side wrapper around a resource defined on an MCP server. It allows
/// retrieving the resource's content by sending a request to the server with the resource's URI. /// retrieving the resource's content by sending a request to the server with the resource's URI.
/// Instances of this class are typically obtained by calling <see cref="McpClient.ListResourcesAsync"/>. /// Instances of this class are typically obtained by calling <see cref="McpClient.ListResourcesAsync(RequestOptions?, CancellationToken)"/>.
/// </para> /// </para>
/// </remarks> /// </remarks>
public sealed class McpClientResource public sealed class McpClientResource
@ -24,7 +24,7 @@ public sealed class McpClientResource
/// <remarks> /// <remarks>
/// <para> /// <para>
/// This constructor enables reusing cached resource definitions across different <see cref="McpClient"/> instances /// This constructor enables reusing cached resource definitions across different <see cref="McpClient"/> instances
/// without needing to call <see cref="McpClient.ListResourcesAsync"/> on every reconnect. This is particularly useful /// without needing to call <see cref="McpClient.ListResourcesAsync(RequestOptions?, CancellationToken)"/> on every reconnect. This is particularly useful
/// in scenarios where resource definitions are stable and network round-trips should be minimized. /// in scenarios where resource definitions are stable and network round-trips should be minimized.
/// </para> /// </para>
/// <para> /// <para>

View file

@ -9,7 +9,7 @@ namespace ModelContextProtocol.Client;
/// <para> /// <para>
/// This class provides a client-side wrapper around a resource template defined on an MCP server. It allows /// This class provides a client-side wrapper around a resource template defined on an MCP server. It allows
/// retrieving the resource template's content by sending a request to the server with the resource's URI. /// retrieving the resource template's content by sending a request to the server with the resource's URI.
/// Instances of this class are typically obtained by calling <see cref="McpClient.ListResourceTemplatesAsync"/>. /// Instances of this class are typically obtained by calling <see cref="McpClient.ListResourceTemplatesAsync(RequestOptions?, CancellationToken)"/>.
/// </para> /// </para>
/// </remarks> /// </remarks>
public sealed class McpClientResourceTemplate public sealed class McpClientResourceTemplate
@ -24,7 +24,7 @@ public sealed class McpClientResourceTemplate
/// <remarks> /// <remarks>
/// <para> /// <para>
/// This constructor enables reusing cached resource template definitions across different <see cref="McpClient"/> instances /// This constructor enables reusing cached resource template definitions across different <see cref="McpClient"/> instances
/// without needing to call <see cref="McpClient.ListResourceTemplatesAsync"/> on every reconnect. This is particularly useful /// without needing to call <see cref="McpClient.ListResourceTemplatesAsync(RequestOptions?, CancellationToken)"/> on every reconnect. This is particularly useful
/// in scenarios where resource template definitions are stable and network round-trips should be minimized. /// in scenarios where resource template definitions are stable and network round-trips should be minimized.
/// </para> /// </para>
/// <para> /// <para>

View file

@ -19,7 +19,7 @@ namespace ModelContextProtocol.Client;
/// <see cref="WithName"/> and <see cref="WithDescription"/> without changing the underlying tool functionality. /// <see cref="WithName"/> and <see cref="WithDescription"/> without changing the underlying tool functionality.
/// </para> /// </para>
/// <para> /// <para>
/// Typically, you would get instances of this class by calling the <see cref="McpClient.ListToolsAsync"/> /// Typically, you would get instances of this class by calling the <see cref="McpClient.ListToolsAsync(RequestOptions?, CancellationToken)"/>
/// method on an <see cref="McpClient"/> instance. /// method on an <see cref="McpClient"/> instance.
/// </para> /// </para>
/// </remarks> /// </remarks>
@ -49,8 +49,8 @@ public sealed class McpClientTool : AIFunction
/// <remarks> /// <remarks>
/// <para> /// <para>
/// This constructor enables reusing cached tool definitions across different <see cref="McpClient"/> instances /// This constructor enables reusing cached tool definitions across different <see cref="McpClient"/> instances
/// without needing to call <see cref="McpClient.ListToolsAsync"/> on every reconnect. This is particularly useful /// without needing to call <see cref="McpClient.ListToolsAsync(RequestOptions?, CancellationToken)"/> on every reconnect.
/// in scenarios where tool definitions are stable and network round-trips should be minimized. /// This is particularly useful in scenarios where tool definitions are stable and network round-trips should be minimized.
/// </para> /// </para>
/// <para> /// <para>
/// The provided <paramref name="tool"/> must represent a tool that is actually available on the server /// The provided <paramref name="tool"/> must represent a tool that is actually available on the server
@ -275,6 +275,7 @@ public sealed class McpClientTool : AIFunction
/// </para> /// </para>
/// </remarks> /// </remarks>
/// <returns>A new instance of <see cref="McpClientTool"/>, configured with the provided progress instance.</returns> /// <returns>A new instance of <see cref="McpClientTool"/>, configured with the provided progress instance.</returns>
/// <exception cref="ArgumentNullException"><paramref name="progress"/> is <see langword="null"/>.</exception>
public McpClientTool WithProgress(IProgress<ProgressNotificationValue> progress) public McpClientTool WithProgress(IProgress<ProgressNotificationValue> progress)
{ {
Throw.IfNull(progress); Throw.IfNull(progress);

View file

@ -39,6 +39,7 @@ public sealed partial class StdioClientTransport : IClientTransport
/// </summary> /// </summary>
/// <param name="options">Configuration options for the transport, including the command to execute, arguments, working directory, and environment variables.</param> /// <param name="options">Configuration options for the transport, including the command to execute, arguments, working directory, and environment variables.</param>
/// <param name="loggerFactory">A logger factory for creating loggers used for diagnostic output during transport operations.</param> /// <param name="loggerFactory">A logger factory for creating loggers used for diagnostic output during transport operations.</param>
/// <exception cref="ArgumentNullException"><paramref name="options"/> is <see langword="null"/>.</exception>
public StdioClientTransport(StdioClientTransportOptions options, ILoggerFactory? loggerFactory = null) public StdioClientTransport(StdioClientTransportOptions options, ILoggerFactory? loggerFactory = null)
{ {
Throw.IfNull(options); Throw.IfNull(options);

View file

@ -29,6 +29,7 @@ public sealed class StreamClientTransport : IClientTransport
/// Reads from this stream will receive messages from the server. /// Reads from this stream will receive messages from the server.
/// </param> /// </param>
/// <param name="loggerFactory">A logger factory for creating loggers.</param> /// <param name="loggerFactory">A logger factory for creating loggers.</param>
/// <exception cref="ArgumentNullException"><paramref name="serverInput"/> or <paramref name="serverOutput"/> is <see langword="null"/>.</exception>
public StreamClientTransport( public StreamClientTransport(
Stream serverInput, Stream serverOutput, ILoggerFactory? loggerFactory = null) Stream serverInput, Stream serverOutput, ILoggerFactory? loggerFactory = null)
{ {

View file

@ -29,9 +29,13 @@ public abstract partial class McpSession : IAsyncDisposable
serializerOptions ??= McpJsonUtilities.DefaultOptions; serializerOptions ??= McpJsonUtilities.DefaultOptions;
serializerOptions.MakeReadOnly(); serializerOptions.MakeReadOnly();
JsonTypeInfo<TParameters> paramsTypeInfo = serializerOptions.GetTypeInfo<TParameters>(); return SendRequestAsync(
JsonTypeInfo<TResult> resultTypeInfo = serializerOptions.GetTypeInfo<TResult>(); method,
return SendRequestAsync(method, parameters, paramsTypeInfo, resultTypeInfo, requestId, cancellationToken); parameters,
serializerOptions.GetTypeInfo<TParameters>(),
serializerOptions.GetTypeInfo<TResult>(),
requestId,
cancellationToken);
} }
/// <summary> /// <summary>
@ -76,6 +80,8 @@ public abstract partial class McpSession : IAsyncDisposable
/// <param name="method">The notification method name.</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> /// <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> /// <returns>A task that represents the asynchronous send operation.</returns>
/// <exception cref="ArgumentNullException"><paramref name="method"/> is <see langword="null"/>.</exception>
/// <exception cref="ArgumentException"><paramref name="method"/> is empty or composed entirely of whitespace.</exception>
/// <remarks> /// <remarks>
/// <para> /// <para>
/// This method sends a notification without any parameters. Notifications are one-way messages /// This method sends a notification without any parameters. Notifications are one-way messages
@ -86,6 +92,7 @@ public abstract partial class McpSession : IAsyncDisposable
public Task SendNotificationAsync(string method, CancellationToken cancellationToken = default) public Task SendNotificationAsync(string method, CancellationToken cancellationToken = default)
{ {
Throw.IfNullOrWhiteSpace(method); Throw.IfNullOrWhiteSpace(method);
return SendMessageAsync(new JsonRpcNotification { Method = method }, cancellationToken); return SendMessageAsync(new JsonRpcNotification { Method = method }, cancellationToken);
} }
@ -98,6 +105,8 @@ public abstract partial class McpSession : IAsyncDisposable
/// <param name="serializerOptions">The options governing parameter serialization. If null, default options are used.</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> /// <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> /// <returns>A task that represents the asynchronous send operation.</returns>
/// <exception cref="ArgumentNullException"><paramref name="method"/> is <see langword="null"/>.</exception>
/// <exception cref="ArgumentException"><paramref name="method"/> is empty or composed entirely of whitespace.</exception>
/// <remarks> /// <remarks>
/// <para> /// <para>
/// This method sends a notification with parameters to the connected session. Notifications are one-way /// This method sends a notification with parameters to the connected session. Notifications are one-way
@ -153,7 +162,7 @@ public abstract partial class McpSession : IAsyncDisposable
/// <param name="options">Optional request options including metadata, serialization settings, and progress tracking.</param> /// <param name="options">Optional request options including metadata, serialization settings, and progress tracking.</param>
/// <param name="cancellationToken">The <see cref="CancellationToken"/> to monitor for cancellation requests. The default is <see cref="CancellationToken.None"/>.</param> /// <param name="cancellationToken">The <see cref="CancellationToken"/> to monitor for cancellation requests. The default is <see cref="CancellationToken.None"/>.</param>
/// <returns>A task representing the completion of the notification operation (not the operation being tracked).</returns> /// <returns>A task representing the completion of the notification operation (not the operation being tracked).</returns>
/// <exception cref="ArgumentNullException">The current session instance is <see langword="null"/>.</exception> /// <exception cref="ArgumentNullException"><paramref name="progress"/> is <see langword="null"/>.</exception>
/// <remarks> /// <remarks>
/// <para> /// <para>
/// This method sends a progress notification to the connected session using the Model Context Protocol's /// This method sends a progress notification to the connected session using the Model Context Protocol's
@ -170,14 +179,44 @@ public abstract partial class McpSession : IAsyncDisposable
RequestOptions? options = null, RequestOptions? options = null,
CancellationToken cancellationToken = default) CancellationToken cancellationToken = default)
{ {
return SendNotificationAsync( Throw.IfNull(progress);
NotificationMethods.ProgressNotification,
return NotifyProgressAsync(
new ProgressNotificationParams new ProgressNotificationParams
{ {
ProgressToken = progressToken, ProgressToken = progressToken,
Progress = progress, Progress = progress,
Meta = options?.Meta, Meta = options?.GetMetaForRequest(),
}, },
cancellationToken);
}
/// <summary>
/// Notifies the connected session of progress for a long-running operation.
/// </summary>
/// <param name="requestParams">The request parameters to send in the request.</param>
/// <param name="cancellationToken">The <see cref="CancellationToken"/> to monitor for cancellation requests. The default is <see cref="CancellationToken.None"/>.</param>
/// <returns>A task representing the completion of the notification operation (not the operation being tracked).</returns>
/// <exception cref="ArgumentNullException"><paramref name="requestParams"/> is <see langword="null"/>.</exception>
/// <remarks>
/// <para>
/// This method sends a progress notification to the connected session 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 Task NotifyProgressAsync(
ProgressNotificationParams requestParams,
CancellationToken cancellationToken = default)
{
Throw.IfNull(requestParams);
return SendNotificationAsync(
NotificationMethods.ProgressNotification,
requestParams,
McpJsonUtilities.JsonContext.Default.ProgressNotificationParams, McpJsonUtilities.JsonContext.Default.ProgressNotificationParams,
cancellationToken); cancellationToken);
} }

View file

@ -94,7 +94,7 @@ internal sealed class NotificationHandlers
/// </summary> /// </summary>
/// <param name="method">The notification method name to invoke handlers for.</param> /// <param name="method">The notification method name to invoke handlers for.</param>
/// <param name="notification">The notification object to pass to each handler.</param> /// <param name="notification">The notification object to pass to each handler.</param>
/// <param name="cancellationToken">A token that can be used to cancel the operation.</param> /// <param name="cancellationToken">The <see cref="CancellationToken"/> to monitor for cancellation requests. The default is <see cref="CancellationToken.None"/>.</param>
/// <remarks> /// <remarks>
/// Handlers are invoked in reverse order of registration (newest first). /// Handlers are invoked in reverse order of registration (newest first).
/// If any handler throws an exception, all handlers will still be invoked, and an <see cref="AggregateException"/> /// If any handler throws an exception, all handlers will still be invoked, and an <see cref="AggregateException"/>

View file

@ -391,7 +391,7 @@ public sealed class ElicitRequestParams : RequestParams
return psd; return psd;
} }
private static IList<EnumSchemaOption> DeserializeEnumOptions(ref Utf8JsonReader reader) private static List<EnumSchemaOption> DeserializeEnumOptions(ref Utf8JsonReader reader)
{ {
if (reader.TokenType != JsonTokenType.StartArray) if (reader.TokenType != JsonTokenType.StartArray)
{ {

View file

@ -13,6 +13,7 @@ public readonly struct ProgressToken : IEquatable<ProgressToken>
{ {
/// <summary>Initializes a new instance of the <see cref="ProgressToken"/> with a specified value.</summary> /// <summary>Initializes a new instance of the <see cref="ProgressToken"/> with a specified value.</summary>
/// <param name="value">The required ID value.</param> /// <param name="value">The required ID value.</param>
/// <exception cref="ArgumentNullException"><paramref name="value"/> is <see langword="null"/>.</exception>
public ProgressToken(string value) public ProgressToken(string value)
{ {
Throw.IfNull(value); Throw.IfNull(value);

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