From 8c7d1550540747aa3d5a08d4cf0c33d97af0147f Mon Sep 17 00:00:00 2001 From: PederHP Date: Tue, 5 Aug 2025 21:26:56 +0200 Subject: [PATCH 1/7] Resource subscription is now supported --- src/ModelContextProtocol.Core/Server/McpServer.cs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/ModelContextProtocol.Core/Server/McpServer.cs b/src/ModelContextProtocol.Core/Server/McpServer.cs index 6c5858f91..e14d0add3 100644 --- a/src/ModelContextProtocol.Core/Server/McpServer.cs +++ b/src/ModelContextProtocol.Core/Server/McpServer.cs @@ -308,8 +308,7 @@ await originalListResourceTemplatesHandler(request, cancellationToken).Configure listChanged = true; - // TODO: Implement subscribe/unsubscribe logic for resource and resource template collections. - // subscribe = true; + subscribe = true; } ServerCapabilities.Resources.ListResourcesHandler = listResourcesHandler; From d2d98f14c4c9fae54d743df7455397986c867643 Mon Sep 17 00:00:00 2001 From: PederHP Date: Tue, 5 Aug 2025 21:42:12 +0200 Subject: [PATCH 2/7] Allow subscribing to resources, but subscribe is only true if full set of handlers present. --- src/ModelContextProtocol.Core/Server/McpServer.cs | 3 ++- src/ModelContextProtocol/McpServerHandlers.cs | 9 +++++++-- 2 files changed, 9 insertions(+), 3 deletions(-) diff --git a/src/ModelContextProtocol.Core/Server/McpServer.cs b/src/ModelContextProtocol.Core/Server/McpServer.cs index e14d0add3..a4d72c732 100644 --- a/src/ModelContextProtocol.Core/Server/McpServer.cs +++ b/src/ModelContextProtocol.Core/Server/McpServer.cs @@ -308,7 +308,8 @@ await originalListResourceTemplatesHandler(request, cancellationToken).Configure listChanged = true; - subscribe = true; + // TODO: Implement subscribe/unsubscribe logic for resource and resource template collections + // subscribe = true; } ServerCapabilities.Resources.ListResourcesHandler = listResourcesHandler; diff --git a/src/ModelContextProtocol/McpServerHandlers.cs b/src/ModelContextProtocol/McpServerHandlers.cs index a07c81b54..8dcebeec1 100644 --- a/src/ModelContextProtocol/McpServerHandlers.cs +++ b/src/ModelContextProtocol/McpServerHandlers.cs @@ -188,12 +188,17 @@ internal void OverwriteWithSetHandlers(McpServerOptions options) if (SubscribeToResourcesHandler is not null || UnsubscribeFromResourcesHandler is not null) { - resourcesCapability.SubscribeToResourcesHandler = SubscribeToResourcesHandler ?? resourcesCapability.SubscribeToResourcesHandler; - resourcesCapability.UnsubscribeFromResourcesHandler = UnsubscribeFromResourcesHandler ?? resourcesCapability.UnsubscribeFromResourcesHandler; resourcesCapability.Subscribe = true; } } + if (SubscribeToResourcesHandler is not null || UnsubscribeFromResourcesHandler is not null) + { + resourcesCapability.SubscribeToResourcesHandler = SubscribeToResourcesHandler ?? resourcesCapability.SubscribeToResourcesHandler; + resourcesCapability.UnsubscribeFromResourcesHandler = UnsubscribeFromResourcesHandler ?? resourcesCapability.UnsubscribeFromResourcesHandler; + } + + ToolsCapability? toolsCapability = options.Capabilities?.Tools; if (ListToolsHandler is not null || CallToolHandler is not null) { From ddf598430c855883338dfa669923c48b504adee8 Mon Sep 17 00:00:00 2001 From: PederHP Date: Tue, 5 Aug 2025 21:42:12 +0200 Subject: [PATCH 3/7] Allow subscribing to resources, but subscribe is only true if full set of handlers present. --- src/ModelContextProtocol.Core/Server/McpServer.cs | 3 ++- src/ModelContextProtocol/McpServerHandlers.cs | 12 ++++++------ 2 files changed, 8 insertions(+), 7 deletions(-) diff --git a/src/ModelContextProtocol.Core/Server/McpServer.cs b/src/ModelContextProtocol.Core/Server/McpServer.cs index e14d0add3..a4d72c732 100644 --- a/src/ModelContextProtocol.Core/Server/McpServer.cs +++ b/src/ModelContextProtocol.Core/Server/McpServer.cs @@ -308,7 +308,8 @@ await originalListResourceTemplatesHandler(request, cancellationToken).Configure listChanged = true; - subscribe = true; + // TODO: Implement subscribe/unsubscribe logic for resource and resource template collections + // subscribe = true; } ServerCapabilities.Resources.ListResourcesHandler = listResourcesHandler; diff --git a/src/ModelContextProtocol/McpServerHandlers.cs b/src/ModelContextProtocol/McpServerHandlers.cs index a07c81b54..0af79e97f 100644 --- a/src/ModelContextProtocol/McpServerHandlers.cs +++ b/src/ModelContextProtocol/McpServerHandlers.cs @@ -185,13 +185,13 @@ internal void OverwriteWithSetHandlers(McpServerOptions options) resourcesCapability.ListResourceTemplatesHandler = ListResourceTemplatesHandler ?? resourcesCapability.ListResourceTemplatesHandler; resourcesCapability.ListResourcesHandler = ListResourcesHandler ?? resourcesCapability.ListResourcesHandler; resourcesCapability.ReadResourceHandler = ReadResourceHandler ?? resourcesCapability.ReadResourceHandler; + } - if (SubscribeToResourcesHandler is not null || UnsubscribeFromResourcesHandler is not null) - { - resourcesCapability.SubscribeToResourcesHandler = SubscribeToResourcesHandler ?? resourcesCapability.SubscribeToResourcesHandler; - resourcesCapability.UnsubscribeFromResourcesHandler = UnsubscribeFromResourcesHandler ?? resourcesCapability.UnsubscribeFromResourcesHandler; - resourcesCapability.Subscribe = true; - } + if (SubscribeToResourcesHandler is not null || UnsubscribeFromResourcesHandler is not null) + { + resourcesCapability.SubscribeToResourcesHandler = SubscribeToResourcesHandler ?? resourcesCapability.SubscribeToResourcesHandler; + resourcesCapability.UnsubscribeFromResourcesHandler = UnsubscribeFromResourcesHandler ?? resourcesCapability.UnsubscribeFromResourcesHandler; + resourcesCapability.Subscribe = true; } ToolsCapability? toolsCapability = options.Capabilities?.Tools; From 318bd9e3437457fa7017d7fc2669761d3c13f469 Mon Sep 17 00:00:00 2001 From: PederHP Date: Tue, 5 Aug 2025 21:47:07 +0200 Subject: [PATCH 4/7] Tweak --- src/ModelContextProtocol.Core/Server/McpServer.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/ModelContextProtocol.Core/Server/McpServer.cs b/src/ModelContextProtocol.Core/Server/McpServer.cs index a4d72c732..6c5858f91 100644 --- a/src/ModelContextProtocol.Core/Server/McpServer.cs +++ b/src/ModelContextProtocol.Core/Server/McpServer.cs @@ -308,7 +308,7 @@ await originalListResourceTemplatesHandler(request, cancellationToken).Configure listChanged = true; - // TODO: Implement subscribe/unsubscribe logic for resource and resource template collections + // TODO: Implement subscribe/unsubscribe logic for resource and resource template collections. // subscribe = true; } From 78433a76e17ddeab2c2dea5e232018e36c84199e Mon Sep 17 00:00:00 2001 From: Stephen Halter Date: Thu, 11 Dec 2025 18:24:50 -0800 Subject: [PATCH 5/7] Apply suggestion from @mikekistler Co-authored-by: Mike Kistler --- src/ModelContextProtocol/McpServerHandlers.cs | 1 + 1 file changed, 1 insertion(+) diff --git a/src/ModelContextProtocol/McpServerHandlers.cs b/src/ModelContextProtocol/McpServerHandlers.cs index 0af79e97f..568cc0d5a 100644 --- a/src/ModelContextProtocol/McpServerHandlers.cs +++ b/src/ModelContextProtocol/McpServerHandlers.cs @@ -189,6 +189,7 @@ internal void OverwriteWithSetHandlers(McpServerOptions options) if (SubscribeToResourcesHandler is not null || UnsubscribeFromResourcesHandler is not null) { + resourcesCapability ??= new(); resourcesCapability.SubscribeToResourcesHandler = SubscribeToResourcesHandler ?? resourcesCapability.SubscribeToResourcesHandler; resourcesCapability.UnsubscribeFromResourcesHandler = UnsubscribeFromResourcesHandler ?? resourcesCapability.UnsubscribeFromResourcesHandler; resourcesCapability.Subscribe = true; From 95289a76df993fb4dba95451b872a1c816d21aee Mon Sep 17 00:00:00 2001 From: Stephen Halter Date: Thu, 11 Dec 2025 19:05:13 -0800 Subject: [PATCH 6/7] Update McpServerOptionsSetupTests to reflect new behavior --- .../Configuration/McpServerOptionsSetupTests.cs | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/tests/ModelContextProtocol.Tests/Configuration/McpServerOptionsSetupTests.cs b/tests/ModelContextProtocol.Tests/Configuration/McpServerOptionsSetupTests.cs index 27a3580ab..6f770bf5d 100644 --- a/tests/ModelContextProtocol.Tests/Configuration/McpServerOptionsSetupTests.cs +++ b/tests/ModelContextProtocol.Tests/Configuration/McpServerOptionsSetupTests.cs @@ -108,7 +108,7 @@ public void Configure_WithUnsubscribeFromResourcesHandler_And_WithOtherResources } [Fact] - public void Configure_WithSubscribeToResourcesHandler_WithoutOtherResourcesHandler_DoesNotCreateResourcesCapability() + public void Configure_WithSubscribeToResourcesHandler_WithoutOtherResourcesHandler_DoesCreateResourcesCapability() { var services = new ServiceCollection(); services.AddMcpServer() @@ -116,12 +116,13 @@ public void Configure_WithSubscribeToResourcesHandler_WithoutOtherResourcesHandl var options = services.BuildServiceProvider().GetRequiredService>().Value; - Assert.Null(options.Handlers.SubscribeToResourcesHandler); - Assert.Null(options.Capabilities?.Resources); + Assert.NotNull(options.Handlers.SubscribeToResourcesHandler); + Assert.NotNull(options.Capabilities?.Resources); + Assert.True(options.Capabilities.Resources.Subscribe); } [Fact] - public void Configure_WithUnsubscribeFromResourcesHandler_WithoutOtherResourcesHandler_DoesNotCreateResourcesCapability() + public void Configure_WithUnsubscribeFromResourcesHandler_WithoutOtherResourcesHandler_DoesCreateResourcesCapability() { var services = new ServiceCollection(); services.AddMcpServer() @@ -129,8 +130,9 @@ public void Configure_WithUnsubscribeFromResourcesHandler_WithoutOtherResourcesH var options = services.BuildServiceProvider().GetRequiredService>().Value; - Assert.Null(options.Handlers.UnsubscribeFromResourcesHandler); - Assert.Null(options.Capabilities?.Resources); + Assert.NotNull(options.Handlers.UnsubscribeFromResourcesHandler); + Assert.NotNull(options.Capabilities?.Resources); + Assert.True(options.Capabilities.Resources.Subscribe); } [Fact] From 131fbbe244550d0214d3fdb0c18cb363979d3388 Mon Sep 17 00:00:00 2001 From: Stephen Halter Date: Thu, 11 Dec 2025 20:16:59 -0800 Subject: [PATCH 7/7] Fix ServerConformanceTests --- .../ServerConformanceTests.cs | 15 --------------- .../Program.cs | 18 +++--------------- 2 files changed, 3 insertions(+), 30 deletions(-) diff --git a/tests/ModelContextProtocol.AspNetCore.Tests/ServerConformanceTests.cs b/tests/ModelContextProtocol.AspNetCore.Tests/ServerConformanceTests.cs index 75888c2d8..8c0055fe8 100644 --- a/tests/ModelContextProtocol.AspNetCore.Tests/ServerConformanceTests.cs +++ b/tests/ModelContextProtocol.AspNetCore.Tests/ServerConformanceTests.cs @@ -110,21 +110,6 @@ public async Task RunConformanceTests() private void StartConformanceServer() { - // The ConformanceServer binary is in a parallel directory to the test binary - // Test binary is in: artifacts/bin/ModelContextProtocol.ConformanceTests/Debug/{tfm}/ - // ConformanceServer binary is in: artifacts/bin/ModelContextProtocol.ConformanceServer/Debug/{tfm}/ - var testBinaryDir = AppContext.BaseDirectory; // e.g., .../net10.0/ - var configuration = Path.GetFileName(Path.GetDirectoryName(testBinaryDir.TrimEnd(Path.DirectorySeparatorChar))!); - var targetFramework = Path.GetFileName(testBinaryDir.TrimEnd(Path.DirectorySeparatorChar)); - var conformanceServerDir = Path.GetFullPath( - Path.Combine(testBinaryDir, "..", "..", "..", "ModelContextProtocol.ConformanceServer", configuration, targetFramework)); - - if (!Directory.Exists(conformanceServerDir)) - { - throw new DirectoryNotFoundException( - $"ConformanceServer directory not found at: {conformanceServerDir}"); - } - // Start the server in a background task _serverCts = new CancellationTokenSource(); _serverTask = Task.Run(() => ConformanceServer.Program.MainAsync(["--urls", _serverUrl], new XunitLoggerProvider(_output), cancellationToken: _serverCts.Token)); diff --git a/tests/ModelContextProtocol.ConformanceServer/Program.cs b/tests/ModelContextProtocol.ConformanceServer/Program.cs index e9c810fba..10422678d 100644 --- a/tests/ModelContextProtocol.ConformanceServer/Program.cs +++ b/tests/ModelContextProtocol.ConformanceServer/Program.cs @@ -1,12 +1,9 @@ using ConformanceServer.Prompts; using ConformanceServer.Resources; using ConformanceServer.Tools; -using Microsoft.Extensions.AI; using ModelContextProtocol.Protocol; using System.Collections.Concurrent; using System.Text.Json; -using System.Text.Json.Nodes; -using System.Text.Json.Serialization; namespace ModelContextProtocol.ConformanceServer; @@ -41,18 +38,8 @@ public static async Task MainAsync(string[] args, ILoggerProvider? loggerProvide } if (ctx.Params?.Uri is { } uri) { - subscriptions[ctx.Server.SessionId].TryAdd(uri, 0); - - await ctx.Server.SampleAsync([ - new ChatMessage(ChatRole.System, "You are a helpful test server"), - new ChatMessage(ChatRole.User, $"Resource {uri}, context: A new subscription was started"), - ], - chatOptions: new ChatOptions - { - MaxOutputTokens = 100, - Temperature = 0.7f, - }, - cancellationToken: ct); + var sessionSubscriptions = subscriptions.GetOrAdd(ctx.Server.SessionId, _ => new()); + sessionSubscriptions.TryAdd(uri, 0); } return new EmptyResult(); @@ -67,6 +54,7 @@ await ctx.Server.SampleAsync([ { subscriptions[ctx.Server.SessionId].TryRemove(uri, out _); } + return new EmptyResult(); }) .WithCompleteHandler(async (ctx, ct) =>