Skip to content

Fix McpServerResource registration #411

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 1 commit into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -334,16 +334,16 @@ where t.GetCustomAttribute<McpServerPromptTypeAttribute>() is not null

/// <summary>Adds <see cref="McpServerResource"/> instances to the service collection backing <paramref name="builder"/>.</summary>
/// <param name="builder">The builder instance.</param>
/// <param name="resourcetemplates">The <see cref="McpServerResource"/> instances to add to the server.</param>
/// <param name="resourceTemplates">The <see cref="McpServerResource"/> instances to add to the server.</param>
/// <returns>The builder provided in <paramref name="builder"/>.</returns>
/// <exception cref="ArgumentNullException"><paramref name="builder"/> is <see langword="null"/>.</exception>
/// <exception cref="ArgumentNullException"><paramref name="resourcetemplates"/> is <see langword="null"/>.</exception>
public static IMcpServerBuilder WithResources(this IMcpServerBuilder builder, IEnumerable<McpServerResource> resourcetemplates)
/// <exception cref="ArgumentNullException"><paramref name="resourceTemplates"/> is <see langword="null"/>.</exception>
public static IMcpServerBuilder WithResources(this IMcpServerBuilder builder, IEnumerable<McpServerResource> resourceTemplates)
{
Throw.IfNull(builder);
Throw.IfNull(resourcetemplates);
Throw.IfNull(resourceTemplates);

foreach (var resourceTemplate in resourcetemplates)
foreach (var resourceTemplate in resourceTemplates)
{
if (resourceTemplate is not null)
{
Expand Down Expand Up @@ -409,7 +409,7 @@ public static IMcpServerBuilder WithResources(this IMcpServerBuilder builder, IE
/// of the containing class will be constructed for each invocation of the resource.
/// </para>
/// <para>
/// Resource templates registered through this method can be discovered by clients using the <c>list_resourcetemplates</c> request
/// Resource templates registered through this method can be discovered by clients using the <c>list_resourceTemplates</c> request
/// and invoked using the <c>read_resource</c> request.
/// </para>
/// <para>
Expand Down
21 changes: 20 additions & 1 deletion src/ModelContextProtocol/Configuration/McpServerOptionsSetup.cs
Original file line number Diff line number Diff line change
Expand Up @@ -10,10 +10,12 @@ namespace Microsoft.Extensions.DependencyInjection;
/// <param name="serverHandlers">The server handlers configuration options.</param>
/// <param name="serverTools">Tools individually registered.</param>
/// <param name="serverPrompts">Prompts individually registered.</param>
/// <param name="serverResources">Resources individually registered.</param>
internal sealed class McpServerOptionsSetup(
IOptions<McpServerHandlers> serverHandlers,
IEnumerable<McpServerTool> serverTools,
IEnumerable<McpServerPrompt> serverPrompts) : IConfigureOptions<McpServerOptions>
IEnumerable<McpServerPrompt> serverPrompts,
IEnumerable<McpServerResource> serverResources) : IConfigureOptions<McpServerOptions>
{
/// <summary>
/// Configures the given McpServerOptions instance by setting server information
Expand Down Expand Up @@ -58,6 +60,23 @@ public void Configure(McpServerOptions options)
options.Capabilities.Prompts.PromptCollection = promptCollection;
}

// Collect all of the provided resources into a resources collection. If the options already has
// a collection, add to it, otherwise create a new one. We want to maintain the identity
// of an existing collection in case someone has provided their own derived type, wants
// change notifications, etc.
McpServerPrimitiveCollection<McpServerResource> resourceCollection = options.Capabilities?.Resources?.ResourceCollection ?? [];
foreach (var resource in serverResources)
{
resourceCollection.TryAdd(resource);
}

if (!resourceCollection.IsEmpty)
{
options.Capabilities ??= new();
options.Capabilities.Resources ??= new();
options.Capabilities.Resources.ResourceCollection = resourceCollection;
}

// Apply custom server handlers.
serverHandlers.Value.OverwriteWithSetHandlers(options);
}
Expand Down
3 changes: 0 additions & 3 deletions src/ModelContextProtocol/Server/AIFunctionMcpServerPrompt.cs
Original file line number Diff line number Diff line change
Expand Up @@ -213,9 +213,6 @@ private AIFunctionMcpServerPrompt(AIFunction function, Prompt prompt)
ProtocolPrompt = prompt;
}

/// <inheritdoc />
public override string ToString() => AIFunction.ToString();

/// <inheritdoc />
public override Prompt ProtocolPrompt { get; }

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -328,9 +328,6 @@ private AIFunctionMcpServerResource(AIFunction function, ResourceTemplate resour
}
}

/// <inheritdoc />
public override string ToString() => AIFunction.ToString();

/// <inheritdoc />
public override ResourceTemplate ProtocolResourceTemplate { get; }

Expand Down
3 changes: 0 additions & 3 deletions src/ModelContextProtocol/Server/AIFunctionMcpServerTool.cs
Original file line number Diff line number Diff line change
Expand Up @@ -243,9 +243,6 @@ private AIFunctionMcpServerTool(AIFunction function, Tool tool)
ProtocolTool = tool;
}

/// <inheritdoc />
public override string ToString() => AIFunction.ToString();

/// <inheritdoc />
public override Tool ProtocolTool { get; }

Expand Down
29 changes: 11 additions & 18 deletions src/ModelContextProtocol/Server/McpServer.cs
Original file line number Diff line number Diff line change
Expand Up @@ -75,26 +75,19 @@ public McpServer(ITransport transport, McpServerOptions options, ILoggerFactory?
}

// Now that everything has been configured, subscribe to any necessary notifications.
if (ServerOptions.Capabilities?.Tools?.ToolCollection is { } tools)
{
EventHandler changed = (sender, e) => _ = this.SendNotificationAsync(NotificationMethods.ToolListChangedNotification);
tools.Changed += changed;
_disposables.Add(() => tools.Changed -= changed);
}
Register(ServerOptions.Capabilities?.Tools?.ToolCollection, NotificationMethods.ToolListChangedNotification);
Register(ServerOptions.Capabilities?.Prompts?.PromptCollection, NotificationMethods.PromptListChangedNotification);
Register(ServerOptions.Capabilities?.Resources?.ResourceCollection, NotificationMethods.ResourceListChangedNotification);

if (ServerOptions.Capabilities?.Prompts?.PromptCollection is { } prompts)
void Register<TPrimitive>(McpServerPrimitiveCollection<TPrimitive>? collection, string notificationMethod)
where TPrimitive : IMcpServerPrimitive
{
EventHandler changed = (sender, e) => _ = this.SendNotificationAsync(NotificationMethods.PromptListChangedNotification);
prompts.Changed += changed;
_disposables.Add(() => prompts.Changed -= changed);
}

var resources = ServerOptions.Capabilities?.Resources?.ResourceCollection;
if (resources is not null)
{
EventHandler changed = (sender, e) => _ = this.SendNotificationAsync(NotificationMethods.PromptListChangedNotification);
resources.Changed += changed;
_disposables.Add(() => resources.Changed -= changed);
if (collection is not null)
{
EventHandler changed = (sender, e) => _ = this.SendNotificationAsync(notificationMethod);
collection.Changed += changed;
_disposables.Add(() => collection.Changed -= changed);
}
}

// And initialize the session.
Expand Down
Loading