Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
41 changes: 2 additions & 39 deletions src/ModelContextProtocol/Client/IMcpClient.cs
Original file line number Diff line number Diff line change
@@ -1,12 +1,11 @@
using ModelContextProtocol.Protocol.Messages;
using ModelContextProtocol.Protocol.Types;
using ModelContextProtocol.Protocol.Types;

namespace ModelContextProtocol.Client;

/// <summary>
/// Represents an instance of an MCP client connecting to a specific server.
/// </summary>
public interface IMcpClient : IAsyncDisposable
public interface IMcpClient : IMcpEndpoint
{
/// <summary>
/// Gets the capabilities supported by the server.
Expand All @@ -24,40 +23,4 @@ public interface IMcpClient : IAsyncDisposable
/// It can be thought of like a "hint" to the model. For example, this information MAY be added to the system prompt.
/// </summary>
string? ServerInstructions { get; }

/// <summary>
/// Adds a handler for server notifications of a specific method.
/// </summary>
/// <param name="method">The notification method to handle.</param>
/// <param name="handler">The async handler function to process notifications.</param>
/// <remarks>
/// <para>
/// Each method may have multiple handlers. Adding a handler for a method that already has one
/// will not replace the existing handler.
/// </para>
/// <para>
/// <see cref="NotificationMethods"> provides constants for common notification methods.</see>
/// </para>
/// </remarks>
void AddNotificationHandler(string method, Func<JsonRpcNotification, Task> handler);

/// <summary>
/// Sends a generic JSON-RPC request to the server.
/// </summary>
/// <typeparam name="TResult">The expected response type.</typeparam>
/// <param name="request">The JSON-RPC request to send.</param>
/// <param name="cancellationToken">A token to cancel the operation.</param>
/// <returns>A task containing the server's response.</returns>
/// <remarks>
/// It is recommended to use the capability-specific methods that use this one in their implementation.
/// Use this method for custom requests or those not yet covered explicitly.
/// </remarks>
Task<TResult> SendRequestAsync<TResult>(JsonRpcRequest request, CancellationToken cancellationToken = default) where TResult : class;

/// <summary>
/// Sends a message to the server.
/// </summary>
/// <param name="message">The message.</param>
/// <param name="cancellationToken">A token to cancel the operation.</param>
Task SendMessageAsync(IJsonRpcMessage message, CancellationToken cancellationToken = default);
}
7 changes: 5 additions & 2 deletions src/ModelContextProtocol/Client/McpClient.cs
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,10 @@ public McpClient(IClientTransport clientTransport, McpClientOptions options, Mcp

SetRequestHandler<CreateMessageRequestParams, CreateMessageResult>(
RequestMethods.SamplingCreateMessage,
(request, ct) => samplingHandler(request, ct));
(request, cancellationToken) => samplingHandler(
request,
request?.Meta?.ProgressToken is { } token ? new TokenProgress(this, token) : NullProgress.Instance,
cancellationToken));
}

if (options.Capabilities?.Roots is { } rootsCapability)
Expand All @@ -54,7 +57,7 @@ public McpClient(IClientTransport clientTransport, McpClientOptions options, Mcp

SetRequestHandler<ListRootsRequestParams, ListRootsResult>(
RequestMethods.RootsList,
(request, ct) => rootsHandler(request, ct));
(request, cancellationToken) => rootsHandler(request, cancellationToken));
}
}

Expand Down
28 changes: 21 additions & 7 deletions src/ModelContextProtocol/Client/McpClientExtensions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -8,9 +8,7 @@

namespace ModelContextProtocol.Client;

/// <summary>
/// Provides extensions for operating on MCP clients.
/// </summary>
/// <summary>Provides extension methods for interacting with an <see cref="IMcpClient"/>.</summary>
public static class McpClientExtensions
{
/// <summary>
Expand Down Expand Up @@ -531,17 +529,33 @@ internal static CreateMessageResult ToCreateMessageResult(this ChatResponse chat
/// </summary>
/// <param name="chatClient">The <see cref="IChatClient"/> with which to satisfy sampling requests.</param>
/// <returns>The created handler delegate.</returns>
public static Func<CreateMessageRequestParams?, CancellationToken, Task<CreateMessageResult>> CreateSamplingHandler(this IChatClient chatClient)
public static Func<CreateMessageRequestParams?, IProgress<ProgressNotificationValue>, CancellationToken, Task<CreateMessageResult>> CreateSamplingHandler(
this IChatClient chatClient)
{
Throw.IfNull(chatClient);

return async (requestParams, cancellationToken) =>
return async (requestParams, progress, cancellationToken) =>
{
Throw.IfNull(requestParams);

var (messages, options) = requestParams.ToChatClientArguments();
var response = await chatClient.GetResponseAsync(messages, options, cancellationToken).ConfigureAwait(false);
return response.ToCreateMessageResult();
var progressToken = requestParams.Meta?.ProgressToken;

List<ChatResponseUpdate> updates = [];
await foreach (var update in chatClient.GetStreamingResponseAsync(messages, options, cancellationToken))
{
updates.Add(update);

if (progressToken is not null)
{
progress.Report(new()
{
Progress = updates.Count,
});
}
}

return updates.ToChatResponse().ToCreateMessageResult();
};
}

Expand Down
35 changes: 35 additions & 0 deletions src/ModelContextProtocol/IMcpEndpoint.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
using ModelContextProtocol.Protocol.Messages;

namespace ModelContextProtocol;

/// <summary>Represents a client or server MCP endpoint.</summary>
public interface IMcpEndpoint : IAsyncDisposable
{
/// <summary>Sends a generic JSON-RPC request to the connected endpoint.</summary>
/// <typeparam name="TResult">The expected response type.</typeparam>
/// <param name="request">The JSON-RPC request to send.</param>
/// <param name="cancellationToken">A token to cancel the operation.</param>
/// <returns>A task containing the client's response.</returns>
Task<TResult> SendRequestAsync<TResult>(JsonRpcRequest request, CancellationToken cancellationToken = default) where TResult : class;

/// <summary>Sends a message to the connected endpoint.</summary>
/// <param name="message">The message.</param>
/// <param name="cancellationToken">A token to cancel the operation.</param>
Task SendMessageAsync(IJsonRpcMessage message, CancellationToken cancellationToken = default);

/// <summary>
/// Adds a handler for server notifications of a specific method.
/// </summary>
/// <param name="method">The notification method to handle.</param>
/// <param name="handler">The async handler function to process notifications.</param>
/// <remarks>
/// <para>
/// Each method may have multiple handlers. Adding a handler for a method that already has one
/// will not replace the existing handler.
/// </para>
/// <para>
/// <see cref="NotificationMethods"> provides constants for common notification methods.</see>
/// </para>
/// </remarks>
void AddNotificationHandler(string method, Func<JsonRpcNotification, Task> handler);
}
34 changes: 34 additions & 0 deletions src/ModelContextProtocol/McpEndpointExtensions.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
using ModelContextProtocol.Protocol.Messages;
using ModelContextProtocol.Utils;

namespace ModelContextProtocol;

/// <summary>Provides extension methods for interacting with an <see cref="IMcpEndpoint"/>.</summary>
public static class McpEndpointExtensions
{
/// <summary>Notifies the connected endpoint of progress.</summary>
/// <param name="endpoint">The endpoint issueing the notification.</param>
/// <param name="progressToken">The <see cref="ProgressToken"/> identifying the operation.</param>
/// <param name="progress">The progress update to send.</param>
/// <param name="cancellationToken">A token to cancel the operation.</param>
/// <returns>A task representing the completion of the operation.</returns>
/// <exception cref="ArgumentNullException"><paramref name="endpoint"/> is <see langword="null"/>.</exception>
public static Task NotifyProgressAsync(
this IMcpEndpoint endpoint,
ProgressToken progressToken,
ProgressNotificationValue progress,
CancellationToken cancellationToken = default)
{
Throw.IfNull(endpoint);

return endpoint.SendMessageAsync(new JsonRpcNotification()
{
Method = NotificationMethods.ProgressNotification,
Params = new ProgressNotification()
{
ProgressToken = progressToken,
Progress = progress,
},
}, cancellationToken);
}
}
2 changes: 1 addition & 1 deletion src/ModelContextProtocol/Protocol/Types/Capabilities.cs
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,7 @@ public class SamplingCapability

/// <summary>Gets or sets the handler for sampling requests.</summary>
[JsonIgnore]
public Func<CreateMessageRequestParams?, CancellationToken, Task<CreateMessageResult>>? SamplingHandler { get; set; }
public Func<CreateMessageRequestParams?, IProgress<ProgressNotificationValue>, CancellationToken, Task<CreateMessageResult>>? SamplingHandler { get; set; }
}

/// <summary>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,12 +4,4 @@
/// Sent from the client to request a list of prompts and prompt templates the server has.
/// <see href="https://github.com/modelcontextprotocol/specification/blob/main/schema/">See the schema for details</see>
/// </summary>
public class ListPromptsRequestParams
{
/// <summary>
/// An opaque token representing the current pagination position.
/// If provided, the server should return results starting after this cursor.
/// </summary>
[System.Text.Json.Serialization.JsonPropertyName("cursor")]
public string? Cursor { get; init; }
}
public class ListPromptsRequestParams : PaginatedRequestParams;
Original file line number Diff line number Diff line change
Expand Up @@ -4,12 +4,4 @@
/// Sent from the client to request a list of resource templates the server has.
/// <see href="https://github.com/modelcontextprotocol/specification/blob/main/schema/">See the schema for details</see>
/// </summary>
public class ListResourceTemplatesRequestParams
{
/// <summary>
/// An opaque token representing the current pagination position.
/// If provided, the server should return results starting after this cursor.
/// </summary>
[System.Text.Json.Serialization.JsonPropertyName("cursor")]
public string? Cursor { get; init; }
}
public class ListResourceTemplatesRequestParams : PaginatedRequestParams;
Original file line number Diff line number Diff line change
Expand Up @@ -4,12 +4,4 @@
/// Sent from the client to request a list of resources the server has.
/// <see href="https://github.com/modelcontextprotocol/specification/blob/main/schema/">See the schema for details</see>
/// </summary>
public class ListResourcesRequestParams
{
/// <summary>
/// An opaque token representing the current pagination position.
/// If provided, the server should return results starting after this cursor.
/// </summary>
[System.Text.Json.Serialization.JsonPropertyName("cursor")]
public string? Cursor { get; init; }
}
public class ListResourcesRequestParams : PaginatedRequestParams;
Original file line number Diff line number Diff line change
Expand Up @@ -6,11 +6,4 @@ namespace ModelContextProtocol.Protocol.Types;
/// A request from the server to get a list of root URIs from the client.
/// <see href="https://github.com/modelcontextprotocol/specification/blob/main/schema/">See the schema for details</see>
/// </summary>
public class ListRootsRequestParams
{
/// <summary>
/// Optional progress token for out-of-band progress notifications.
/// </summary>
[System.Text.Json.Serialization.JsonPropertyName("progressToken")]
public ProgressToken? ProgressToken { get; init; }
}
public class ListRootsRequestParams : RequestParams;
Original file line number Diff line number Diff line change
Expand Up @@ -4,12 +4,4 @@
/// Sent from the client to request a list of tools the server has.
/// <see href="https://github.com/modelcontextprotocol/specification/blob/main/schema/">See the schema for details</see>
/// </summary>
public class ListToolsRequestParams
{
/// <summary>
/// An opaque token representing the current pagination position.
/// If provided, the server should return results starting after this cursor.
/// </summary>
[System.Text.Json.Serialization.JsonPropertyName("cursor")]
public string? Cursor { get; init; }
}
public class ListToolsRequestParams : PaginatedRequestParams;
15 changes: 15 additions & 0 deletions src/ModelContextProtocol/Protocol/Types/PaginatedRequest.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
namespace ModelContextProtocol.Protocol.Types;

/// <summary>
/// Used as a base class for paginated requests.
/// <see href="https://github.com/modelcontextprotocol/specification/blob/main/schema/2024-11-05/schema.json">See the schema for details</see>
/// </summary>
public class PaginatedRequestParams : RequestParams
{
/// <summary>
/// An opaque token representing the current pagination position.
/// If provided, the server should return results starting after this cursor.
/// </summary>
[System.Text.Json.Serialization.JsonPropertyName("cursor")]
public string? Cursor { get; init; }
}
2 changes: 1 addition & 1 deletion src/ModelContextProtocol/Protocol/Types/Tool.cs
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ public JsonElement InputSchema
{
if (!McpJsonUtilities.IsValidMcpToolSchema(value))
{
throw new ArgumentException("The specified document is not a valid MPC tool JSON schema.", nameof(InputSchema));
throw new ArgumentException("The specified document is not a valid MCP tool JSON schema.", nameof(InputSchema));
}

_inputSchema = value;
Expand Down
39 changes: 2 additions & 37 deletions src/ModelContextProtocol/Server/IMcpServer.cs
Original file line number Diff line number Diff line change
@@ -1,12 +1,11 @@
using ModelContextProtocol.Protocol.Messages;
using ModelContextProtocol.Protocol.Types;
using ModelContextProtocol.Protocol.Types;

namespace ModelContextProtocol.Server;

/// <summary>
/// Represents a server that can communicate with a client using the MCP protocol.
/// </summary>
public interface IMcpServer : IAsyncDisposable
public interface IMcpServer : IMcpEndpoint
{
/// <summary>
/// Gets the capabilities supported by the client.
Expand All @@ -26,42 +25,8 @@ public interface IMcpServer : IAsyncDisposable
/// </summary>
IServiceProvider? Services { get; }

/// <summary>
/// Adds a handler for client notifications of a specific method.
/// </summary>
/// <param name="method">The notification method to handle.</param>
/// <param name="handler">The async handler function to process notifications.</param>
/// <remarks>
/// <para>
/// Each method may have multiple handlers. Adding a handler for a method that already has one
/// will not replace the existing handler.
/// </para>
/// <para>
/// <see cref="NotificationMethods"> provides constants for common notification methods.</see>
/// </para>
/// </remarks>
void AddNotificationHandler(string method, Func<JsonRpcNotification, Task> handler);

/// <summary>
/// Runs the server, listening for and handling client requests.
/// </summary>
Task RunAsync(CancellationToken cancellationToken = default);

/// <summary>
/// Sends a generic JSON-RPC request to the client.
/// NB! This is a temporary method that is available to send not yet implemented feature messages.
/// Once all MCP features are implemented this will be made private, as it is purely a convenience for those who wish to implement features ahead of the library.
/// </summary>
/// <typeparam name="TResult">The expected response type.</typeparam>
/// <param name="request">The JSON-RPC request to send.</param>
/// <param name="cancellationToken">A token to cancel the operation.</param>
/// <returns>A task containing the client's response.</returns>
Task<TResult> SendRequestAsync<TResult>(JsonRpcRequest request, CancellationToken cancellationToken = default) where TResult : class;

/// <summary>
/// Sends a message to the client.
/// </summary>
/// <param name="message">The message.</param>
/// <param name="cancellationToken">A token to cancel the operation.</param>
Task SendMessageAsync(IJsonRpcMessage message, CancellationToken cancellationToken = default);
}
1 change: 0 additions & 1 deletion src/ModelContextProtocol/Server/McpServer.cs
Original file line number Diff line number Diff line change
Expand Up @@ -68,7 +68,6 @@ public McpServer(ITransport transport, McpServerOptions options, ILoggerFactory?
});

SetToolsHandler(options);

SetInitializeHandler(options);
SetCompletionHandler(options);
SetPingHandler();
Expand Down
2 changes: 1 addition & 1 deletion src/ModelContextProtocol/Server/McpServerExtensions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@

namespace ModelContextProtocol.Server;

/// <inheritdoc />
/// <summary>Provides extension methods for interacting with an <see cref="IMcpServer"/>.</summary>
public static class McpServerExtensions
{
/// <summary>
Expand Down
Loading