Skip to content

.Net: Use DI to keep prompt, resource, and resource template definitions. #11544

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

Merged
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 @@ -60,23 +60,36 @@ public static IMcpServerBuilder WithTools(this IMcpServerBuilder builder, Kernel
}

/// <summary>
/// Adds a resource template to the server.
/// Adds a prompt definition and handlers for listing and reading prompts.
/// </summary>
/// <param name="builder">The MCP server builder.</param>
/// <param name="templateDefinition">The resource template definition.</param>
/// <param name="promptDefinition">The prompt definition.</param>
/// <returns>The builder instance.</returns>
public static IMcpServerBuilder WithPrompt(this IMcpServerBuilder builder, PromptDefinition templateDefinition)
public static IMcpServerBuilder WithPrompt(this IMcpServerBuilder builder, PromptDefinition promptDefinition)
{
PromptRegistry.RegisterPrompt(templateDefinition);
// Register the prompt definition in the DI container
builder.Services.AddSingleton(promptDefinition);

builder.WithPromptHandlers();

return builder;
}

builder.WithListPromptsHandler(PromptRegistry.HandlerListPromptRequestsAsync);
builder.WithGetPromptHandler(PromptRegistry.HandlerGetPromptRequestsAsync);
/// <summary>
/// Adds handlers for listing and reading prompts.
/// </summary>
/// <param name="builder">The MCP server builder.</param>
/// <returns>The builder instance.</returns>
public static IMcpServerBuilder WithPromptHandlers(this IMcpServerBuilder builder)
{
builder.WithListPromptsHandler(HandleListPromptRequestsAsync);
builder.WithGetPromptHandler(HandleGetPromptRequestsAsync);

return builder;
}

/// <summary>
/// Adds a resource template to the server.
/// Adds a resource template and handlers for listing and reading resource templates.
/// </summary>
/// <param name="builder">The MCP server builder.</param>
/// <param name="kernel">The kernel instance.</param>
Expand All @@ -95,23 +108,36 @@ public static IMcpServerBuilder WithResourceTemplate(
}

/// <summary>
/// Adds a resource template to the server.
/// Adds a resource template and handlers for listing and reading resource templates.
/// </summary>
/// <param name="builder">The MCP server builder.</param>
/// <param name="templateDefinition">The resource template definition.</param>
/// <returns>The builder instance.</returns>
public static IMcpServerBuilder WithResourceTemplate(this IMcpServerBuilder builder, ResourceTemplateDefinition templateDefinition)
{
ResourceRegistry.RegisterResourceTemplate(templateDefinition);
// Register the resource template definition in the DI container
builder.Services.AddSingleton(templateDefinition);

builder.WithResourceTemplateHandlers();

return builder;
}

builder.WithListResourceTemplatesHandler(ResourceRegistry.HandleListResourceTemplatesRequestAsync);
builder.WithReadResourceHandler(ResourceRegistry.HandleReadResourceRequestAsync);
/// <summary>
/// Adds handlers for listing and reading resource templates.
/// </summary>
/// <param name="builder">The MCP server builder.</param>
/// <returns>The builder instance.</returns>
public static IMcpServerBuilder WithResourceTemplateHandlers(this IMcpServerBuilder builder)
{
builder.WithListResourceTemplatesHandler(HandleListResourceTemplatesRequestAsync);
builder.WithReadResourceHandler(HandleReadResourceRequestAsync);

return builder;
}

/// <summary>
/// Adds a resource to the server.
/// Adds a resource and handlers for listing and reading resources.
/// </summary>
/// <param name="builder">The MCP server builder.</param>
/// <param name="kernel">The kernel instance.</param>
Expand All @@ -130,18 +156,117 @@ public static IMcpServerBuilder WithResource(
}

/// <summary>
/// Adds a resource to the server.
/// Adds a resource and handlers for listing and reading resources.
/// </summary>
/// <param name="builder">The MCP server builder.</param>
/// <param name="resourceDefinition">The resource definition.</param>
/// <returns>The builder instance.</returns>
public static IMcpServerBuilder WithResource(this IMcpServerBuilder builder, ResourceDefinition resourceDefinition)
{
ResourceRegistry.RegisterResource(resourceDefinition);
// Register the resource definition in the DI container
builder.Services.AddSingleton(resourceDefinition);

builder.WithResourceHandlers();

return builder;
}

builder.WithListResourcesHandler(ResourceRegistry.HandleListResourcesRequestAsync);
builder.WithReadResourceHandler(ResourceRegistry.HandleReadResourceRequestAsync);
/// <summary>
/// Adds handlers for listing and reading resources.
/// </summary>
/// <param name="builder">The MCP server builder.</param>
/// <returns>The builder instance.</returns>
public static IMcpServerBuilder WithResourceHandlers(this IMcpServerBuilder builder)
{
builder.WithListResourcesHandler(HandleListResourcesRequestAsync);
builder.WithReadResourceHandler(HandleReadResourceRequestAsync);

return builder;
}

private static Task<ListPromptsResult> HandleListPromptRequestsAsync(RequestContext<ListPromptsRequestParams> context, CancellationToken cancellationToken)
{
// Get and return all prompt definitions registered in the DI container
IEnumerable<PromptDefinition> promptDefinitions = context.Server.Services!.GetServices<PromptDefinition>();

return Task.FromResult(new ListPromptsResult
{
Prompts = [.. promptDefinitions.Select(d => d.Prompt)]
});
}

private static async Task<GetPromptResult> HandleGetPromptRequestsAsync(RequestContext<GetPromptRequestParams> context, CancellationToken cancellationToken)
{
// Make sure the prompt name is provided
if (context.Params?.Name is not string { } promptName || string.IsNullOrEmpty(promptName))
{
throw new ArgumentException("Prompt name is required.");
}

// Get all prompt definitions registered in the DI container
IEnumerable<PromptDefinition> promptDefinitions = context.Server.Services!.GetServices<PromptDefinition>();

// Look up the prompt definition
PromptDefinition? definition = promptDefinitions.FirstOrDefault(d => d.Prompt.Name == promptName);
if (definition is null)
{
throw new ArgumentException($"No handler found for the prompt '{promptName}'.");
}

// Invoke the handler
return await definition.Handler(context, cancellationToken);
}

private static Task<ReadResourceResult> HandleReadResourceRequestAsync(RequestContext<ReadResourceRequestParams> context, CancellationToken cancellationToken)
{
// Make sure the uri of the resource or resource template is provided
if (context.Params?.Uri is not string { } resourceUri || string.IsNullOrEmpty(resourceUri))
{
throw new ArgumentException("Resource uri is required.");
}

// Look up in registered resource first
IEnumerable<ResourceDefinition> resourceDefinitions = context.Server.Services!.GetServices<ResourceDefinition>();

ResourceDefinition? resourceDefinition = resourceDefinitions.FirstOrDefault(d => d.Resource.Uri == resourceUri);
if (resourceDefinition is not null)
{
return resourceDefinition.InvokeHandlerAsync(context, cancellationToken);
}

// Look up in registered resource templates
IEnumerable<ResourceTemplateDefinition> resourceTemplateDefinitions = context.Server.Services!.GetServices<ResourceTemplateDefinition>();

foreach (var resourceTemplateDefinition in resourceTemplateDefinitions)
{
if (resourceTemplateDefinition.IsMatch(resourceUri))
{
return resourceTemplateDefinition.InvokeHandlerAsync(context, cancellationToken);
}
}

throw new ArgumentException($"No handler found for the resource uri '{resourceUri}'.");
}

private static Task<ListResourceTemplatesResult> HandleListResourceTemplatesRequestAsync(RequestContext<ListResourceTemplatesRequestParams> context, CancellationToken cancellationToken)
{
// Get and return all resource template definitions registered in the DI container
IEnumerable<ResourceTemplateDefinition> definitions = context.Server.Services!.GetServices<ResourceTemplateDefinition>();

return Task.FromResult(new ListResourceTemplatesResult
{
ResourceTemplates = [.. definitions.Select(d => d.ResourceTemplate)]
});
}

private static Task<ListResourcesResult> HandleListResourcesRequestAsync(RequestContext<ListResourcesRequestParams> context, CancellationToken cancellationToken)
{
// Get and return all resource template definitions registered in the DI container
IEnumerable<ResourceDefinition> definitions = context.Server.Services!.GetServices<ResourceDefinition>();

return Task.FromResult(new ListResourcesResult
{
Resources = [.. definitions.Select(d => d.Resource)]
});
}
}

This file was deleted.

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -57,10 +57,6 @@ public async Task<ReadResourceResult> InvokeHandlerAsync(RequestContext<ReadReso
{
this._kernelFunction ??= KernelFunctionFactory.CreateFromMethod(this.Handler);

this.Kernel
??= context.Server.Services?.GetRequiredService<Kernel>()
?? throw new InvalidOperationException("Kernel is not available.");

this.Kernel
??= context.Server.Services?.GetRequiredService<Kernel>()
?? throw new InvalidOperationException("Kernel is not available.");
Expand Down
Loading