|
| 1 | +using ModelContextProtocol.Client; |
| 2 | +using ModelContextProtocol.Protocol; |
| 3 | +using ModelContextProtocol.Server; |
| 4 | +using System.Diagnostics.CodeAnalysis; |
| 5 | +using System.Runtime.CompilerServices; |
| 6 | +using System.Text.Json; |
| 7 | +using System.Text.Json.Serialization.Metadata; |
| 8 | + |
| 9 | +namespace ModelContextProtocol; |
| 10 | + |
| 11 | +/// <summary> |
| 12 | +/// Provides extension methods for interacting with an <see cref="IMcpEndpoint"/>. |
| 13 | +/// </summary> |
| 14 | +/// <remarks> |
| 15 | +/// <para> |
| 16 | +/// This class provides strongly-typed methods for working with the Model Context Protocol (MCP) endpoints, |
| 17 | +/// simplifying JSON-RPC communication by handling serialization and deserialization of parameters and results. |
| 18 | +/// </para> |
| 19 | +/// <para> |
| 20 | +/// These extension methods are designed to be used with both client (<see cref="IMcpClient"/>) and |
| 21 | +/// server (<see cref="IMcpServer"/>) implementations of the <see cref="IMcpEndpoint"/> interface. |
| 22 | +/// </para> |
| 23 | +/// </remarks> |
| 24 | +public static class McpEndpointExtensions |
| 25 | +{ |
| 26 | + /// <summary> |
| 27 | + /// Sends a JSON-RPC request and attempts to deserialize the result to <typeparamref name="TResult"/>. |
| 28 | + /// </summary> |
| 29 | + /// <typeparam name="TParameters">The type of the request parameters to serialize from.</typeparam> |
| 30 | + /// <typeparam name="TResult">The type of the result to deserialize to.</typeparam> |
| 31 | + /// <param name="endpoint">The MCP client or server instance.</param> |
| 32 | + /// <param name="method">The JSON-RPC method name to invoke.</param> |
| 33 | + /// <param name="parameters">Object representing the request parameters.</param> |
| 34 | + /// <param name="requestId">The request id for the request.</param> |
| 35 | + /// <param name="serializerOptions">The options governing request serialization.</param> |
| 36 | + /// <param name="cancellationToken">The <see cref="CancellationToken"/> to monitor for cancellation requests. The default is <see cref="CancellationToken.None"/>.</param> |
| 37 | + /// <returns>A task that represents the asynchronous operation. The task result contains the deserialized result.</returns> |
| 38 | + [Obsolete($"Use {nameof(McpSession)}.{nameof(McpSession.SendRequestAsync)} instead.")] |
| 39 | + public static ValueTask<TResult> SendRequestAsync<TParameters, TResult>( |
| 40 | + this IMcpEndpoint endpoint, |
| 41 | + string method, |
| 42 | + TParameters parameters, |
| 43 | + JsonSerializerOptions? serializerOptions = null, |
| 44 | + RequestId requestId = default, |
| 45 | + CancellationToken cancellationToken = default) |
| 46 | + where TResult : notnull |
| 47 | + => AsSessionOrThrow(endpoint).SendRequestAsync<TParameters, TResult>(method, parameters, serializerOptions, requestId, cancellationToken); |
| 48 | + |
| 49 | + /// <summary> |
| 50 | + /// Sends a parameterless notification to the connected endpoint. |
| 51 | + /// </summary> |
| 52 | + /// <param name="client">The MCP client or server instance.</param> |
| 53 | + /// <param name="method">The notification method name.</param> |
| 54 | + /// <param name="cancellationToken">The <see cref="CancellationToken"/> to monitor for cancellation requests. The default is <see cref="CancellationToken.None"/>.</param> |
| 55 | + /// <returns>A task that represents the asynchronous send operation.</returns> |
| 56 | + /// <remarks> |
| 57 | + /// <para> |
| 58 | + /// This method sends a notification without any parameters. Notifications are one-way messages |
| 59 | + /// that don't expect a response. They are commonly used for events, status updates, or to signal |
| 60 | + /// changes in state. |
| 61 | + /// </para> |
| 62 | + /// </remarks> |
| 63 | + [Obsolete($"Use {nameof(McpSession)}.{nameof(McpSession.SendNotificationAsync)} instead.")] |
| 64 | + public static Task SendNotificationAsync(this IMcpEndpoint client, string method, CancellationToken cancellationToken = default) |
| 65 | + => AsSessionOrThrow(client).SendNotificationAsync(method, cancellationToken); |
| 66 | + |
| 67 | + /// <summary> |
| 68 | + /// Sends a notification with parameters to the connected endpoint. |
| 69 | + /// </summary> |
| 70 | + /// <typeparam name="TParameters">The type of the notification parameters to serialize.</typeparam> |
| 71 | + /// <param name="endpoint">The MCP client or server instance.</param> |
| 72 | + /// <param name="method">The JSON-RPC method name for the notification.</param> |
| 73 | + /// <param name="parameters">Object representing the notification parameters.</param> |
| 74 | + /// <param name="serializerOptions">The options governing parameter serialization. If null, default options are used.</param> |
| 75 | + /// <param name="cancellationToken">The <see cref="CancellationToken"/> to monitor for cancellation requests. The default is <see cref="CancellationToken.None"/>.</param> |
| 76 | + /// <returns>A task that represents the asynchronous send operation.</returns> |
| 77 | + /// <remarks> |
| 78 | + /// <para> |
| 79 | + /// This method sends a notification with parameters to the connected endpoint. Notifications are one-way |
| 80 | + /// messages that don't expect a response, commonly used for events, status updates, or signaling changes. |
| 81 | + /// </para> |
| 82 | + /// <para> |
| 83 | + /// The parameters object is serialized to JSON according to the provided serializer options or the default |
| 84 | + /// options if none are specified. |
| 85 | + /// </para> |
| 86 | + /// <para> |
| 87 | + /// The Model Context Protocol defines several standard notification methods in <see cref="NotificationMethods"/>, |
| 88 | + /// but custom methods can also be used for application-specific notifications. |
| 89 | + /// </para> |
| 90 | + /// </remarks> |
| 91 | + [Obsolete($"Use {nameof(McpSession)}.{nameof(McpSession.SendNotificationAsync)} instead.")] |
| 92 | + public static Task SendNotificationAsync<TParameters>( |
| 93 | + this IMcpEndpoint endpoint, |
| 94 | + string method, |
| 95 | + TParameters parameters, |
| 96 | + JsonSerializerOptions? serializerOptions = null, |
| 97 | + CancellationToken cancellationToken = default) |
| 98 | + => AsSessionOrThrow(endpoint).SendNotificationAsync(method, parameters, serializerOptions, cancellationToken); |
| 99 | + |
| 100 | + /// <summary> |
| 101 | + /// Notifies the connected endpoint of progress for a long-running operation. |
| 102 | + /// </summary> |
| 103 | + /// <param name="endpoint">The endpoint issuing the notification.</param> |
| 104 | + /// <param name="progressToken">The <see cref="ProgressToken"/> identifying the operation for which progress is being reported.</param> |
| 105 | + /// <param name="progress">The progress update to send, containing information such as percentage complete or status message.</param> |
| 106 | + /// <param name="cancellationToken">The <see cref="CancellationToken"/> to monitor for cancellation requests. The default is <see cref="CancellationToken.None"/>.</param> |
| 107 | + /// <returns>A task representing the completion of the notification operation (not the operation being tracked).</returns> |
| 108 | + /// <exception cref="ArgumentNullException"><paramref name="endpoint"/> is <see langword="null"/>.</exception> |
| 109 | + /// <remarks> |
| 110 | + /// <para> |
| 111 | + /// This method sends a progress notification to the connected endpoint using the Model Context Protocol's |
| 112 | + /// standardized progress notification format. Progress updates are identified by a <see cref="ProgressToken"/> |
| 113 | + /// that allows the recipient to correlate multiple updates with a specific long-running operation. |
| 114 | + /// </para> |
| 115 | + /// <para> |
| 116 | + /// Progress notifications are sent asynchronously and don't block the operation from continuing. |
| 117 | + /// </para> |
| 118 | + /// </remarks> |
| 119 | + [Obsolete($"Use {nameof(McpSession)}.{nameof(McpSession.NotifyProgressAsync)} instead.")] |
| 120 | + public static Task NotifyProgressAsync( |
| 121 | + this IMcpEndpoint endpoint, |
| 122 | + ProgressToken progressToken, |
| 123 | + ProgressNotificationValue progress, |
| 124 | + CancellationToken cancellationToken = default) |
| 125 | + => AsSessionOrThrow(endpoint).NotifyProgressAsync(progressToken, progress, cancellationToken); |
| 126 | + |
| 127 | + [MethodImpl(MethodImplOptions.AggressiveInlining)] |
| 128 | +#pragma warning disable CS0618 // Type or member is obsolete |
| 129 | + private static McpSession AsSessionOrThrow(IMcpEndpoint endpoint, [CallerMemberName] string memberName = "") |
| 130 | +#pragma warning restore CS0618 // Type or member is obsolete |
| 131 | + { |
| 132 | + if (endpoint is not McpSession session) |
| 133 | + { |
| 134 | + ThrowInvalidEndpointType(memberName); |
| 135 | + } |
| 136 | + |
| 137 | + return session; |
| 138 | + |
| 139 | + [DoesNotReturn] |
| 140 | + [MethodImpl(MethodImplOptions.NoInlining)] |
| 141 | + static void ThrowInvalidEndpointType(string memberName) |
| 142 | + => throw new InvalidOperationException( |
| 143 | + $"Only arguments assignable to '{nameof(McpSession)}' are supported. " + |
| 144 | + $"Prefer using '{nameof(McpServer)}.{memberName}' instead, as " + |
| 145 | + $"'{nameof(McpEndpointExtensions)}.{memberName}' is obsolete and will be " + |
| 146 | + $"removed in the future."); |
| 147 | + } |
| 148 | +} |
0 commit comments