chore: import upstream snapshot with attribution
This commit is contained in:
+271
@@ -0,0 +1,271 @@
|
||||
// Copyright (c) Microsoft. All rights reserved.
|
||||
|
||||
using MCPServer.Prompts;
|
||||
using MCPServer.Resources;
|
||||
using Microsoft.SemanticKernel;
|
||||
using ModelContextProtocol.Protocol;
|
||||
using ModelContextProtocol.Server;
|
||||
|
||||
namespace MCPServer;
|
||||
|
||||
/// <summary>
|
||||
/// Extension methods for <see cref="IMcpServerBuilder"/>.
|
||||
/// </summary>
|
||||
public static class McpServerBuilderExtensions
|
||||
{
|
||||
/// <summary>
|
||||
/// Adds all functions of the kernel plugins as tools to the server.
|
||||
/// </summary>
|
||||
/// <param name="builder">The MCP builder instance.</param>
|
||||
/// <param name="kernel">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.
|
||||
/// </param>
|
||||
/// <returns>The builder instance.</returns>
|
||||
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<IEnumerable<McpServerTool>>(services =>
|
||||
{
|
||||
IEnumerable<KernelPlugin> plugins = services.GetServices<KernelPlugin>();
|
||||
|
||||
List<McpServerTool> tools = new(plugins.Count());
|
||||
|
||||
foreach (var plugin in plugins)
|
||||
{
|
||||
foreach (var function in plugin)
|
||||
{
|
||||
tools.Add(McpServerTool.Create(function));
|
||||
}
|
||||
}
|
||||
|
||||
return tools;
|
||||
});
|
||||
|
||||
return builder;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Adds a prompt definition and handlers for listing and reading prompts.
|
||||
/// </summary>
|
||||
/// <param name="builder">The MCP server builder.</param>
|
||||
/// <param name="promptDefinition">The prompt definition.</param>
|
||||
/// <returns>The builder instance.</returns>
|
||||
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;
|
||||
}
|
||||
|
||||
/// <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 and handlers for listing and reading resource templates.
|
||||
/// </summary>
|
||||
/// <param name="builder">The MCP server builder.</param>
|
||||
/// <param name="kernel">The kernel instance.</param>
|
||||
/// <param name="template">The MCP resource template.</param>
|
||||
/// <param name="handler">The MCP resource template handler.</param>
|
||||
/// <returns>The builder instance.</returns>
|
||||
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;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 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)
|
||||
{
|
||||
// Register the resource template definition in the DI container
|
||||
builder.Services.AddSingleton(templateDefinition);
|
||||
|
||||
builder.WithResourceTemplateHandlers();
|
||||
|
||||
return builder;
|
||||
}
|
||||
|
||||
/// <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 and handlers for listing and reading resources.
|
||||
/// </summary>
|
||||
/// <param name="builder">The MCP server builder.</param>
|
||||
/// <param name="kernel">The kernel instance.</param>
|
||||
/// <param name="resource">The MCP resource.</param>
|
||||
/// <param name="handler">The MCP resource handler.</param>
|
||||
/// <returns>The builder instance.</returns>
|
||||
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;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 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)
|
||||
{
|
||||
// Register the resource definition in the DI container
|
||||
builder.Services.AddSingleton(resourceDefinition);
|
||||
|
||||
builder.WithResourceHandlers();
|
||||
|
||||
return builder;
|
||||
}
|
||||
|
||||
/// <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 ValueTask<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 ValueTask.FromResult(new ListPromptsResult
|
||||
{
|
||||
Prompts = [.. promptDefinitions.Select(d => d.Prompt)]
|
||||
});
|
||||
}
|
||||
|
||||
private static async ValueTask<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 ValueTask<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 ValueTask<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 ValueTask.FromResult(new ListResourceTemplatesResult
|
||||
{
|
||||
ResourceTemplates = [.. definitions.Select(d => d.ResourceTemplate)]
|
||||
});
|
||||
}
|
||||
|
||||
private static ValueTask<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 ValueTask.FromResult(new ListResourcesResult
|
||||
{
|
||||
Resources = [.. definitions.Select(d => d.Resource)]
|
||||
});
|
||||
}
|
||||
}
|
||||
+54
@@ -0,0 +1,54 @@
|
||||
// Copyright (c) Microsoft. All rights reserved.
|
||||
|
||||
using Microsoft.Extensions.AI;
|
||||
using Microsoft.Extensions.VectorData;
|
||||
|
||||
namespace MCPServer;
|
||||
|
||||
/// <summary>
|
||||
/// Extensions for vector stores.
|
||||
/// </summary>
|
||||
public static class VectorStoreExtensions
|
||||
{
|
||||
/// <summary>
|
||||
/// Delegate to create a record from a string.
|
||||
/// </summary>
|
||||
/// <typeparam name="TKey">Type of the record key.</typeparam>
|
||||
/// <typeparam name="TRecord">Type of the record.</typeparam>
|
||||
public delegate TRecord CreateRecordFromString<TKey, TRecord>(string text, ReadOnlyMemory<float> vector) where TKey : notnull;
|
||||
|
||||
/// <summary>
|
||||
/// Create a <see cref="VectorStoreCollection{TKey, TRecord}"/> from a list of strings by:
|
||||
/// </summary>
|
||||
/// <typeparam name="TKey">The data type of the record key.</typeparam>
|
||||
/// <typeparam name="TRecord">The data type of the record.</typeparam>
|
||||
/// <param name="vectorStore">The instance of <see cref="VectorStore"/> used to create the collection.</param>
|
||||
/// <param name="collectionName">The name of the collection.</param>
|
||||
/// <param name="entries">The list of strings to create records from.</param>
|
||||
/// <param name="embeddingGenerator">The text embedding generation service.</param>
|
||||
/// <param name="createRecord">The delegate which can create a record for each string and its embedding.</param>
|
||||
/// <returns>The created collection.</returns>
|
||||
public static async Task<VectorStoreCollection<TKey, TRecord>> CreateCollectionFromListAsync<TKey, TRecord>(
|
||||
this VectorStore vectorStore,
|
||||
string collectionName,
|
||||
string[] entries,
|
||||
IEmbeddingGenerator<string, Embedding<float>> embeddingGenerator,
|
||||
CreateRecordFromString<TKey, TRecord> createRecord)
|
||||
where TKey : notnull
|
||||
where TRecord : class
|
||||
{
|
||||
// Get and create collection if it doesn't exist.
|
||||
var collection = vectorStore.GetCollection<TKey, TRecord>(collectionName);
|
||||
await collection.EnsureCollectionExistsAsync().ConfigureAwait(false);
|
||||
|
||||
// Create records and generate embeddings for them.
|
||||
var tasks = entries.Select(entry => Task.Run(async () =>
|
||||
{
|
||||
var record = createRecord(entry, (await embeddingGenerator.GenerateAsync(entry).ConfigureAwait(false)).Vector);
|
||||
await collection.UpsertAsync(record).ConfigureAwait(false);
|
||||
}));
|
||||
await Task.WhenAll(tasks).ConfigureAwait(false);
|
||||
|
||||
return collection;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user