From cdc77319a8bd0b2bf091510abd148f1ccb5f7a33 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Wed, 29 Oct 2025 17:55:15 +0000 Subject: [PATCH 1/5] Initial plan From 63e10662455f2ad35fa39f77c08ef57d2e8d7a55 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Wed, 29 Oct 2025 18:08:26 +0000 Subject: [PATCH 2/5] Add DefaultSamplingMaxTokens property to McpServerOptions Co-authored-by: MackinnonBuck <10456961+MackinnonBuck@users.noreply.github.com> --- .../Server/McpServer.Methods.cs | 2 +- .../Server/McpServerOptions.cs | 17 +++++++++++++++++ .../Server/McpServerExtensionsTests.cs | 4 ++++ 3 files changed, 22 insertions(+), 1 deletion(-) diff --git a/src/ModelContextProtocol.Core/Server/McpServer.Methods.cs b/src/ModelContextProtocol.Core/Server/McpServer.Methods.cs index a48e41b6..0f55fa2e 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 ?? int.MaxValue, + MaxTokens = options?.MaxOutputTokens ?? ServerOptions.DefaultSamplingMaxTokens, StopSequences = options?.StopSequences?.ToArray(), SystemPrompt = systemPrompt?.ToString(), Temperature = options?.Temperature, diff --git a/src/ModelContextProtocol.Core/Server/McpServerOptions.cs b/src/ModelContextProtocol.Core/Server/McpServerOptions.cs index 618e87d5..a2c498b4 100644 --- a/src/ModelContextProtocol.Core/Server/McpServerOptions.cs +++ b/src/ModelContextProtocol.Core/Server/McpServerOptions.cs @@ -152,4 +152,21 @@ public McpServerHandlers Handlers /// /// public McpServerPrimitiveCollection? PromptCollection { get; set; } + + /// + /// Gets or sets the default maximum number of tokens to use for sampling requests when not explicitly specified. + /// + /// + /// + /// This value is used as the maxTokens parameter in sampling requests to the client when the + /// property is not set in the request options. + /// The MCP protocol requires a maxTokens value for all sampling requests. + /// + /// + /// The default value is 1000 tokens, which provides a reasonable balance between allowing meaningful + /// responses and preventing excessive token usage. This value should be set based on your application's + /// requirements and the capabilities of the LLM being used by the client. + /// + /// + public int DefaultSamplingMaxTokens { get; set; } = 1000; } diff --git a/tests/ModelContextProtocol.Tests/Server/McpServerExtensionsTests.cs b/tests/ModelContextProtocol.Tests/Server/McpServerExtensionsTests.cs index 2bfbb098..e70d919d 100644 --- a/tests/ModelContextProtocol.Tests/Server/McpServerExtensionsTests.cs +++ b/tests/ModelContextProtocol.Tests/Server/McpServerExtensionsTests.cs @@ -129,6 +129,10 @@ public async Task SampleAsync_Messages_Forwards_To_McpServer_SendRequestAsync() .Setup(s => s.ClientCapabilities) .Returns(new ClientCapabilities() { Sampling = new() }); + mockServer + .Setup(s => s.ServerOptions) + .Returns(new McpServerOptions()); + mockServer .Setup(s => s.SendRequestAsync(It.IsAny(), It.IsAny())) .ReturnsAsync(new JsonRpcResponse From 39a2f47ed2c395f210fd683139e1a51519e37fcc Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Wed, 29 Oct 2025 23:11:52 +0000 Subject: [PATCH 3/5] Add test to verify DefaultSamplingMaxTokens is respected Co-authored-by: MackinnonBuck <10456961+MackinnonBuck@users.noreply.github.com> --- .../Server/McpServerExtensionsTests.cs | 48 +++++++++++++++++++ 1 file changed, 48 insertions(+) diff --git a/tests/ModelContextProtocol.Tests/Server/McpServerExtensionsTests.cs b/tests/ModelContextProtocol.Tests/Server/McpServerExtensionsTests.cs index e70d919d..14475874 100644 --- a/tests/ModelContextProtocol.Tests/Server/McpServerExtensionsTests.cs +++ b/tests/ModelContextProtocol.Tests/Server/McpServerExtensionsTests.cs @@ -152,6 +152,54 @@ public async Task SampleAsync_Messages_Forwards_To_McpServer_SendRequestAsync() mockServer.Verify(s => s.SendRequestAsync(It.IsAny(), It.IsAny()), Times.Once); } + [Fact] + public async Task SampleAsync_Messages_UsesDefaultSamplingMaxTokens_WhenNotSpecified() + { + var mockServer = new Mock { CallBase = true }; + + var resultPayload = new CreateMessageResult + { + Content = new TextContentBlock { Text = "resp" }, + Model = "test-model", + Role = Role.Assistant, + StopReason = "endTurn", + }; + + const int customDefaultMaxTokens = 500; + + mockServer + .Setup(s => s.ClientCapabilities) + .Returns(new ClientCapabilities() { Sampling = new() }); + + mockServer + .Setup(s => s.ServerOptions) + .Returns(new McpServerOptions { DefaultSamplingMaxTokens = customDefaultMaxTokens }); + + CreateMessageRequestParams? capturedRequest = null; + mockServer + .Setup(s => s.SendRequestAsync(It.IsAny(), It.IsAny())) + .Callback((request, _) => + { + capturedRequest = JsonSerializer.Deserialize( + request.Params ?? throw new InvalidOperationException(), + McpJsonUtilities.DefaultOptions); + }) + .ReturnsAsync(new JsonRpcResponse + { + Id = default, + Result = JsonSerializer.SerializeToNode(resultPayload, McpJsonUtilities.DefaultOptions), + }); + + IMcpServer server = mockServer.Object; + + // Call SampleAsync without specifying MaxOutputTokens in options + await server.SampleAsync([new ChatMessage(ChatRole.User, "hi")], cancellationToken: TestContext.Current.CancellationToken); + + // Verify that the default value was used + Assert.NotNull(capturedRequest); + Assert.Equal(customDefaultMaxTokens, capturedRequest.MaxTokens); + } + [Fact] public async Task RequestRootsAsync_Forwards_To_McpServer_SendRequestAsync() { From c528ccb81c256da41fadb915718e15995b9f85de Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Wed, 29 Oct 2025 23:23:20 +0000 Subject: [PATCH 4/5] Merge test into existing SampleAsync_Messages_Forwards_To_McpServer_SendRequestAsync Co-authored-by: MackinnonBuck <10456961+MackinnonBuck@users.noreply.github.com> --- .../Server/McpServerExtensionsTests.cs | 49 +++---------------- 1 file changed, 7 insertions(+), 42 deletions(-) diff --git a/tests/ModelContextProtocol.Tests/Server/McpServerExtensionsTests.cs b/tests/ModelContextProtocol.Tests/Server/McpServerExtensionsTests.cs index 14475874..55fcc0ea 100644 --- a/tests/ModelContextProtocol.Tests/Server/McpServerExtensionsTests.cs +++ b/tests/ModelContextProtocol.Tests/Server/McpServerExtensionsTests.cs @@ -125,46 +125,6 @@ public async Task SampleAsync_Messages_Forwards_To_McpServer_SendRequestAsync() StopReason = "endTurn", }; - mockServer - .Setup(s => s.ClientCapabilities) - .Returns(new ClientCapabilities() { Sampling = new() }); - - mockServer - .Setup(s => s.ServerOptions) - .Returns(new McpServerOptions()); - - mockServer - .Setup(s => s.SendRequestAsync(It.IsAny(), It.IsAny())) - .ReturnsAsync(new JsonRpcResponse - { - Id = default, - Result = JsonSerializer.SerializeToNode(resultPayload, McpJsonUtilities.DefaultOptions), - }); - - IMcpServer server = mockServer.Object; - - var chatResponse = await server.SampleAsync([new ChatMessage(ChatRole.User, "hi")], cancellationToken: TestContext.Current.CancellationToken); - - Assert.Equal("test-model", chatResponse.ModelId); - var last = chatResponse.Messages.Last(); - Assert.Equal(ChatRole.Assistant, last.Role); - Assert.Equal("resp", last.Text); - mockServer.Verify(s => s.SendRequestAsync(It.IsAny(), It.IsAny()), Times.Once); - } - - [Fact] - public async Task SampleAsync_Messages_UsesDefaultSamplingMaxTokens_WhenNotSpecified() - { - var mockServer = new Mock { CallBase = true }; - - var resultPayload = new CreateMessageResult - { - Content = new TextContentBlock { Text = "resp" }, - Model = "test-model", - Role = Role.Assistant, - StopReason = "endTurn", - }; - const int customDefaultMaxTokens = 500; mockServer @@ -192,9 +152,14 @@ public async Task SampleAsync_Messages_UsesDefaultSamplingMaxTokens_WhenNotSpeci IMcpServer server = mockServer.Object; - // Call SampleAsync without specifying MaxOutputTokens in options - await server.SampleAsync([new ChatMessage(ChatRole.User, "hi")], cancellationToken: TestContext.Current.CancellationToken); + var chatResponse = await server.SampleAsync([new ChatMessage(ChatRole.User, "hi")], cancellationToken: TestContext.Current.CancellationToken); + Assert.Equal("test-model", chatResponse.ModelId); + var last = chatResponse.Messages.Last(); + Assert.Equal(ChatRole.Assistant, last.Role); + Assert.Equal("resp", last.Text); + mockServer.Verify(s => s.SendRequestAsync(It.IsAny(), It.IsAny()), Times.Once); + // Verify that the default value was used Assert.NotNull(capturedRequest); Assert.Equal(customDefaultMaxTokens, capturedRequest.MaxTokens); From 274d9a9c06793463eb05b4d343a8ea8102ef26b1 Mon Sep 17 00:00:00 2001 From: Mackinnon Buck Date: Wed, 29 Oct 2025 16:33:58 -0700 Subject: [PATCH 5/5] Update src/ModelContextProtocol.Core/Server/McpServerOptions.cs --- src/ModelContextProtocol.Core/Server/McpServerOptions.cs | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/src/ModelContextProtocol.Core/Server/McpServerOptions.cs b/src/ModelContextProtocol.Core/Server/McpServerOptions.cs index a2c498b4..e7bf1cf6 100644 --- a/src/ModelContextProtocol.Core/Server/McpServerOptions.cs +++ b/src/ModelContextProtocol.Core/Server/McpServerOptions.cs @@ -158,14 +158,10 @@ public McpServerHandlers Handlers /// /// /// - /// This value is used as the maxTokens parameter in sampling requests to the client when the - /// property is not set in the request options. - /// The MCP protocol requires a maxTokens value for all sampling requests. + /// This value is used when is not set in the request options. /// /// - /// The default value is 1000 tokens, which provides a reasonable balance between allowing meaningful - /// responses and preventing excessive token usage. This value should be set based on your application's - /// requirements and the capabilities of the LLM being used by the client. + /// The default value is 1000 tokens. /// /// public int DefaultSamplingMaxTokens { get; set; } = 1000;