From fd5d22b8c502b794aa43518c67da057d33b7996f Mon Sep 17 00:00:00 2001 From: Mackinnon Buck Date: Thu, 16 Oct 2025 15:36:46 -0700 Subject: [PATCH] Audit --- .../StreamableHttpHandler.cs | 1 + .../AIContentExtensions.cs | 1 + .../McpSessionHandler.cs | 2 +- .../Protocol/Annotations.cs | 4 +- .../Protocol/Argument.cs | 6 +-- .../Protocol/BlobResourceContents.cs | 4 +- .../Protocol/CallToolRequestParams.cs | 4 +- .../Protocol/CancelledNotificationParams.cs | 2 +- .../Protocol/CompleteContext.cs | 2 +- .../Protocol/CompleteRequestParams.cs | 6 +-- .../Protocol/ContentBlock.cs | 38 ++++++++++--------- .../Protocol/CreateMessageRequestParams.cs | 16 ++++---- .../Protocol/CreateMessageResult.cs | 8 ++-- .../Protocol/ElicitRequestParams.cs | 2 +- .../Protocol/ElicitResult.cs | 19 ++++++++++ .../Protocol/GetPromptRequestParams.cs | 4 +- .../Protocol/Icon.cs | 10 ++--- .../Protocol/InitializeRequestParams.cs | 6 +-- .../Protocol/InitializeResult.cs | 8 ++-- .../Protocol/JsonRpcError.cs | 2 +- .../Protocol/JsonRpcErrorDetail.cs | 8 ++-- .../Protocol/JsonRpcMessage.cs | 3 +- .../Protocol/JsonRpcMessageWithId.cs | 2 +- .../Protocol/JsonRpcNotification.cs | 4 +- .../Protocol/JsonRpcRequest.cs | 16 +------- .../Protocol/JsonRpcResponse.cs | 2 +- .../Protocol/ListRootsResult.cs | 2 +- .../LoggingMessageNotificationParams.cs | 8 ++-- .../Protocol/ModelHint.cs | 4 +- .../Protocol/ModelPreferences.cs | 8 ++-- .../Protocol/PaginatedRequest.cs | 4 +- .../Protocol/ProgressNotificationParams.cs | 4 +- .../Protocol/PromptArgument.cs | 2 +- ...n .cs => PromptListChangedNotification.cs} | 0 .../Protocol/PromptMessage.cs | 4 +- .../Protocol/ReadResourceRequestParams.cs | 4 +- .../Protocol/Reference.cs | 18 ++++----- .../Protocol/RequestParamsMetadata.cs | 2 +- .../Protocol/Resource.cs | 14 ++++--- .../Protocol/ResourceContents.cs | 6 ++- .../Protocol/ResourceTemplate.cs | 10 ++--- .../ResourceUpdatedNotificationParams.cs | 2 +- .../Protocol/Result.cs | 4 +- .../Protocol/Root.cs | 10 +++-- .../Protocol/SamplingMessage.cs | 4 +- .../Protocol/SetLevelRequestParams.cs | 2 +- .../Protocol/SubscribeRequestParams.cs | 2 +- .../Protocol/TextResourceContents.cs | 2 +- .../Protocol/UnsubscribeRequestParams.cs | 2 +- .../Server/McpServer.Methods.cs | 2 +- .../MapMcpTests.cs | 1 + .../StatelessServerTests.cs | 20 ++++++++-- .../Client/McpClientExtensionsTests.cs | 9 +++++ .../Client/McpClientResourceTemplateTests.cs | 2 +- .../Client/McpClientTests.cs | 8 ++-- .../McpServerBuilderExtensionsFilterTests.cs | 2 +- ...cpServerBuilderExtensionsResourcesTests.cs | 2 +- .../McpServerOptionsSetupTests.cs | 26 ++++++------- .../McpEndpointExtensionsTests.cs | 1 + .../Server/McpServerExtensionsTests.cs | 13 ++++++- .../Server/McpServerResourceTests.cs | 8 ++-- .../Server/McpServerTests.cs | 22 +++++------ 62 files changed, 230 insertions(+), 184 deletions(-) rename src/ModelContextProtocol.Core/Protocol/{PromptListChangedNotification .cs => PromptListChangedNotification.cs} (100%) diff --git a/src/ModelContextProtocol.AspNetCore/StreamableHttpHandler.cs b/src/ModelContextProtocol.AspNetCore/StreamableHttpHandler.cs index 9f4af7ea5..9528ddba7 100644 --- a/src/ModelContextProtocol.AspNetCore/StreamableHttpHandler.cs +++ b/src/ModelContextProtocol.AspNetCore/StreamableHttpHandler.cs @@ -250,6 +250,7 @@ private static Task WriteJsonRpcErrorAsync(HttpContext context, string errorMess { var jsonRpcError = new JsonRpcError { + Id = default, Error = new() { Code = errorCode, diff --git a/src/ModelContextProtocol.Core/AIContentExtensions.cs b/src/ModelContextProtocol.Core/AIContentExtensions.cs index b792bc1af..8686b7b6a 100644 --- a/src/ModelContextProtocol.Core/AIContentExtensions.cs +++ b/src/ModelContextProtocol.Core/AIContentExtensions.cs @@ -226,6 +226,7 @@ internal static ContentBlock ToContent(this AIContent content) => { Blob = dataContent.Base64Data.ToString(), MimeType = dataContent.MediaType, + Uri = string.Empty, } }, diff --git a/src/ModelContextProtocol.Core/McpSessionHandler.cs b/src/ModelContextProtocol.Core/McpSessionHandler.cs index fcd7980d9..9a1092e31 100644 --- a/src/ModelContextProtocol.Core/McpSessionHandler.cs +++ b/src/ModelContextProtocol.Core/McpSessionHandler.cs @@ -411,7 +411,7 @@ public async Task SendRequestAsync(JsonRpcRequest request, Canc // Set request ID if (request.Id.Id is null) { - request = request.WithId(new RequestId(Interlocked.Increment(ref _lastRequestId))); + request.Id = new RequestId(Interlocked.Increment(ref _lastRequestId)); } _propagator.InjectActivityContext(activity, request); diff --git a/src/ModelContextProtocol.Core/Protocol/Annotations.cs b/src/ModelContextProtocol.Core/Protocol/Annotations.cs index 6f90182da..0963b4dd7 100644 --- a/src/ModelContextProtocol.Core/Protocol/Annotations.cs +++ b/src/ModelContextProtocol.Core/Protocol/Annotations.cs @@ -15,7 +15,7 @@ public sealed class Annotations /// Gets or sets the intended audience for this content as an array of values. /// [JsonPropertyName("audience")] - public IList? Audience { get; init; } + public IList? Audience { get; set; } /// /// Gets or sets a value indicating how important this data is for operating the server. @@ -25,7 +25,7 @@ public sealed class Annotations /// 1 represents highest priority. /// [JsonPropertyName("priority")] - public float? Priority { get; init; } + public float? Priority { get; set; } /// /// Gets or sets the moment the resource was last modified. diff --git a/src/ModelContextProtocol.Core/Protocol/Argument.cs b/src/ModelContextProtocol.Core/Protocol/Argument.cs index f49692941..edee4e18d 100644 --- a/src/ModelContextProtocol.Core/Protocol/Argument.cs +++ b/src/ModelContextProtocol.Core/Protocol/Argument.cs @@ -15,7 +15,7 @@ public sealed class Argument /// Gets or sets the name of the argument being completed. /// [JsonPropertyName("name")] - public string Name { get; set; } = string.Empty; + public required string Name { get; set; } /// /// Gets or sets the current partial text value for which completion suggestions are requested. @@ -25,5 +25,5 @@ public sealed class Argument /// options should be generated. /// [JsonPropertyName("value")] - public string Value { get; set; } = string.Empty; -} \ No newline at end of file + public required string Value { get; set; } +} diff --git a/src/ModelContextProtocol.Core/Protocol/BlobResourceContents.cs b/src/ModelContextProtocol.Core/Protocol/BlobResourceContents.cs index 017b13b38..dc56daca1 100644 --- a/src/ModelContextProtocol.Core/Protocol/BlobResourceContents.cs +++ b/src/ModelContextProtocol.Core/Protocol/BlobResourceContents.cs @@ -26,5 +26,5 @@ public sealed class BlobResourceContents : ResourceContents /// Gets or sets the base64-encoded string representing the binary data of the item. /// [JsonPropertyName("blob")] - public string Blob { get; set; } = string.Empty; -} \ No newline at end of file + public required string Blob { get; set; } +} diff --git a/src/ModelContextProtocol.Core/Protocol/CallToolRequestParams.cs b/src/ModelContextProtocol.Core/Protocol/CallToolRequestParams.cs index 832cf5bfb..07183f1bd 100644 --- a/src/ModelContextProtocol.Core/Protocol/CallToolRequestParams.cs +++ b/src/ModelContextProtocol.Core/Protocol/CallToolRequestParams.cs @@ -14,7 +14,7 @@ public sealed class CallToolRequestParams : RequestParams { /// Gets or sets the name of the tool to invoke. [JsonPropertyName("name")] - public required string Name { get; init; } + public required string Name { get; set; } /// /// Gets or sets optional arguments to pass to the tool when invoking it on the server. @@ -24,5 +24,5 @@ public sealed class CallToolRequestParams : RequestParams /// a parameter name and its corresponding argument value. /// [JsonPropertyName("arguments")] - public IReadOnlyDictionary? Arguments { get; init; } + public IDictionary? Arguments { get; set; } } diff --git a/src/ModelContextProtocol.Core/Protocol/CancelledNotificationParams.cs b/src/ModelContextProtocol.Core/Protocol/CancelledNotificationParams.cs index 8b59e7e22..ba10d3e8f 100644 --- a/src/ModelContextProtocol.Core/Protocol/CancelledNotificationParams.cs +++ b/src/ModelContextProtocol.Core/Protocol/CancelledNotificationParams.cs @@ -20,7 +20,7 @@ public sealed class CancelledNotificationParams : NotificationParams /// This must match the ID of an in-flight request that the sender wishes to cancel. /// [JsonPropertyName("requestId")] - public RequestId RequestId { get; set; } + public required RequestId RequestId { get; set; } /// /// Gets or sets an optional string describing the reason for the cancellation request. diff --git a/src/ModelContextProtocol.Core/Protocol/CompleteContext.cs b/src/ModelContextProtocol.Core/Protocol/CompleteContext.cs index f5ea37871..76ea765fd 100644 --- a/src/ModelContextProtocol.Core/Protocol/CompleteContext.cs +++ b/src/ModelContextProtocol.Core/Protocol/CompleteContext.cs @@ -15,5 +15,5 @@ public sealed class CompleteContext /// Gets or sets previously-resolved variables in a URI template or prompt. /// [JsonPropertyName("arguments")] - public IDictionary? Arguments { get; init; } + public IDictionary? Arguments { get; set; } } diff --git a/src/ModelContextProtocol.Core/Protocol/CompleteRequestParams.cs b/src/ModelContextProtocol.Core/Protocol/CompleteRequestParams.cs index 2798c7e68..806cf7935 100644 --- a/src/ModelContextProtocol.Core/Protocol/CompleteRequestParams.cs +++ b/src/ModelContextProtocol.Core/Protocol/CompleteRequestParams.cs @@ -23,18 +23,18 @@ public sealed class CompleteRequestParams : RequestParams /// Gets or sets the reference's information. /// [JsonPropertyName("ref")] - public required Reference Ref { get; init; } + public required Reference Ref { get; set; } /// /// Gets or sets the argument information for the completion request, specifying what is being completed /// and the current partial input. /// [JsonPropertyName("argument")] - public required Argument Argument { get; init; } + public required Argument Argument { get; set; } /// /// Gets or sets additional, optional context for completions. /// [JsonPropertyName("context")] - public CompleteContext? Context { get; init; } + public CompleteContext? Context { get; set; } } diff --git a/src/ModelContextProtocol.Core/Protocol/ContentBlock.cs b/src/ModelContextProtocol.Core/Protocol/ContentBlock.cs index 04de39db4..bbdb490ac 100644 --- a/src/ModelContextProtocol.Core/Protocol/ContentBlock.cs +++ b/src/ModelContextProtocol.Core/Protocol/ContentBlock.cs @@ -1,6 +1,7 @@ using Microsoft.Extensions.AI; using System.ComponentModel; using System.Diagnostics; +using System.Diagnostics.CodeAnalysis; using System.Text.Json; using System.Text.Json.Nodes; using System.Text.Json.Serialization; @@ -39,7 +40,7 @@ private protected ContentBlock() /// This determines the structure of the content object. Valid values include "image", "audio", "text", "resource", and "resource_link". /// [JsonPropertyName("type")] - public string Type { get; set; } = string.Empty; + public abstract string Type { get; } /// /// Gets or sets optional annotations for the content. @@ -49,7 +50,7 @@ private protected ContentBlock() /// and the priority level of the content. Clients can use this information to filter or prioritize content for different roles. /// [JsonPropertyName("annotations")] - public Annotations? Annotations { get; init; } + public Annotations? Annotations { get; set; } /// /// Provides a for . @@ -276,8 +277,8 @@ public override void Write(Utf8JsonWriter writer, ContentBlock value, JsonSerial /// Represents text provided to or from an LLM. public sealed class TextContentBlock : ContentBlock { - /// Initializes the instance of the class. - public TextContentBlock() => Type = "text"; + /// + public override string Type => "text"; /// /// Gets or sets the text content of the message. @@ -298,8 +299,8 @@ public sealed class TextContentBlock : ContentBlock /// Represents an image provided to or from an LLM. public sealed class ImageContentBlock : ContentBlock { - /// Initializes the instance of the class. - public ImageContentBlock() => Type = "image"; + /// + public override string Type => "image"; /// /// Gets or sets the base64-encoded image data. @@ -331,8 +332,8 @@ public sealed class ImageContentBlock : ContentBlock /// Represents audio provided to or from an LLM. public sealed class AudioContentBlock : ContentBlock { - /// Initializes the instance of the class. - public AudioContentBlock() => Type = "audio"; + /// + public override string Type => "audio"; /// /// Gets or sets the base64-encoded audio data. @@ -367,8 +368,8 @@ public sealed class AudioContentBlock : ContentBlock /// public sealed class EmbeddedResourceBlock : ContentBlock { - /// Initializes the instance of the class. - public EmbeddedResourceBlock() => Type = "resource"; + /// + public override string Type => "resource"; /// /// Gets or sets the resource content of the message when is "resource". @@ -399,20 +400,21 @@ public sealed class EmbeddedResourceBlock : ContentBlock /// public sealed class ResourceLinkBlock : ContentBlock { - /// Initializes the instance of the class. - public ResourceLinkBlock() => Type = "resource_link"; + /// + public override string Type => "resource_link"; /// /// Gets or sets the URI of this resource. /// [JsonPropertyName("uri")] - public required string Uri { get; init; } + [StringSyntax(StringSyntaxAttribute.Uri)] + public required string Uri { get; set; } /// /// Gets or sets a human-readable name for this resource. /// [JsonPropertyName("name")] - public required string Name { get; init; } + public required string Name { get; set; } /// /// Gets or sets a description of what this resource represents. @@ -431,7 +433,7 @@ public sealed class ResourceLinkBlock : ContentBlock /// /// [JsonPropertyName("description")] - public string? Description { get; init; } + public string? Description { get; set; } /// /// Gets or sets the MIME type of this resource. @@ -447,7 +449,7 @@ public sealed class ResourceLinkBlock : ContentBlock /// /// [JsonPropertyName("mimeType")] - public string? MimeType { get; init; } + public string? MimeType { get; set; } /// /// Gets or sets the size of the raw resource content (before base64 encoding), in bytes, if known. @@ -456,5 +458,5 @@ public sealed class ResourceLinkBlock : ContentBlock /// This can be used by applications to display file sizes and estimate context window usage. /// [JsonPropertyName("size")] - public long? Size { get; init; } -} \ No newline at end of file + public long? Size { get; set; } +} diff --git a/src/ModelContextProtocol.Core/Protocol/CreateMessageRequestParams.cs b/src/ModelContextProtocol.Core/Protocol/CreateMessageRequestParams.cs index b71358df3..d3086c0be 100644 --- a/src/ModelContextProtocol.Core/Protocol/CreateMessageRequestParams.cs +++ b/src/ModelContextProtocol.Core/Protocol/CreateMessageRequestParams.cs @@ -19,7 +19,7 @@ public sealed class CreateMessageRequestParams : RequestParams /// The client may ignore this request. /// [JsonPropertyName("includeContext")] - public ContextInclusion? IncludeContext { get; init; } + public ContextInclusion? IncludeContext { get; set; } /// /// Gets or sets the maximum number of tokens to generate in the LLM response, as requested by the server. @@ -29,13 +29,13 @@ public sealed class CreateMessageRequestParams : RequestParams /// response length and computation time. The client may choose to sample fewer tokens than requested. /// [JsonPropertyName("maxTokens")] - public int? MaxTokens { get; init; } + public required int MaxTokens { get; set; } /// /// Gets or sets the messages requested by the server to be included in the prompt. /// [JsonPropertyName("messages")] - public required IReadOnlyList Messages { get; init; } + public IList Messages { get; set; } = []; /// /// Gets or sets optional metadata to pass through to the LLM provider. @@ -46,7 +46,7 @@ public sealed class CreateMessageRequestParams : RequestParams /// that are specific to certain AI models or providers. /// [JsonPropertyName("metadata")] - public JsonElement? Metadata { get; init; } + public JsonElement? Metadata { get; set; } /// /// Gets or sets the server's preferences for which model to select. @@ -66,7 +66,7 @@ public sealed class CreateMessageRequestParams : RequestParams /// /// [JsonPropertyName("modelPreferences")] - public ModelPreferences? ModelPreferences { get; init; } + public ModelPreferences? ModelPreferences { get; set; } /// /// Gets or sets optional sequences of characters that signal the LLM to stop generating text when encountered. @@ -84,7 +84,7 @@ public sealed class CreateMessageRequestParams : RequestParams /// /// [JsonPropertyName("stopSequences")] - public IReadOnlyList? StopSequences { get; init; } + public IList? StopSequences { get; set; } /// /// Gets or sets an optional system prompt the server wants to use for sampling. @@ -93,11 +93,11 @@ public sealed class CreateMessageRequestParams : RequestParams /// The client may modify or omit this prompt. /// [JsonPropertyName("systemPrompt")] - public string? SystemPrompt { get; init; } + public string? SystemPrompt { get; set; } /// /// Gets or sets the temperature to use for sampling, as requested by the server. /// [JsonPropertyName("temperature")] - public float? Temperature { get; init; } + public float? Temperature { get; set; } } diff --git a/src/ModelContextProtocol.Core/Protocol/CreateMessageResult.cs b/src/ModelContextProtocol.Core/Protocol/CreateMessageResult.cs index ba599d6c5..7fada6399 100644 --- a/src/ModelContextProtocol.Core/Protocol/CreateMessageResult.cs +++ b/src/ModelContextProtocol.Core/Protocol/CreateMessageResult.cs @@ -14,7 +14,7 @@ public sealed class CreateMessageResult : Result /// Gets or sets the content of the message. /// [JsonPropertyName("content")] - public required ContentBlock Content { get; init; } + public required ContentBlock Content { get; set; } /// /// Gets or sets the name of the model that generated the message. @@ -29,7 +29,7 @@ public sealed class CreateMessageResult : Result /// /// [JsonPropertyName("model")] - public required string Model { get; init; } + public required string Model { get; set; } /// /// Gets or sets the reason why message generation (sampling) stopped, if known. @@ -43,11 +43,11 @@ public sealed class CreateMessageResult : Result /// /// [JsonPropertyName("stopReason")] - public string? StopReason { get; init; } + public string? StopReason { get; set; } /// /// Gets or sets the role of the user who generated the message. /// [JsonPropertyName("role")] - public required Role Role { get; init; } + public required Role Role { get; set; } } diff --git a/src/ModelContextProtocol.Core/Protocol/ElicitRequestParams.cs b/src/ModelContextProtocol.Core/Protocol/ElicitRequestParams.cs index 3a9926e22..75d205787 100644 --- a/src/ModelContextProtocol.Core/Protocol/ElicitRequestParams.cs +++ b/src/ModelContextProtocol.Core/Protocol/ElicitRequestParams.cs @@ -15,7 +15,7 @@ public sealed class ElicitRequestParams /// Gets or sets the message to present to the user. /// [JsonPropertyName("message")] - public string Message { get; set; } = string.Empty; + public required string Message { get; set; } /// /// Gets or sets the requested schema. diff --git a/src/ModelContextProtocol.Core/Protocol/ElicitResult.cs b/src/ModelContextProtocol.Core/Protocol/ElicitResult.cs index 024f5eb19..1515be25c 100644 --- a/src/ModelContextProtocol.Core/Protocol/ElicitResult.cs +++ b/src/ModelContextProtocol.Core/Protocol/ElicitResult.cs @@ -67,6 +67,25 @@ public sealed class ElicitResult : Result /// /// Gets or sets the user action in response to the elicitation. /// + /// + /// Defaults to "cancel" if not explicitly set. + /// + /// + /// + /// + /// "accept" + /// User submitted the form/confirmed the action + /// + /// + /// "decline" + /// User explicitly declined the action + /// + /// + /// "cancel" + /// User dismissed without making an explicit choice (default) + /// + /// + /// public string Action { get; set; } = "cancel"; /// diff --git a/src/ModelContextProtocol.Core/Protocol/GetPromptRequestParams.cs b/src/ModelContextProtocol.Core/Protocol/GetPromptRequestParams.cs index c94174a31..b5801450d 100644 --- a/src/ModelContextProtocol.Core/Protocol/GetPromptRequestParams.cs +++ b/src/ModelContextProtocol.Core/Protocol/GetPromptRequestParams.cs @@ -16,7 +16,7 @@ public sealed class GetPromptRequestParams : RequestParams /// Gets or sets the name of the prompt. /// [JsonPropertyName("name")] - public required string Name { get; init; } + public required string Name { get; set; } /// /// Gets or sets arguments to use for templating the prompt when retrieving it from the server. @@ -27,5 +27,5 @@ public sealed class GetPromptRequestParams : RequestParams /// choose to use these arguments in any way it deems appropriate to generate the prompt. /// [JsonPropertyName("arguments")] - public IReadOnlyDictionary? Arguments { get; init; } + public IDictionary? Arguments { get; set; } } diff --git a/src/ModelContextProtocol.Core/Protocol/Icon.cs b/src/ModelContextProtocol.Core/Protocol/Icon.cs index 2ccd13734..db23dd1a8 100644 --- a/src/ModelContextProtocol.Core/Protocol/Icon.cs +++ b/src/ModelContextProtocol.Core/Protocol/Icon.cs @@ -46,7 +46,7 @@ public sealed class Icon /// /// [JsonPropertyName("src")] - public required string Source { get; init; } + public required string Source { get; set; } /// /// Gets or sets the optional MIME type of the icon. @@ -56,7 +56,7 @@ public sealed class Icon /// Common values include "image/png", "image/jpeg", "image/svg+xml", and "image/webp". /// [JsonPropertyName("mimeType")] - public string? MimeType { get; init; } + public string? MimeType { get; set; } /// /// Gets or sets the optional size specifications for the icon. @@ -71,7 +71,7 @@ public sealed class Icon /// /// [JsonPropertyName("sizes")] - public IList? Sizes { get; init; } + public IList? Sizes { get; set; } /// /// Gets or sets the optional theme for this icon. @@ -81,5 +81,5 @@ public sealed class Icon /// Used to specify which UI theme the icon is designed for. /// [JsonPropertyName("theme")] - public string? Theme { get; init; } -} \ No newline at end of file + public string? Theme { get; set; } +} diff --git a/src/ModelContextProtocol.Core/Protocol/InitializeRequestParams.cs b/src/ModelContextProtocol.Core/Protocol/InitializeRequestParams.cs index 7aedd4058..e7c581604 100644 --- a/src/ModelContextProtocol.Core/Protocol/InitializeRequestParams.cs +++ b/src/ModelContextProtocol.Core/Protocol/InitializeRequestParams.cs @@ -38,7 +38,7 @@ public sealed class InitializeRequestParams : RequestParams /// /// [JsonPropertyName("protocolVersion")] - public required string ProtocolVersion { get; init; } + public required string ProtocolVersion { get; set; } /// /// Gets or sets the client's capabilities. @@ -47,7 +47,7 @@ public sealed class InitializeRequestParams : RequestParams /// Capabilities define the features the client supports, such as "sampling" or "roots". /// [JsonPropertyName("capabilities")] - public ClientCapabilities? Capabilities { get; init; } + public required ClientCapabilities Capabilities { get; set; } /// /// Gets or sets information about the client implementation, including its name and version. @@ -57,5 +57,5 @@ public sealed class InitializeRequestParams : RequestParams /// Servers may use this information for logging, debugging, or compatibility checks. /// [JsonPropertyName("clientInfo")] - public required Implementation ClientInfo { get; init; } + public required Implementation ClientInfo { get; set; } } diff --git a/src/ModelContextProtocol.Core/Protocol/InitializeResult.cs b/src/ModelContextProtocol.Core/Protocol/InitializeResult.cs index 831cc670e..326c6e37d 100644 --- a/src/ModelContextProtocol.Core/Protocol/InitializeResult.cs +++ b/src/ModelContextProtocol.Core/Protocol/InitializeResult.cs @@ -38,7 +38,7 @@ public sealed class InitializeResult : Result /// /// [JsonPropertyName("protocolVersion")] - public required string ProtocolVersion { get; init; } + public required string ProtocolVersion { get; set; } /// /// Gets or sets the server's capabilities. @@ -48,7 +48,7 @@ public sealed class InitializeResult : Result /// and other protocol-specific functionality. /// [JsonPropertyName("capabilities")] - public required ServerCapabilities Capabilities { get; init; } + public required ServerCapabilities Capabilities { get; set; } /// /// Gets or sets information about the server implementation, including its name and version. @@ -58,7 +58,7 @@ public sealed class InitializeResult : Result /// Clients may use this information for logging, debugging, or compatibility checks. /// [JsonPropertyName("serverInfo")] - public required Implementation ServerInfo { get; init; } + public required Implementation ServerInfo { get; set; } /// /// Gets or sets optional instructions for using the server and its features. @@ -75,5 +75,5 @@ public sealed class InitializeResult : Result /// /// [JsonPropertyName("instructions")] - public string? Instructions { get; init; } + public string? Instructions { get; set; } } diff --git a/src/ModelContextProtocol.Core/Protocol/JsonRpcError.cs b/src/ModelContextProtocol.Core/Protocol/JsonRpcError.cs index 5de344db8..be15a4776 100644 --- a/src/ModelContextProtocol.Core/Protocol/JsonRpcError.cs +++ b/src/ModelContextProtocol.Core/Protocol/JsonRpcError.cs @@ -23,5 +23,5 @@ public sealed class JsonRpcError : JsonRpcMessageWithId /// message, and optional additional data /// [JsonPropertyName("error")] - public required JsonRpcErrorDetail Error { get; init; } + public required JsonRpcErrorDetail Error { get; set; } } diff --git a/src/ModelContextProtocol.Core/Protocol/JsonRpcErrorDetail.cs b/src/ModelContextProtocol.Core/Protocol/JsonRpcErrorDetail.cs index e1009ce93..7e9947fae 100644 --- a/src/ModelContextProtocol.Core/Protocol/JsonRpcErrorDetail.cs +++ b/src/ModelContextProtocol.Core/Protocol/JsonRpcErrorDetail.cs @@ -17,7 +17,7 @@ public sealed class JsonRpcErrorDetail /// Gets an integer error code according to the JSON-RPC specification. /// [JsonPropertyName("code")] - public required int Code { get; init; } + public required int Code { get; set; } /// /// Gets a short description of the error. @@ -28,7 +28,7 @@ public sealed class JsonRpcErrorDetail /// in the JSON-RPC 2.0 specification. /// [JsonPropertyName("message")] - public required string Message { get; init; } + public required string Message { get; set; } /// /// Gets optional additional error data. @@ -40,5 +40,5 @@ public sealed class JsonRpcErrorDetail /// the error condition. /// [JsonPropertyName("data")] - public object? Data { get; init; } -} \ No newline at end of file + public object? Data { get; set; } +} diff --git a/src/ModelContextProtocol.Core/Protocol/JsonRpcMessage.cs b/src/ModelContextProtocol.Core/Protocol/JsonRpcMessage.cs index a01a5b58a..faac0d718 100644 --- a/src/ModelContextProtocol.Core/Protocol/JsonRpcMessage.cs +++ b/src/ModelContextProtocol.Core/Protocol/JsonRpcMessage.cs @@ -1,6 +1,5 @@ using ModelContextProtocol.Server; using System.ComponentModel; -using System.Security.Claims; using System.Text.Json; using System.Text.Json.Serialization; @@ -27,7 +26,7 @@ private protected JsonRpcMessage() /// /// [JsonPropertyName("jsonrpc")] - public string JsonRpc { get; init; } = "2.0"; + public string JsonRpc { get; set; } = "2.0"; /// /// Gets or sets the contextual information for this JSON-RPC message. diff --git a/src/ModelContextProtocol.Core/Protocol/JsonRpcMessageWithId.cs b/src/ModelContextProtocol.Core/Protocol/JsonRpcMessageWithId.cs index 8233df485..79f4b8687 100644 --- a/src/ModelContextProtocol.Core/Protocol/JsonRpcMessageWithId.cs +++ b/src/ModelContextProtocol.Core/Protocol/JsonRpcMessageWithId.cs @@ -26,5 +26,5 @@ private protected JsonRpcMessageWithId() /// Each ID is expected to be unique within the context of a given session. /// [JsonPropertyName("id")] - public RequestId Id { get; init; } + public required RequestId Id { get; set; } } diff --git a/src/ModelContextProtocol.Core/Protocol/JsonRpcNotification.cs b/src/ModelContextProtocol.Core/Protocol/JsonRpcNotification.cs index 6a2065151..4ff77007d 100644 --- a/src/ModelContextProtocol.Core/Protocol/JsonRpcNotification.cs +++ b/src/ModelContextProtocol.Core/Protocol/JsonRpcNotification.cs @@ -17,11 +17,11 @@ public sealed class JsonRpcNotification : JsonRpcMessage /// Gets or sets the name of the notification method. /// [JsonPropertyName("method")] - public required string Method { get; init; } + public required string Method { get; set; } /// /// Gets or sets optional parameters for the notification. /// [JsonPropertyName("params")] - public JsonNode? Params { get; init; } + public JsonNode? Params { get; set; } } diff --git a/src/ModelContextProtocol.Core/Protocol/JsonRpcRequest.cs b/src/ModelContextProtocol.Core/Protocol/JsonRpcRequest.cs index e80b25f47..f4837adf2 100644 --- a/src/ModelContextProtocol.Core/Protocol/JsonRpcRequest.cs +++ b/src/ModelContextProtocol.Core/Protocol/JsonRpcRequest.cs @@ -20,23 +20,11 @@ public sealed class JsonRpcRequest : JsonRpcMessageWithId /// Name of the method to invoke. /// [JsonPropertyName("method")] - public required string Method { get; init; } + public required string Method { get; set; } /// /// Optional parameters for the method. /// [JsonPropertyName("params")] - public JsonNode? Params { get; init; } - - internal JsonRpcRequest WithId(RequestId id) - { - return new JsonRpcRequest - { - JsonRpc = JsonRpc, - Id = id, - Method = Method, - Params = Params, - Context = Context, - }; - } + public JsonNode? Params { get; set; } } diff --git a/src/ModelContextProtocol.Core/Protocol/JsonRpcResponse.cs b/src/ModelContextProtocol.Core/Protocol/JsonRpcResponse.cs index c7d824b77..6fbcbcaac 100644 --- a/src/ModelContextProtocol.Core/Protocol/JsonRpcResponse.cs +++ b/src/ModelContextProtocol.Core/Protocol/JsonRpcResponse.cs @@ -25,5 +25,5 @@ public sealed class JsonRpcResponse : JsonRpcMessageWithId /// This property contains the result data returned by the server in response to the JSON-RPC method request. /// [JsonPropertyName("result")] - public required JsonNode? Result { get; init; } + public required JsonNode? Result { get; set; } } diff --git a/src/ModelContextProtocol.Core/Protocol/ListRootsResult.cs b/src/ModelContextProtocol.Core/Protocol/ListRootsResult.cs index 384249175..115283e98 100644 --- a/src/ModelContextProtocol.Core/Protocol/ListRootsResult.cs +++ b/src/ModelContextProtocol.Core/Protocol/ListRootsResult.cs @@ -26,5 +26,5 @@ public sealed class ListRootsResult : Result /// Each root serves as an entry point for resource navigation in the Model Context Protocol. /// [JsonPropertyName("roots")] - public required IReadOnlyList Roots { get; init; } + public required IList Roots { get; set; } } diff --git a/src/ModelContextProtocol.Core/Protocol/LoggingMessageNotificationParams.cs b/src/ModelContextProtocol.Core/Protocol/LoggingMessageNotificationParams.cs index 8c9167ddc..55975b772 100644 --- a/src/ModelContextProtocol.Core/Protocol/LoggingMessageNotificationParams.cs +++ b/src/ModelContextProtocol.Core/Protocol/LoggingMessageNotificationParams.cs @@ -26,7 +26,7 @@ public sealed class LoggingMessageNotificationParams : NotificationParams /// Gets or sets the severity of this log message. /// [JsonPropertyName("level")] - public LoggingLevel Level { get; init; } + public required LoggingLevel Level { get; set; } /// /// Gets or sets an optional name of the logger issuing this message. @@ -42,11 +42,11 @@ public sealed class LoggingMessageNotificationParams : NotificationParams /// /// [JsonPropertyName("logger")] - public string? Logger { get; init; } + public string? Logger { get; set; } /// /// Gets or sets the data to be logged, such as a string message. /// [JsonPropertyName("data")] - public JsonElement? Data { get; init; } -} \ No newline at end of file + public JsonElement? Data { get; set; } +} diff --git a/src/ModelContextProtocol.Core/Protocol/ModelHint.cs b/src/ModelContextProtocol.Core/Protocol/ModelHint.cs index 30e18cb59..c56d4569f 100644 --- a/src/ModelContextProtocol.Core/Protocol/ModelHint.cs +++ b/src/ModelContextProtocol.Core/Protocol/ModelHint.cs @@ -25,5 +25,5 @@ public sealed class ModelHint /// selection based on these preferences and their available models. /// [JsonPropertyName("name")] - public string? Name { get; init; } -} \ No newline at end of file + public string? Name { get; set; } +} diff --git a/src/ModelContextProtocol.Core/Protocol/ModelPreferences.cs b/src/ModelContextProtocol.Core/Protocol/ModelPreferences.cs index 8d9d13fc1..6be17325c 100644 --- a/src/ModelContextProtocol.Core/Protocol/ModelPreferences.cs +++ b/src/ModelContextProtocol.Core/Protocol/ModelPreferences.cs @@ -31,13 +31,13 @@ public sealed class ModelPreferences /// A value of 0 means cost is not important, while a value of 1 means cost is the most important factor. /// [JsonPropertyName("costPriority")] - public float? CostPriority { get; init; } + public float? CostPriority { get; set; } /// /// Gets or sets optional hints to use for model selection. /// [JsonPropertyName("hints")] - public IReadOnlyList? Hints { get; init; } + public IList? Hints { get; set; } /// /// Gets or sets how much to prioritize sampling speed (latency) when selecting a model. @@ -46,7 +46,7 @@ public sealed class ModelPreferences /// A value of 0 means speed is not important, while a value of 1 means speed is the most important factor. /// [JsonPropertyName("speedPriority")] - public float? SpeedPriority { get; init; } + public float? SpeedPriority { get; set; } /// /// Gets or sets how much to prioritize intelligence and capabilities when selecting a model. @@ -55,5 +55,5 @@ public sealed class ModelPreferences /// A value of 0 means intelligence is not important, while a value of 1 means intelligence is the most important factor. /// [JsonPropertyName("intelligencePriority")] - public float? IntelligencePriority { get; init; } + public float? IntelligencePriority { get; set; } } diff --git a/src/ModelContextProtocol.Core/Protocol/PaginatedRequest.cs b/src/ModelContextProtocol.Core/Protocol/PaginatedRequest.cs index 0dc43c660..cc7e33f24 100644 --- a/src/ModelContextProtocol.Core/Protocol/PaginatedRequest.cs +++ b/src/ModelContextProtocol.Core/Protocol/PaginatedRequest.cs @@ -24,5 +24,5 @@ private protected PaginatedRequestParams() /// property of a previous request's response. /// [JsonPropertyName("cursor")] - public string? Cursor { get; init; } -} \ No newline at end of file + public string? Cursor { get; set; } +} diff --git a/src/ModelContextProtocol.Core/Protocol/ProgressNotificationParams.cs b/src/ModelContextProtocol.Core/Protocol/ProgressNotificationParams.cs index 0b661d181..294b12f1e 100644 --- a/src/ModelContextProtocol.Core/Protocol/ProgressNotificationParams.cs +++ b/src/ModelContextProtocol.Core/Protocol/ProgressNotificationParams.cs @@ -29,7 +29,7 @@ public sealed class ProgressNotificationParams : NotificationParams /// correlate the notifications with the original request. /// /// - public required ProgressToken ProgressToken { get; init; } + public required ProgressToken ProgressToken { get; set; } /// /// Gets or sets the progress thus far. @@ -37,7 +37,7 @@ public sealed class ProgressNotificationParams : NotificationParams /// /// This should increase for each notification issued as part of the same request, even if the total is unknown. /// - public required ProgressNotificationValue Progress { get; init; } + public required ProgressNotificationValue Progress { get; set; } /// /// Provides a for . diff --git a/src/ModelContextProtocol.Core/Protocol/PromptArgument.cs b/src/ModelContextProtocol.Core/Protocol/PromptArgument.cs index 648eccc07..b4e932212 100644 --- a/src/ModelContextProtocol.Core/Protocol/PromptArgument.cs +++ b/src/ModelContextProtocol.Core/Protocol/PromptArgument.cs @@ -33,7 +33,7 @@ public sealed class PromptArgument : IBaseMetadata /// for this argument and how it will affect the generated prompt. /// [JsonPropertyName("description")] - public string? Description { get; set; } = string.Empty; + public string? Description { get; set; } /// /// Gets or sets an indication as to whether this argument must be provided when requesting the prompt. diff --git a/src/ModelContextProtocol.Core/Protocol/PromptListChangedNotification .cs b/src/ModelContextProtocol.Core/Protocol/PromptListChangedNotification.cs similarity index 100% rename from src/ModelContextProtocol.Core/Protocol/PromptListChangedNotification .cs rename to src/ModelContextProtocol.Core/Protocol/PromptListChangedNotification.cs diff --git a/src/ModelContextProtocol.Core/Protocol/PromptMessage.cs b/src/ModelContextProtocol.Core/Protocol/PromptMessage.cs index 4bf8b28ef..e1dbc7851 100644 --- a/src/ModelContextProtocol.Core/Protocol/PromptMessage.cs +++ b/src/ModelContextProtocol.Core/Protocol/PromptMessage.cs @@ -37,7 +37,7 @@ public sealed class PromptMessage /// The property indicates the specific content type. /// [JsonPropertyName("content")] - public ContentBlock Content { get; set; } = new TextContentBlock { Text = "" }; + public required ContentBlock Content { get; set; } /// /// Gets or sets the role of the message sender, specifying whether it's from a "user" or an "assistant". @@ -48,5 +48,5 @@ public sealed class PromptMessage /// messages represent responses generated by AI models. /// [JsonPropertyName("role")] - public Role Role { get; set; } = Role.User; + public required Role Role { get; set; } } diff --git a/src/ModelContextProtocol.Core/Protocol/ReadResourceRequestParams.cs b/src/ModelContextProtocol.Core/Protocol/ReadResourceRequestParams.cs index 680909c0f..7ea2cc774 100644 --- a/src/ModelContextProtocol.Core/Protocol/ReadResourceRequestParams.cs +++ b/src/ModelContextProtocol.Core/Protocol/ReadResourceRequestParams.cs @@ -1,3 +1,4 @@ +using System.Diagnostics.CodeAnalysis; using System.Text.Json.Serialization; namespace ModelContextProtocol.Protocol; @@ -15,5 +16,6 @@ public sealed class ReadResourceRequestParams : RequestParams /// The URI of the resource to read. The URI can use any protocol; it is up to the server how to interpret it. /// [JsonPropertyName("uri")] - public required string Uri { get; init; } + [StringSyntax(StringSyntaxAttribute.Uri)] + public required string Uri { get; set; } } diff --git a/src/ModelContextProtocol.Core/Protocol/Reference.cs b/src/ModelContextProtocol.Core/Protocol/Reference.cs index af95cf330..149a31880 100644 --- a/src/ModelContextProtocol.Core/Protocol/Reference.cs +++ b/src/ModelContextProtocol.Core/Protocol/Reference.cs @@ -23,18 +23,18 @@ namespace ModelContextProtocol.Protocol; public abstract class Reference { /// Prevent external derivations. - private protected Reference() + private protected Reference() { } /// - /// Gets or sets the type of content. + /// Gets the type of content. /// /// /// This can be "ref/resource" or "ref/prompt". /// [JsonPropertyName("type")] - public string Type { get; set; } = string.Empty; + public abstract string Type { get; } /// /// Provides a for . @@ -153,10 +153,8 @@ public override void Write(Utf8JsonWriter writer, Reference value, JsonSerialize /// public sealed class PromptReference : Reference, IBaseMetadata { - /// - /// Initializes a new instance of the class. - /// - public PromptReference() => Type = "ref/prompt"; + /// + public override string Type => "ref/prompt"; /// [JsonPropertyName("name")] @@ -175,10 +173,8 @@ public sealed class PromptReference : Reference, IBaseMetadata /// public sealed class ResourceTemplateReference : Reference { - /// - /// Initializes a new instance of the class. - /// - public ResourceTemplateReference() => Type = "ref/resource"; + /// + public override string Type => "ref/resource"; /// /// Gets or sets the URI or URI template of the resource. diff --git a/src/ModelContextProtocol.Core/Protocol/RequestParamsMetadata.cs b/src/ModelContextProtocol.Core/Protocol/RequestParamsMetadata.cs index 963584ccd..19400d394 100644 --- a/src/ModelContextProtocol.Core/Protocol/RequestParamsMetadata.cs +++ b/src/ModelContextProtocol.Core/Protocol/RequestParamsMetadata.cs @@ -18,5 +18,5 @@ public sealed class RequestParamsMetadata /// The receiver is not obligated to provide these notifications. /// [JsonPropertyName("progressToken")] - public ProgressToken? ProgressToken { get; set; } = default!; + public ProgressToken? ProgressToken { get; set; } } diff --git a/src/ModelContextProtocol.Core/Protocol/Resource.cs b/src/ModelContextProtocol.Core/Protocol/Resource.cs index d8441488e..e6c9fafe7 100644 --- a/src/ModelContextProtocol.Core/Protocol/Resource.cs +++ b/src/ModelContextProtocol.Core/Protocol/Resource.cs @@ -1,3 +1,4 @@ +using System.Diagnostics.CodeAnalysis; using System.Text.Json.Nodes; using System.Text.Json.Serialization; using ModelContextProtocol.Server; @@ -24,7 +25,8 @@ public sealed class Resource : IBaseMetadata /// Gets or sets the URI of this resource. /// [JsonPropertyName("uri")] - public required string Uri { get; init; } + [StringSyntax(StringSyntaxAttribute.Uri)] + public required string Uri { get; set; } /// /// Gets or sets a description of what this resource represents. @@ -43,7 +45,7 @@ public sealed class Resource : IBaseMetadata /// /// [JsonPropertyName("description")] - public string? Description { get; init; } + public string? Description { get; set; } /// /// Gets or sets the MIME type of this resource. @@ -59,7 +61,7 @@ public sealed class Resource : IBaseMetadata /// /// [JsonPropertyName("mimeType")] - public string? MimeType { get; init; } + public string? MimeType { get; set; } /// /// Gets or sets optional annotations for the resource. @@ -69,7 +71,7 @@ public sealed class Resource : IBaseMetadata /// and the priority level of the resource. Clients can use this information to filter or prioritize resources for different roles. /// [JsonPropertyName("annotations")] - public Annotations? Annotations { get; init; } + public Annotations? Annotations { get; set; } /// /// Gets or sets the size of the raw resource content (before base64 encoding), in bytes, if known. @@ -78,7 +80,7 @@ public sealed class Resource : IBaseMetadata /// This can be used by applications to display file sizes and estimate context window usage. /// [JsonPropertyName("size")] - public long? Size { get; init; } + public long? Size { get; set; } /// /// Gets or sets an optional list of icons for this resource. @@ -96,7 +98,7 @@ public sealed class Resource : IBaseMetadata /// Implementations must not make assumptions about its contents. /// [JsonPropertyName("_meta")] - public JsonObject? Meta { get; init; } + public JsonObject? Meta { get; set; } /// /// Gets or sets the callable server resource corresponding to this metadata if any. diff --git a/src/ModelContextProtocol.Core/Protocol/ResourceContents.cs b/src/ModelContextProtocol.Core/Protocol/ResourceContents.cs index d5f25f41a..90a37af1c 100644 --- a/src/ModelContextProtocol.Core/Protocol/ResourceContents.cs +++ b/src/ModelContextProtocol.Core/Protocol/ResourceContents.cs @@ -1,5 +1,6 @@ using System.ComponentModel; using System.Diagnostics; +using System.Diagnostics.CodeAnalysis; using System.Text.Json; using System.Text.Json.Nodes; using System.Text.Json.Serialization; @@ -38,7 +39,8 @@ private protected ResourceContents() /// Gets or sets the URI of the resource. /// [JsonPropertyName("uri")] - public string Uri { get; set; } = string.Empty; + [StringSyntax(StringSyntaxAttribute.Uri)] + public required string Uri { get; set; } /// /// Gets or sets the MIME type of the resource content. @@ -155,7 +157,7 @@ public override void Write(Utf8JsonWriter writer, ResourceContents value, JsonSe writer.WriteStartObject(); writer.WriteString("uri", value.Uri); writer.WriteString("mimeType", value.MimeType); - + Debug.Assert(value is BlobResourceContents or TextResourceContents); if (value is BlobResourceContents blobResource) { diff --git a/src/ModelContextProtocol.Core/Protocol/ResourceTemplate.cs b/src/ModelContextProtocol.Core/Protocol/ResourceTemplate.cs index fe753510f..2f9d16e42 100644 --- a/src/ModelContextProtocol.Core/Protocol/ResourceTemplate.cs +++ b/src/ModelContextProtocol.Core/Protocol/ResourceTemplate.cs @@ -25,7 +25,7 @@ public sealed class ResourceTemplate : IBaseMetadata /// Gets or sets the URI template (according to RFC 6570) that can be used to construct resource URIs. /// [JsonPropertyName("uriTemplate")] - public required string UriTemplate { get; init; } + public required string UriTemplate { get; set; } /// /// Gets or sets a description of what this resource template represents. @@ -42,7 +42,7 @@ public sealed class ResourceTemplate : IBaseMetadata /// /// [JsonPropertyName("description")] - public string? Description { get; init; } + public string? Description { get; set; } /// /// Gets or sets the MIME type of this resource template, if known. @@ -59,7 +59,7 @@ public sealed class ResourceTemplate : IBaseMetadata /// /// [JsonPropertyName("mimeType")] - public string? MimeType { get; init; } + public string? MimeType { get; set; } /// /// Gets or sets optional annotations for the resource template. @@ -70,7 +70,7 @@ public sealed class ResourceTemplate : IBaseMetadata /// or prioritize resource templates for different roles. /// [JsonPropertyName("annotations")] - public Annotations? Annotations { get; init; } + public Annotations? Annotations { get; set; } /// /// Gets or sets an optional list of icons for this resource template. @@ -88,7 +88,7 @@ public sealed class ResourceTemplate : IBaseMetadata /// Implementations must not make assumptions about its contents. /// [JsonPropertyName("_meta")] - public JsonObject? Meta { get; init; } + public JsonObject? Meta { get; set; } /// Gets whether contains any template expressions. [JsonIgnore] diff --git a/src/ModelContextProtocol.Core/Protocol/ResourceUpdatedNotificationParams.cs b/src/ModelContextProtocol.Core/Protocol/ResourceUpdatedNotificationParams.cs index d97eaa752..c6a4aaa28 100644 --- a/src/ModelContextProtocol.Core/Protocol/ResourceUpdatedNotificationParams.cs +++ b/src/ModelContextProtocol.Core/Protocol/ResourceUpdatedNotificationParams.cs @@ -27,5 +27,5 @@ public sealed class ResourceUpdatedNotificationParams : NotificationParams /// [JsonPropertyName("uri")] [StringSyntax(StringSyntaxAttribute.Uri)] - public string? Uri { get; init; } + public required string Uri { get; set; } } diff --git a/src/ModelContextProtocol.Core/Protocol/Result.cs b/src/ModelContextProtocol.Core/Protocol/Result.cs index 4e8393e98..58b076ddb 100644 --- a/src/ModelContextProtocol.Core/Protocol/Result.cs +++ b/src/ModelContextProtocol.Core/Protocol/Result.cs @@ -20,5 +20,5 @@ private protected Result() /// Implementations must not make assumptions about its contents. /// [JsonPropertyName("_meta")] - public JsonObject? Meta { get; init; } -} \ No newline at end of file + public JsonObject? Meta { get; set; } +} diff --git a/src/ModelContextProtocol.Core/Protocol/Root.cs b/src/ModelContextProtocol.Core/Protocol/Root.cs index 5893bfec2..465d99892 100644 --- a/src/ModelContextProtocol.Core/Protocol/Root.cs +++ b/src/ModelContextProtocol.Core/Protocol/Root.cs @@ -1,3 +1,4 @@ +using System.Diagnostics.CodeAnalysis; using System.Text.Json; using System.Text.Json.Serialization; @@ -18,13 +19,14 @@ public sealed class Root /// Gets or sets the URI of the root. /// [JsonPropertyName("uri")] - public required string Uri { get; init; } + [StringSyntax(StringSyntaxAttribute.Uri)] + public required string Uri { get; set; } /// /// Gets or sets a human-readable name for the root. /// [JsonPropertyName("name")] - public string? Name { get; init; } + public string? Name { get; set; } /// /// Gets or sets additional metadata for the root. @@ -33,5 +35,5 @@ public sealed class Root /// This is reserved by the protocol for future use. /// [JsonPropertyName("_meta")] - public JsonElement? Meta { get; init; } -} \ No newline at end of file + public JsonElement? Meta { get; set; } +} diff --git a/src/ModelContextProtocol.Core/Protocol/SamplingMessage.cs b/src/ModelContextProtocol.Core/Protocol/SamplingMessage.cs index 4c4971cb3..60db179cc 100644 --- a/src/ModelContextProtocol.Core/Protocol/SamplingMessage.cs +++ b/src/ModelContextProtocol.Core/Protocol/SamplingMessage.cs @@ -29,11 +29,11 @@ public sealed class SamplingMessage /// Gets or sets the content of the message. /// [JsonPropertyName("content")] - public required ContentBlock Content { get; init; } + public required ContentBlock Content { get; set; } /// /// Gets or sets the role of the message sender, indicating whether it's from a "user" or an "assistant". /// [JsonPropertyName("role")] - public required Role Role { get; init; } + public required Role Role { get; set; } } diff --git a/src/ModelContextProtocol.Core/Protocol/SetLevelRequestParams.cs b/src/ModelContextProtocol.Core/Protocol/SetLevelRequestParams.cs index 5346c9a51..9441d39ac 100644 --- a/src/ModelContextProtocol.Core/Protocol/SetLevelRequestParams.cs +++ b/src/ModelContextProtocol.Core/Protocol/SetLevelRequestParams.cs @@ -16,5 +16,5 @@ public sealed class SetLevelRequestParams : RequestParams /// Gets or sets the level of logging that the client wants to receive from the server. /// [JsonPropertyName("level")] - public required LoggingLevel Level { get; init; } + public required LoggingLevel Level { get; set; } } diff --git a/src/ModelContextProtocol.Core/Protocol/SubscribeRequestParams.cs b/src/ModelContextProtocol.Core/Protocol/SubscribeRequestParams.cs index 24a928608..5f95a5217 100644 --- a/src/ModelContextProtocol.Core/Protocol/SubscribeRequestParams.cs +++ b/src/ModelContextProtocol.Core/Protocol/SubscribeRequestParams.cs @@ -31,5 +31,5 @@ public sealed class SubscribeRequestParams : RequestParams /// [JsonPropertyName("uri")] [StringSyntax(StringSyntaxAttribute.Uri)] - public string? Uri { get; init; } + public required string Uri { get; set; } } diff --git a/src/ModelContextProtocol.Core/Protocol/TextResourceContents.cs b/src/ModelContextProtocol.Core/Protocol/TextResourceContents.cs index 4659e15e9..9f8a9b826 100644 --- a/src/ModelContextProtocol.Core/Protocol/TextResourceContents.cs +++ b/src/ModelContextProtocol.Core/Protocol/TextResourceContents.cs @@ -25,5 +25,5 @@ public sealed class TextResourceContents : ResourceContents /// Gets or sets the text of the item. /// [JsonPropertyName("text")] - public string Text { get; set; } = string.Empty; + public required string Text { get; set; } } diff --git a/src/ModelContextProtocol.Core/Protocol/UnsubscribeRequestParams.cs b/src/ModelContextProtocol.Core/Protocol/UnsubscribeRequestParams.cs index 49414f8c1..8999a4287 100644 --- a/src/ModelContextProtocol.Core/Protocol/UnsubscribeRequestParams.cs +++ b/src/ModelContextProtocol.Core/Protocol/UnsubscribeRequestParams.cs @@ -25,5 +25,5 @@ public sealed class UnsubscribeRequestParams : RequestParams /// [JsonPropertyName("uri")] [StringSyntax(StringSyntaxAttribute.Uri)] - public string? Uri { get; init; } + public required string Uri { get; set; } } diff --git a/src/ModelContextProtocol.Core/Server/McpServer.Methods.cs b/src/ModelContextProtocol.Core/Server/McpServer.Methods.cs index a24918bd9..a48e41b65 100644 --- a/src/ModelContextProtocol.Core/Server/McpServer.Methods.cs +++ b/src/ModelContextProtocol.Core/Server/McpServer.Methods.cs @@ -153,7 +153,7 @@ public async Task SampleAsync( var result = await SampleAsync(new() { Messages = samplingMessages, - MaxTokens = options?.MaxOutputTokens, + MaxTokens = options?.MaxOutputTokens ?? int.MaxValue, StopSequences = options?.StopSequences?.ToArray(), SystemPrompt = systemPrompt?.ToString(), Temperature = options?.Temperature, diff --git a/tests/ModelContextProtocol.AspNetCore.Tests/MapMcpTests.cs b/tests/ModelContextProtocol.AspNetCore.Tests/MapMcpTests.cs index d3700f57f..0c71e56e3 100644 --- a/tests/ModelContextProtocol.AspNetCore.Tests/MapMcpTests.cs +++ b/tests/ModelContextProtocol.AspNetCore.Tests/MapMcpTests.cs @@ -288,6 +288,7 @@ public static async Task SamplingToolAsync(McpServer server, string prom Content = new TextContentBlock { Text = prompt }, } ], + MaxTokens = 1000 }; await server.SampleAsync(samplingRequest, cancellationToken); diff --git a/tests/ModelContextProtocol.AspNetCore.Tests/StatelessServerTests.cs b/tests/ModelContextProtocol.AspNetCore.Tests/StatelessServerTests.cs index 199d815eb..252af4b88 100644 --- a/tests/ModelContextProtocol.AspNetCore.Tests/StatelessServerTests.cs +++ b/tests/ModelContextProtocol.AspNetCore.Tests/StatelessServerTests.cs @@ -201,7 +201,11 @@ public static async Task TestSamplingErrors(McpServer server) var requestSamplingEx = await Assert.ThrowsAsync(() => server.SampleAsync([])); Assert.Equal(expectedSamplingErrorMessage, requestSamplingEx.Message); - var ex = await Assert.ThrowsAsync(() => server.SendRequestAsync(new JsonRpcRequest { Method = RequestMethods.SamplingCreateMessage })); + var ex = await Assert.ThrowsAsync(() => server.SendRequestAsync(new JsonRpcRequest + { + Id = default, + Method = RequestMethods.SamplingCreateMessage + })); return ex.Message; } @@ -216,7 +220,11 @@ public static async Task TestRootsErrors(McpServer server) var requestRootsEx = Assert.Throws(() => server.RequestRootsAsync(new())); Assert.Equal(expectedRootsErrorMessage, requestRootsEx.Message); - var ex = await Assert.ThrowsAsync(() => server.SendRequestAsync(new JsonRpcRequest { Method = RequestMethods.RootsList })); + var ex = await Assert.ThrowsAsync(() => server.SendRequestAsync(new JsonRpcRequest + { + Id = default, + Method = RequestMethods.RootsList + })); return ex.Message; } @@ -228,10 +236,14 @@ public static async Task TestElicitationErrors(McpServer server) // Even when the client has elicitation support, it should not be advertised in stateless mode. Assert.Null(server.ClientCapabilities); - var requestElicitationEx = Assert.Throws(() => server.ElicitAsync(new())); + var requestElicitationEx = Assert.Throws(() => server.ElicitAsync(new() { Message = string.Empty })); Assert.Equal(expectedElicitationErrorMessage, requestElicitationEx.Message); - var ex = await Assert.ThrowsAsync(() => server.SendRequestAsync(new JsonRpcRequest { Method = RequestMethods.ElicitationCreate })); + var ex = await Assert.ThrowsAsync(() => server.SendRequestAsync(new JsonRpcRequest + { + Id = default, + Method = RequestMethods.ElicitationCreate + })); return ex.Message; } diff --git a/tests/ModelContextProtocol.Tests/Client/McpClientExtensionsTests.cs b/tests/ModelContextProtocol.Tests/Client/McpClientExtensionsTests.cs index f4e6062de..8fb7d2203 100644 --- a/tests/ModelContextProtocol.Tests/Client/McpClientExtensionsTests.cs +++ b/tests/ModelContextProtocol.Tests/Client/McpClientExtensionsTests.cs @@ -204,6 +204,7 @@ public async Task PingAsync_Forwards_To_McpClient_SendRequestAsync() .Setup(c => c.SendRequestAsync(It.IsAny(), It.IsAny())) .ReturnsAsync(new JsonRpcResponse { + Id = default, Result = JsonSerializer.SerializeToNode(new object(), McpJsonUtilities.DefaultOptions), }); @@ -225,6 +226,7 @@ public async Task GetPromptAsync_Forwards_To_McpClient_SendRequestAsync() .Setup(c => c.SendRequestAsync(It.IsAny(), It.IsAny())) .ReturnsAsync(new JsonRpcResponse { + Id = default, Result = JsonSerializer.SerializeToNode(resultPayload, McpJsonUtilities.DefaultOptions), }); @@ -247,6 +249,7 @@ public async Task CallToolAsync_Forwards_To_McpClient_SendRequestAsync() .Setup(c => c.SendRequestAsync(It.IsAny(), It.IsAny())) .ReturnsAsync(new JsonRpcResponse { + Id = default, Result = JsonSerializer.SerializeToNode(callResult, McpJsonUtilities.DefaultOptions), }); @@ -267,6 +270,7 @@ public async Task SubscribeToResourceAsync_Forwards_To_McpClient_SendRequestAsyn .Setup(c => c.SendRequestAsync(It.IsAny(), It.IsAny())) .ReturnsAsync(new JsonRpcResponse { + Id = default, Result = JsonSerializer.SerializeToNode(new EmptyResult(), McpJsonUtilities.DefaultOptions), }); @@ -286,6 +290,7 @@ public async Task UnsubscribeFromResourceAsync_Forwards_To_McpClient_SendRequest .Setup(c => c.SendRequestAsync(It.IsAny(), It.IsAny())) .ReturnsAsync(new JsonRpcResponse { + Id = default, Result = JsonSerializer.SerializeToNode(new EmptyResult(), McpJsonUtilities.DefaultOptions), }); @@ -308,6 +313,7 @@ public async Task CompleteAsync_Forwards_To_McpClient_SendRequestAsync() .Setup(c => c.SendRequestAsync(It.IsAny(), It.IsAny())) .ReturnsAsync(new JsonRpcResponse { + Id = default, Result = JsonSerializer.SerializeToNode(resultPayload, McpJsonUtilities.DefaultOptions), }); @@ -330,6 +336,7 @@ public async Task ReadResourceAsync_String_Forwards_To_McpClient_SendRequestAsyn .Setup(c => c.SendRequestAsync(It.IsAny(), It.IsAny())) .ReturnsAsync(new JsonRpcResponse { + Id = default, Result = JsonSerializer.SerializeToNode(resultPayload, McpJsonUtilities.DefaultOptions), }); @@ -352,6 +359,7 @@ public async Task ReadResourceAsync_Uri_Forwards_To_McpClient_SendRequestAsync() .Setup(c => c.SendRequestAsync(It.IsAny(), It.IsAny())) .ReturnsAsync(new JsonRpcResponse { + Id = default, Result = JsonSerializer.SerializeToNode(resultPayload, McpJsonUtilities.DefaultOptions), }); @@ -374,6 +382,7 @@ public async Task ReadResourceAsync_Template_Forwards_To_McpClient_SendRequestAs .Setup(c => c.SendRequestAsync(It.IsAny(), It.IsAny())) .ReturnsAsync(new JsonRpcResponse { + Id = default, Result = JsonSerializer.SerializeToNode(resultPayload, McpJsonUtilities.DefaultOptions), }); diff --git a/tests/ModelContextProtocol.Tests/Client/McpClientResourceTemplateTests.cs b/tests/ModelContextProtocol.Tests/Client/McpClientResourceTemplateTests.cs index 2599d7485..dc03f9df0 100644 --- a/tests/ModelContextProtocol.Tests/Client/McpClientResourceTemplateTests.cs +++ b/tests/ModelContextProtocol.Tests/Client/McpClientResourceTemplateTests.cs @@ -17,7 +17,7 @@ protected override void ConfigureServices(ServiceCollection services, IMcpServer mcpServerBuilder.WithReadResourceHandler((request, cancellationToken) => new ValueTask(new ReadResourceResult { - Contents = [new TextResourceContents { Text = request.Params?.Uri ?? string.Empty }] + Contents = [new TextResourceContents { Text = request.Params?.Uri ?? string.Empty, Uri = request.Params?.Uri ?? string.Empty }] })); } diff --git a/tests/ModelContextProtocol.Tests/Client/McpClientTests.cs b/tests/ModelContextProtocol.Tests/Client/McpClientTests.cs index 3e29fa8a2..2d7484686 100644 --- a/tests/ModelContextProtocol.Tests/Client/McpClientTests.cs +++ b/tests/ModelContextProtocol.Tests/Client/McpClientTests.cs @@ -70,10 +70,10 @@ public async Task CanReadServerInfo() } [Theory] - [InlineData(null, null)] + [InlineData(null, 10)] [InlineData(0.7f, 50)] [InlineData(1.0f, 100)] - public async Task CreateSamplingHandler_ShouldHandleTextMessages(float? temperature, int? maxTokens) + public async Task CreateSamplingHandler_ShouldHandleTextMessages(float? temperature, int maxTokens) { // Arrange var mockChatClient = new Mock(); @@ -472,7 +472,7 @@ public async Task AsClientLoggerProvider_MessagesSentToClient() Assert.Equal("TestLogger", m.Logger); - string ? s = JsonSerializer.Deserialize(m.Data.Value, McpJsonUtilities.DefaultOptions); + string? s = JsonSerializer.Deserialize(m.Data.Value, McpJsonUtilities.DefaultOptions); Assert.NotNull(s); if (s.Contains("Information")) @@ -505,7 +505,7 @@ public async Task AsClientLoggerProvider_MessagesSentToClient() "Error message", "Information message", "Warning message", - ], + ], data.OrderBy(s => s)); } diff --git a/tests/ModelContextProtocol.Tests/Configuration/McpServerBuilderExtensionsFilterTests.cs b/tests/ModelContextProtocol.Tests/Configuration/McpServerBuilderExtensionsFilterTests.cs index 32b588d09..fb35fee85 100644 --- a/tests/ModelContextProtocol.Tests/Configuration/McpServerBuilderExtensionsFilterTests.cs +++ b/tests/ModelContextProtocol.Tests/Configuration/McpServerBuilderExtensionsFilterTests.cs @@ -66,7 +66,7 @@ protected override void ConfigureServices(ServiceCollection services, IMcpServer { return new CallToolResult { - Content = [new TextContentBlock { Type = "text", Text = $"Error from filter: {ex.Message}" }], + Content = [new TextContentBlock { Text = $"Error from filter: {ex.Message}" }], IsError = true }; } diff --git a/tests/ModelContextProtocol.Tests/Configuration/McpServerBuilderExtensionsResourcesTests.cs b/tests/ModelContextProtocol.Tests/Configuration/McpServerBuilderExtensionsResourcesTests.cs index 7d037fb2d..105d459ba 100644 --- a/tests/ModelContextProtocol.Tests/Configuration/McpServerBuilderExtensionsResourcesTests.cs +++ b/tests/ModelContextProtocol.Tests/Configuration/McpServerBuilderExtensionsResourcesTests.cs @@ -105,7 +105,7 @@ protected override void ConfigureServices(ServiceCollection services, IMcpServer case "test://ResourceTemplate2": return new ReadResourceResult { - Contents = [new TextResourceContents { Text = request.Params?.Uri ?? "(null)" }] + Contents = [new TextResourceContents { Text = request.Params?.Uri ?? "(null)", Uri = request.Params?.Uri ?? "(null)" }] }; } diff --git a/tests/ModelContextProtocol.Tests/Configuration/McpServerOptionsSetupTests.cs b/tests/ModelContextProtocol.Tests/Configuration/McpServerOptionsSetupTests.cs index 151111db2..fc897c2d4 100644 --- a/tests/ModelContextProtocol.Tests/Configuration/McpServerOptionsSetupTests.cs +++ b/tests/ModelContextProtocol.Tests/Configuration/McpServerOptionsSetupTests.cs @@ -16,7 +16,7 @@ public void Configure_WithListPromptsHandler_CreatesPromptsCapability() .WithListPromptsHandler(async (request, ct) => new ListPromptsResult()); var options = services.BuildServiceProvider().GetRequiredService>().Value; - + Assert.NotNull(options.Handlers.ListPromptsHandler); Assert.NotNull(options.Capabilities?.Prompts); } @@ -29,7 +29,7 @@ public void Configure_WithGetPromptHandler_CreatesPromptsCapability() .WithGetPromptHandler(async (request, ct) => new GetPromptResult()); var options = services.BuildServiceProvider().GetRequiredService>().Value; - + Assert.NotNull(options.Handlers.GetPromptHandler); Assert.NotNull(options.Capabilities?.Prompts); } @@ -44,7 +44,7 @@ public void Configure_WithListResourceTemplatesHandler_CreatesResourcesCapabilit .WithListResourceTemplatesHandler(async (request, ct) => new ListResourceTemplatesResult()); var options = services.BuildServiceProvider().GetRequiredService>().Value; - + Assert.NotNull(options.Handlers.ListResourceTemplatesHandler); Assert.NotNull(options.Capabilities?.Resources); } @@ -57,7 +57,7 @@ public void Configure_WithListResourcesHandler_CreatesResourcesCapability() .WithListResourcesHandler(async (request, ct) => new ListResourcesResult()); var options = services.BuildServiceProvider().GetRequiredService>().Value; - + Assert.NotNull(options.Handlers.ListResourcesHandler); Assert.NotNull(options.Capabilities?.Resources); } @@ -70,7 +70,7 @@ public void Configure_WithReadResourceHandler_CreatesResourcesCapability() .WithReadResourceHandler(async (request, ct) => new ReadResourceResult()); var options = services.BuildServiceProvider().GetRequiredService>().Value; - + Assert.NotNull(options.Handlers.ReadResourceHandler); Assert.NotNull(options.Capabilities?.Resources); } @@ -84,7 +84,7 @@ public void Configure_WithSubscribeToResourcesHandler_And_WithOtherResourcesHand .WithSubscribeToResourcesHandler(async (request, ct) => new EmptyResult()); var options = services.BuildServiceProvider().GetRequiredService>().Value; - + Assert.NotNull(options.Handlers.ListResourcesHandler); Assert.NotNull(options.Handlers.SubscribeToResourcesHandler); Assert.NotNull(options.Capabilities?.Resources); @@ -100,7 +100,7 @@ public void Configure_WithUnsubscribeFromResourcesHandler_And_WithOtherResources .WithUnsubscribeFromResourcesHandler(async (request, ct) => new EmptyResult()); var options = services.BuildServiceProvider().GetRequiredService>().Value; - + Assert.NotNull(options.Handlers.ListResourcesHandler); Assert.NotNull(options.Handlers.UnsubscribeFromResourcesHandler); Assert.NotNull(options.Capabilities?.Resources); @@ -115,7 +115,7 @@ public void Configure_WithSubscribeToResourcesHandler_WithoutOtherResourcesHandl .WithSubscribeToResourcesHandler(async (request, ct) => new EmptyResult()); var options = services.BuildServiceProvider().GetRequiredService>().Value; - + Assert.Null(options.Handlers.SubscribeToResourcesHandler); Assert.Null(options.Capabilities?.Resources); } @@ -128,7 +128,7 @@ public void Configure_WithUnsubscribeFromResourcesHandler_WithoutOtherResourcesH .WithUnsubscribeFromResourcesHandler(async (request, ct) => new EmptyResult()); var options = services.BuildServiceProvider().GetRequiredService>().Value; - + Assert.Null(options.Handlers.UnsubscribeFromResourcesHandler); Assert.Null(options.Capabilities?.Resources); } @@ -143,7 +143,7 @@ public void Configure_WithListToolsHandler_CreatesToolsCapability() .WithListToolsHandler(async (request, ct) => new ListToolsResult()); var options = services.BuildServiceProvider().GetRequiredService>().Value; - + Assert.NotNull(options.Handlers.ListToolsHandler); Assert.NotNull(options.Capabilities?.Tools); } @@ -156,7 +156,7 @@ public void Configure_WithCallToolHandler_CreatesToolsCapability() .WithCallToolHandler(async (request, ct) => new CallToolResult()); var options = services.BuildServiceProvider().GetRequiredService>().Value; - + Assert.NotNull(options.Handlers.CallToolHandler); Assert.NotNull(options.Capabilities?.Tools); } @@ -171,7 +171,7 @@ public void Configure_WithSetLoggingLevelHandler_CreatesLoggingCapability() .WithSetLoggingLevelHandler(async (request, ct) => new EmptyResult()); var options = services.BuildServiceProvider().GetRequiredService>().Value; - + Assert.NotNull(options.Handlers.SetLoggingLevelHandler); Assert.NotNull(options.Capabilities?.Logging); } @@ -186,7 +186,7 @@ public void Configure_WithCompleteHandler_CreatesCompletionsCapability() .WithCompleteHandler(async (request, ct) => new CompleteResult()); var options = services.BuildServiceProvider().GetRequiredService>().Value; - + Assert.NotNull(options.Handlers.CompleteHandler); Assert.NotNull(options.Capabilities?.Completions); } diff --git a/tests/ModelContextProtocol.Tests/McpEndpointExtensionsTests.cs b/tests/ModelContextProtocol.Tests/McpEndpointExtensionsTests.cs index 613c703c3..402b1d09d 100644 --- a/tests/ModelContextProtocol.Tests/McpEndpointExtensionsTests.cs +++ b/tests/ModelContextProtocol.Tests/McpEndpointExtensionsTests.cs @@ -57,6 +57,7 @@ public async Task SendRequestAsync_Generic_Forwards_To_McpSession_SendRequestAsy .Setup(s => s.SendRequestAsync(It.IsAny(), It.IsAny())) .ReturnsAsync(new JsonRpcResponse { + Id = default, Result = JsonSerializer.SerializeToNode(42, McpJsonUtilities.DefaultOptions), }); diff --git a/tests/ModelContextProtocol.Tests/Server/McpServerExtensionsTests.cs b/tests/ModelContextProtocol.Tests/Server/McpServerExtensionsTests.cs index 5569f993c..2bfbb098e 100644 --- a/tests/ModelContextProtocol.Tests/Server/McpServerExtensionsTests.cs +++ b/tests/ModelContextProtocol.Tests/Server/McpServerExtensionsTests.cs @@ -16,7 +16,11 @@ public async Task SampleAsync_Request_Throws_When_Not_McpServer() var server = new Mock(MockBehavior.Strict).Object; var ex = await Assert.ThrowsAsync(async () => await server.SampleAsync( - new CreateMessageRequestParams { Messages = [new SamplingMessage { Role = Role.User, Content = new TextContentBlock { Text = "hi" } }] }, + new CreateMessageRequestParams + { + Messages = [new SamplingMessage { Role = Role.User, Content = new TextContentBlock { Text = "hi" } }], + MaxTokens = 1000 + }, TestContext.Current.CancellationToken)); Assert.Contains("Prefer using 'McpServer.SampleAsync' instead", ex.Message); } @@ -90,6 +94,7 @@ public async Task SampleAsync_Request_Forwards_To_McpServer_SendRequestAsync() .Setup(s => s.SendRequestAsync(It.IsAny(), It.IsAny())) .ReturnsAsync(new JsonRpcResponse { + Id = default, Result = JsonSerializer.SerializeToNode(resultPayload, McpJsonUtilities.DefaultOptions), }); @@ -97,7 +102,8 @@ public async Task SampleAsync_Request_Forwards_To_McpServer_SendRequestAsync() var result = await server.SampleAsync(new CreateMessageRequestParams { - Messages = [new SamplingMessage { Role = Role.User, Content = new TextContentBlock { Text = "hi" } }] + Messages = [new SamplingMessage { Role = Role.User, Content = new TextContentBlock { Text = "hi" } }], + MaxTokens = 1000 }, TestContext.Current.CancellationToken); Assert.Equal("test-model", result.Model); @@ -127,6 +133,7 @@ public async Task SampleAsync_Messages_Forwards_To_McpServer_SendRequestAsync() .Setup(s => s.SendRequestAsync(It.IsAny(), It.IsAny())) .ReturnsAsync(new JsonRpcResponse { + Id = default, Result = JsonSerializer.SerializeToNode(resultPayload, McpJsonUtilities.DefaultOptions), }); @@ -156,6 +163,7 @@ public async Task RequestRootsAsync_Forwards_To_McpServer_SendRequestAsync() .Setup(s => s.SendRequestAsync(It.IsAny(), It.IsAny())) .ReturnsAsync(new JsonRpcResponse { + Id = default, Result = JsonSerializer.SerializeToNode(resultPayload, McpJsonUtilities.DefaultOptions), }); @@ -182,6 +190,7 @@ public async Task ElicitAsync_Forwards_To_McpServer_SendRequestAsync() .Setup(s => s.SendRequestAsync(It.IsAny(), It.IsAny())) .ReturnsAsync(new JsonRpcResponse { + Id = default, Result = JsonSerializer.SerializeToNode(resultPayload, McpJsonUtilities.DefaultOptions), }); diff --git a/tests/ModelContextProtocol.Tests/Server/McpServerResourceTests.cs b/tests/ModelContextProtocol.Tests/Server/McpServerResourceTests.cs index 135633b82..7a140cf31 100644 --- a/tests/ModelContextProtocol.Tests/Server/McpServerResourceTests.cs +++ b/tests/ModelContextProtocol.Tests/Server/McpServerResourceTests.cs @@ -537,7 +537,7 @@ public async Task CanReturnReadResult() McpServerResource resource = McpServerResource.Create((McpServer server) => { Assert.Same(mockServer.Object, server); - return new ReadResourceResult { Contents = [new TextResourceContents { Text = "hello" }] }; + return new ReadResourceResult { Contents = [new TextResourceContents { Text = "hello", Uri = string.Empty }] }; }, new() { Name = "Test" }); var result = await resource.ReadAsync( new RequestContext(mockServer.Object, CreateTestJsonRpcRequest()) { Params = new() { Uri = "resource://mcp/Test" } }, @@ -554,7 +554,7 @@ public async Task CanReturnResourceContents() McpServerResource resource = McpServerResource.Create((McpServer server) => { Assert.Same(mockServer.Object, server); - return new TextResourceContents { Text = "hello" }; + return new TextResourceContents { Text = "hello", Uri = string.Empty }; }, new() { Name = "Test", SerializerOptions = JsonContext6.Default.Options }); var result = await resource.ReadAsync( new RequestContext(mockServer.Object, CreateTestJsonRpcRequest()) { Params = new() { Uri = "resource://mcp/Test" } }, @@ -573,8 +573,8 @@ public async Task CanReturnCollectionOfResourceContents() Assert.Same(mockServer.Object, server); return (IList) [ - new TextResourceContents { Text = "hello" }, - new BlobResourceContents { Blob = Convert.ToBase64String(new byte[] { 1, 2, 3 }) }, + new TextResourceContents { Text = "hello", Uri = string.Empty }, + new BlobResourceContents { Blob = Convert.ToBase64String(new byte[] { 1, 2, 3 }), Uri = string.Empty }, ]; }, new() { Name = "Test" }); var result = await resource.ReadAsync( diff --git a/tests/ModelContextProtocol.Tests/Server/McpServerTests.cs b/tests/ModelContextProtocol.Tests/Server/McpServerTests.cs index 4352570e7..bbda5b2ea 100644 --- a/tests/ModelContextProtocol.Tests/Server/McpServerTests.cs +++ b/tests/ModelContextProtocol.Tests/Server/McpServerTests.cs @@ -130,7 +130,7 @@ public async Task SampleAsync_Should_Throw_Exception_If_Client_Does_Not_Support_ await using var server = McpServer.Create(transport, _options, LoggerFactory); SetClientCapabilities(server, new ClientCapabilities()); - var action = async () => await server.SampleAsync(new CreateMessageRequestParams { Messages = [] }, CancellationToken.None); + var action = async () => await server.SampleAsync(new CreateMessageRequestParams { Messages = [], MaxTokens = 1000 }, CancellationToken.None); // Act & Assert await Assert.ThrowsAsync(action); @@ -147,7 +147,7 @@ public async Task SampleAsync_Should_SendRequest() var runTask = server.RunAsync(TestContext.Current.CancellationToken); // Act - var result = await server.SampleAsync(new CreateMessageRequestParams { Messages = [] }, CancellationToken.None); + var result = await server.SampleAsync(new CreateMessageRequestParams { Messages = [], MaxTokens = 1000 }, CancellationToken.None); Assert.NotNull(result); Assert.NotEmpty(transport.SentMessages); @@ -201,7 +201,7 @@ public async Task ElicitAsync_Should_Throw_Exception_If_Client_Does_Not_Support_ SetClientCapabilities(server, new ClientCapabilities()); // Act & Assert - await Assert.ThrowsAsync(async () => await server.ElicitAsync(new ElicitRequestParams(), CancellationToken.None)); + await Assert.ThrowsAsync(async () => await server.ElicitAsync(new ElicitRequestParams { Message = string.Empty }, CancellationToken.None)); } [Fact] @@ -214,7 +214,7 @@ public async Task ElicitAsync_Should_SendRequest() var runTask = server.RunAsync(TestContext.Current.CancellationToken); // Act - var result = await server.ElicitAsync(new ElicitRequestParams(), CancellationToken.None); + var result = await server.ElicitAsync(new ElicitRequestParams { Message = string.Empty }, CancellationToken.None); // Assert Assert.NotNull(result); @@ -369,7 +369,7 @@ await Can_Handle_Requests( new ServerCapabilities { Resources = new() - }, + }, method: RequestMethods.ResourcesRead, configureOptions: options => { @@ -377,7 +377,7 @@ await Can_Handle_Requests( { return new ReadResourceResult { - Contents = [new TextResourceContents { Text = "test" }] + Contents = [new TextResourceContents { Text = "test", Uri = string.Empty }] }; }; options.Handlers.ListResourcesHandler = (request, ct) => throw new NotImplementedException(); @@ -438,7 +438,7 @@ public async Task Can_Handle_List_Prompts_Requests_Throws_Exception_If_No_Handle public async Task Can_Handle_Get_Prompts_Requests() { await Can_Handle_Requests( - new ServerCapabilities + new ServerCapabilities { Prompts = new() }, @@ -466,7 +466,7 @@ public async Task Can_Handle_Get_Prompts_Requests_Throws_Exception_If_No_Handler public async Task Can_Handle_List_Tools_Requests() { await Can_Handle_Requests( - new ServerCapabilities + new ServerCapabilities { Tools = new() }, @@ -504,7 +504,7 @@ await Can_Handle_Requests( new ServerCapabilities { Tools = new() - }, + }, method: RequestMethods.ToolsCall, configureOptions: options => { @@ -659,7 +659,7 @@ await transport.SendMessageAsync( { Method = method, Id = new RequestId(55) - } + } ); var response = await receivedMessage.Task.WaitAsync(TimeSpan.FromSeconds(5)); @@ -779,7 +779,7 @@ public override Task SendRequestAsync(JsonRpcRequest request, C }; return Task.FromResult(new JsonRpcResponse - { + { Id = new RequestId("0"), Result = JsonSerializer.SerializeToNode(result, McpJsonUtilities.DefaultOptions), });