Skip to content

Commit 8d4f51d

Browse files
authored
Fix McpServerResource registration (#411)
- Add missing filling of ResourceCollection in McpServerOptionsSetup - Remove AIFunctionMcpServerXx.ToString overrides; for resources, it was changing behavior undesirably, and for the others it was duplicate code. - Fix notification method used for resource list changes. - Fix type in WithResources parameter name.
1 parent ce9f73f commit 8d4f51d

File tree

7 files changed

+330
-34
lines changed

7 files changed

+330
-34
lines changed

src/ModelContextProtocol/Configuration/McpServerBuilderExtensions.cs

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -334,16 +334,16 @@ where t.GetCustomAttribute<McpServerPromptTypeAttribute>() is not null
334334

335335
/// <summary>Adds <see cref="McpServerResource"/> instances to the service collection backing <paramref name="builder"/>.</summary>
336336
/// <param name="builder">The builder instance.</param>
337-
/// <param name="resourcetemplates">The <see cref="McpServerResource"/> instances to add to the server.</param>
337+
/// <param name="resourceTemplates">The <see cref="McpServerResource"/> instances to add to the server.</param>
338338
/// <returns>The builder provided in <paramref name="builder"/>.</returns>
339339
/// <exception cref="ArgumentNullException"><paramref name="builder"/> is <see langword="null"/>.</exception>
340-
/// <exception cref="ArgumentNullException"><paramref name="resourcetemplates"/> is <see langword="null"/>.</exception>
341-
public static IMcpServerBuilder WithResources(this IMcpServerBuilder builder, IEnumerable<McpServerResource> resourcetemplates)
340+
/// <exception cref="ArgumentNullException"><paramref name="resourceTemplates"/> is <see langword="null"/>.</exception>
341+
public static IMcpServerBuilder WithResources(this IMcpServerBuilder builder, IEnumerable<McpServerResource> resourceTemplates)
342342
{
343343
Throw.IfNull(builder);
344-
Throw.IfNull(resourcetemplates);
344+
Throw.IfNull(resourceTemplates);
345345

346-
foreach (var resourceTemplate in resourcetemplates)
346+
foreach (var resourceTemplate in resourceTemplates)
347347
{
348348
if (resourceTemplate is not null)
349349
{
@@ -409,7 +409,7 @@ public static IMcpServerBuilder WithResources(this IMcpServerBuilder builder, IE
409409
/// of the containing class will be constructed for each invocation of the resource.
410410
/// </para>
411411
/// <para>
412-
/// Resource templates registered through this method can be discovered by clients using the <c>list_resourcetemplates</c> request
412+
/// Resource templates registered through this method can be discovered by clients using the <c>list_resourceTemplates</c> request
413413
/// and invoked using the <c>read_resource</c> request.
414414
/// </para>
415415
/// <para>

src/ModelContextProtocol/Configuration/McpServerOptionsSetup.cs

Lines changed: 20 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,10 +10,12 @@ namespace Microsoft.Extensions.DependencyInjection;
1010
/// <param name="serverHandlers">The server handlers configuration options.</param>
1111
/// <param name="serverTools">Tools individually registered.</param>
1212
/// <param name="serverPrompts">Prompts individually registered.</param>
13+
/// <param name="serverResources">Resources individually registered.</param>
1314
internal sealed class McpServerOptionsSetup(
1415
IOptions<McpServerHandlers> serverHandlers,
1516
IEnumerable<McpServerTool> serverTools,
16-
IEnumerable<McpServerPrompt> serverPrompts) : IConfigureOptions<McpServerOptions>
17+
IEnumerable<McpServerPrompt> serverPrompts,
18+
IEnumerable<McpServerResource> serverResources) : IConfigureOptions<McpServerOptions>
1719
{
1820
/// <summary>
1921
/// Configures the given McpServerOptions instance by setting server information
@@ -58,6 +60,23 @@ public void Configure(McpServerOptions options)
5860
options.Capabilities.Prompts.PromptCollection = promptCollection;
5961
}
6062

63+
// Collect all of the provided resources into a resources collection. If the options already has
64+
// a collection, add to it, otherwise create a new one. We want to maintain the identity
65+
// of an existing collection in case someone has provided their own derived type, wants
66+
// change notifications, etc.
67+
McpServerPrimitiveCollection<McpServerResource> resourceCollection = options.Capabilities?.Resources?.ResourceCollection ?? [];
68+
foreach (var resource in serverResources)
69+
{
70+
resourceCollection.TryAdd(resource);
71+
}
72+
73+
if (!resourceCollection.IsEmpty)
74+
{
75+
options.Capabilities ??= new();
76+
options.Capabilities.Resources ??= new();
77+
options.Capabilities.Resources.ResourceCollection = resourceCollection;
78+
}
79+
6180
// Apply custom server handlers.
6281
serverHandlers.Value.OverwriteWithSetHandlers(options);
6382
}

src/ModelContextProtocol/Server/AIFunctionMcpServerPrompt.cs

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -216,9 +216,6 @@ private AIFunctionMcpServerPrompt(AIFunction function, Prompt prompt)
216216
ProtocolPrompt = prompt;
217217
}
218218

219-
/// <inheritdoc />
220-
public override string ToString() => AIFunction.ToString();
221-
222219
/// <inheritdoc />
223220
public override Prompt ProtocolPrompt { get; }
224221

src/ModelContextProtocol/Server/AIFunctionMcpServerResource.cs

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -331,9 +331,6 @@ private AIFunctionMcpServerResource(AIFunction function, ResourceTemplate resour
331331
}
332332
}
333333

334-
/// <inheritdoc />
335-
public override string ToString() => AIFunction.ToString();
336-
337334
/// <inheritdoc />
338335
public override ResourceTemplate ProtocolResourceTemplate { get; }
339336

src/ModelContextProtocol/Server/AIFunctionMcpServerTool.cs

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -247,9 +247,6 @@ private AIFunctionMcpServerTool(AIFunction function, Tool tool)
247247
ProtocolTool = tool;
248248
}
249249

250-
/// <inheritdoc />
251-
public override string ToString() => AIFunction.ToString();
252-
253250
/// <inheritdoc />
254251
public override Tool ProtocolTool { get; }
255252

src/ModelContextProtocol/Server/McpServer.cs

Lines changed: 11 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -81,26 +81,19 @@ public McpServer(ITransport transport, McpServerOptions options, ILoggerFactory?
8181
// Now that everything has been configured, subscribe to any necessary notifications.
8282
if (transport is not StreamableHttpServerTransport streamableHttpTransport || streamableHttpTransport.Stateless is false)
8383
{
84-
if (ServerOptions.Capabilities?.Tools?.ToolCollection is { } tools)
85-
{
86-
EventHandler changed = (sender, e) => _ = this.SendNotificationAsync(NotificationMethods.ToolListChangedNotification);
87-
tools.Changed += changed;
88-
_disposables.Add(() => tools.Changed -= changed);
89-
}
84+
Register(ServerOptions.Capabilities?.Tools?.ToolCollection, NotificationMethods.ToolListChangedNotification);
85+
Register(ServerOptions.Capabilities?.Prompts?.PromptCollection, NotificationMethods.PromptListChangedNotification);
86+
Register(ServerOptions.Capabilities?.Resources?.ResourceCollection, NotificationMethods.ResourceListChangedNotification);
9087

91-
if (ServerOptions.Capabilities?.Prompts?.PromptCollection is { } prompts)
88+
void Register<TPrimitive>(McpServerPrimitiveCollection<TPrimitive>? collection, string notificationMethod)
89+
where TPrimitive : IMcpServerPrimitive
9290
{
93-
EventHandler changed = (sender, e) => _ = this.SendNotificationAsync(NotificationMethods.PromptListChangedNotification);
94-
prompts.Changed += changed;
95-
_disposables.Add(() => prompts.Changed -= changed);
96-
}
97-
98-
var resources = ServerOptions.Capabilities?.Resources?.ResourceCollection;
99-
if (resources is not null)
100-
{
101-
EventHandler changed = (sender, e) => _ = this.SendNotificationAsync(NotificationMethods.PromptListChangedNotification);
102-
resources.Changed += changed;
103-
_disposables.Add(() => resources.Changed -= changed);
91+
if (collection is not null)
92+
{
93+
EventHandler changed = (sender, e) => _ = this.SendNotificationAsync(notificationMethod);
94+
collection.Changed += changed;
95+
_disposables.Add(() => collection.Changed -= changed);
96+
}
10497
}
10598
}
10699

0 commit comments

Comments
 (0)