// Copyright (c) Microsoft. All rights reserved. using MCPServer.Prompts; using MCPServer.Resources; using Microsoft.SemanticKernel; using ModelContextProtocol.Protocol; using ModelContextProtocol.Server; namespace MCPServer; /// /// Extension methods for . /// public static class McpServerBuilderExtensions { /// /// Adds all functions of the kernel plugins as tools to the server. /// /// The MCP builder instance. /// An optional kernel instance which plugins will be added as tools. /// If not provided, all functions from the kernel plugins registered in DI container will be added. /// /// The builder instance. public static IMcpServerBuilder WithTools(this IMcpServerBuilder builder, Kernel? kernel = null) { // If plugins are provided directly, add them as tools if (kernel is not null) { foreach (var plugin in kernel.Plugins) { foreach (var function in plugin) { builder.Services.AddSingleton(McpServerTool.Create(function)); } } return builder; } // If no plugins are provided explicitly, add all functions from the kernel plugins registered in DI container as tools builder.Services.AddSingleton>(services => { IEnumerable plugins = services.GetServices(); List tools = new(plugins.Count()); foreach (var plugin in plugins) { foreach (var function in plugin) { tools.Add(McpServerTool.Create(function)); } } return tools; }); return builder; } /// /// Adds a prompt definition and handlers for listing and reading prompts. /// /// The MCP server builder. /// The prompt definition. /// The builder instance. public static IMcpServerBuilder WithPrompt(this IMcpServerBuilder builder, PromptDefinition promptDefinition) { // Register the prompt definition in the DI container builder.Services.AddSingleton(promptDefinition); builder.WithPromptHandlers(); return builder; } /// /// Adds handlers for listing and reading prompts. /// /// The MCP server builder. /// The builder instance. public static IMcpServerBuilder WithPromptHandlers(this IMcpServerBuilder builder) { builder.WithListPromptsHandler(HandleListPromptRequestsAsync); builder.WithGetPromptHandler(HandleGetPromptRequestsAsync); return builder; } /// /// Adds a resource template and handlers for listing and reading resource templates. /// /// The MCP server builder. /// The kernel instance. /// The MCP resource template. /// The MCP resource template handler. /// The builder instance. public static IMcpServerBuilder WithResourceTemplate( this IMcpServerBuilder builder, Kernel kernel, ResourceTemplate template, Delegate handler) { builder.WithResourceTemplate(new ResourceTemplateDefinition { ResourceTemplate = template, Handler = handler, Kernel = kernel }); return builder; } /// /// Adds a resource template and handlers for listing and reading resource templates. /// /// The MCP server builder. /// The resource template definition. /// The builder instance. public static IMcpServerBuilder WithResourceTemplate(this IMcpServerBuilder builder, ResourceTemplateDefinition templateDefinition) { // Register the resource template definition in the DI container builder.Services.AddSingleton(templateDefinition); builder.WithResourceTemplateHandlers(); return builder; } /// /// Adds handlers for listing and reading resource templates. /// /// The MCP server builder. /// The builder instance. public static IMcpServerBuilder WithResourceTemplateHandlers(this IMcpServerBuilder builder) { builder.WithListResourceTemplatesHandler(HandleListResourceTemplatesRequestAsync); builder.WithReadResourceHandler(HandleReadResourceRequestAsync); return builder; } /// /// Adds a resource and handlers for listing and reading resources. /// /// The MCP server builder. /// The kernel instance. /// The MCP resource. /// The MCP resource handler. /// The builder instance. public static IMcpServerBuilder WithResource( this IMcpServerBuilder builder, Kernel kernel, Resource resource, Delegate handler) { builder.WithResource(new ResourceDefinition { Resource = resource, Handler = handler, Kernel = kernel }); return builder; } /// /// Adds a resource and handlers for listing and reading resources. /// /// The MCP server builder. /// The resource definition. /// The builder instance. public static IMcpServerBuilder WithResource(this IMcpServerBuilder builder, ResourceDefinition resourceDefinition) { // Register the resource definition in the DI container builder.Services.AddSingleton(resourceDefinition); builder.WithResourceHandlers(); return builder; } /// /// Adds handlers for listing and reading resources. /// /// The MCP server builder. /// The builder instance. public static IMcpServerBuilder WithResourceHandlers(this IMcpServerBuilder builder) { builder.WithListResourcesHandler(HandleListResourcesRequestAsync); builder.WithReadResourceHandler(HandleReadResourceRequestAsync); return builder; } private static ValueTask HandleListPromptRequestsAsync(RequestContext context, CancellationToken cancellationToken) { // Get and return all prompt definitions registered in the DI container IEnumerable promptDefinitions = context.Server.Services!.GetServices(); return ValueTask.FromResult(new ListPromptsResult { Prompts = [.. promptDefinitions.Select(d => d.Prompt)] }); } private static async ValueTask HandleGetPromptRequestsAsync(RequestContext 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 promptDefinitions = context.Server.Services!.GetServices(); // 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 ValueTask HandleReadResourceRequestAsync(RequestContext 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 resourceDefinitions = context.Server.Services!.GetServices(); 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 resourceTemplateDefinitions = context.Server.Services!.GetServices(); 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 ValueTask HandleListResourceTemplatesRequestAsync(RequestContext context, CancellationToken cancellationToken) { // Get and return all resource template definitions registered in the DI container IEnumerable definitions = context.Server.Services!.GetServices(); return ValueTask.FromResult(new ListResourceTemplatesResult { ResourceTemplates = [.. definitions.Select(d => d.ResourceTemplate)] }); } private static ValueTask HandleListResourcesRequestAsync(RequestContext context, CancellationToken cancellationToken) { // Get and return all resource template definitions registered in the DI container IEnumerable definitions = context.Server.Services!.GetServices(); return ValueTask.FromResult(new ListResourcesResult { Resources = [.. definitions.Select(d => d.Resource)] }); } }