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;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,45 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk.Worker">
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFramework>net10.0</TargetFramework>
|
||||
<Nullable>enable</Nullable>
|
||||
<ImplicitUsings>enable</ImplicitUsings>
|
||||
<UserSecretsId>5ee045b0-aea3-4f08-8d31-32d1a6f8fed0</UserSecretsId>
|
||||
<NoWarn>$(NoWarn);VSTHRD111;CA2007;CA1054;SKEXP0001;SKEXP0010;SKEXP0110</NoWarn>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<Content Remove="Prompts\getCurrentWeatherForCity.json" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<None Remove="ProjectResources\EmployeeBirthdaysAndPositions.png" />
|
||||
<None Remove="ProjectResources\SalesReport2014.png" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<EmbeddedResource Include="ProjectResources\EmployeeBirthdaysAndPositions.png" />
|
||||
<EmbeddedResource Include="ProjectResources\getCurrentWeatherForCity.json" />
|
||||
<EmbeddedResource Include="ProjectResources\cat.jpg">
|
||||
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
|
||||
</EmbeddedResource>
|
||||
<EmbeddedResource Include="ProjectResources\SalesReport2014.png" />
|
||||
<EmbeddedResource Include="ProjectResources\semantic-kernel-info.txt">
|
||||
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
|
||||
</EmbeddedResource>
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Microsoft.Extensions.Hosting" />
|
||||
<PackageReference Include="ModelContextProtocol" />
|
||||
<PackageReference Include="Microsoft.SemanticKernel.Connectors.InMemory" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\..\..\..\src\Agents\Core\Agents.Core.csproj" />
|
||||
<ProjectReference Include="..\..\..\..\src\Connectors\Connectors.OpenAI\Connectors.OpenAI.csproj" />
|
||||
<ProjectReference Include="..\..\..\..\src\Extensions\PromptTemplates.Handlebars\PromptTemplates.Handlebars.csproj" />
|
||||
<ProjectReference Include="..\..\..\..\src\SemanticKernel.Abstractions\SemanticKernel.Abstractions.csproj" />
|
||||
<ProjectReference Include="..\..\..\..\src\SemanticKernel.Core\SemanticKernel.Core.csproj" />
|
||||
</ItemGroup>
|
||||
</Project>
|
||||
@@ -0,0 +1,174 @@
|
||||
// Copyright (c) Microsoft. All rights reserved.
|
||||
|
||||
using MCPServer;
|
||||
using MCPServer.ProjectResources;
|
||||
using MCPServer.Prompts;
|
||||
using MCPServer.Resources;
|
||||
using MCPServer.Tools;
|
||||
using Microsoft.Extensions.AI;
|
||||
using Microsoft.Extensions.VectorData;
|
||||
using Microsoft.SemanticKernel;
|
||||
using Microsoft.SemanticKernel.Agents;
|
||||
using Microsoft.SemanticKernel.Connectors.InMemory;
|
||||
using ModelContextProtocol.Protocol;
|
||||
using ModelContextProtocol.Server;
|
||||
|
||||
var builder = Host.CreateEmptyApplicationBuilder(settings: null);
|
||||
|
||||
// Load and validate configuration
|
||||
(string embeddingModelId, string chatModelId, string apiKey) = GetConfiguration();
|
||||
|
||||
// Register the kernel
|
||||
IKernelBuilder kernelBuilder = builder.Services.AddKernel();
|
||||
|
||||
// Register SK plugins
|
||||
kernelBuilder.Plugins.AddFromType<DateTimeUtils>();
|
||||
kernelBuilder.Plugins.AddFromType<WeatherUtils>();
|
||||
kernelBuilder.Plugins.AddFromType<MailboxUtils>();
|
||||
|
||||
// Register SK agent as plugin
|
||||
kernelBuilder.Plugins.AddFromFunctions("Agents", [AgentKernelFunctionFactory.CreateFromAgent(CreateSalesAssistantAgent(chatModelId, apiKey))]);
|
||||
|
||||
// Register embedding generation service and in-memory vector store
|
||||
kernelBuilder.Services.AddSingleton<VectorStore, InMemoryVectorStore>();
|
||||
kernelBuilder.Services.AddOpenAIEmbeddingGenerator(embeddingModelId, apiKey);
|
||||
|
||||
// Register MCP server
|
||||
builder.Services
|
||||
.AddMcpServer()
|
||||
.WithStdioServerTransport()
|
||||
|
||||
// Add all functions from the kernel plugins to the MCP server as tools
|
||||
.WithTools()
|
||||
|
||||
// Register the `getCurrentWeatherForCity` prompt
|
||||
.WithPrompt(PromptDefinition.Create(EmbeddedResource.ReadAsString("getCurrentWeatherForCity.json")))
|
||||
|
||||
// Register vector search as MCP resource template
|
||||
.WithResourceTemplate(CreateVectorStoreSearchResourceTemplate())
|
||||
|
||||
// Register the cat image as a MCP resource
|
||||
.WithResource(ResourceDefinition.CreateBlobResource(
|
||||
uri: "image://cat.jpg",
|
||||
name: "cat-image",
|
||||
content: EmbeddedResource.ReadAsBytes("cat.jpg"),
|
||||
mimeType: "image/jpeg"));
|
||||
|
||||
await builder.Build().RunAsync();
|
||||
|
||||
/// <summary>
|
||||
/// Gets configuration.
|
||||
/// </summary>
|
||||
static (string EmbeddingModelId, string ChatModelId, string ApiKey) GetConfiguration()
|
||||
{
|
||||
// Load and validate configuration
|
||||
IConfigurationRoot config = new ConfigurationBuilder()
|
||||
.AddUserSecrets<Program>()
|
||||
.AddEnvironmentVariables()
|
||||
.Build();
|
||||
|
||||
if (config["OpenAI:ApiKey"] is not { } apiKey)
|
||||
{
|
||||
const string Message = "Please provide a valid OpenAI:ApiKey to run this sample. See the associated README.md for more details.";
|
||||
Console.Error.WriteLine(Message);
|
||||
throw new InvalidOperationException(Message);
|
||||
}
|
||||
|
||||
string embeddingModelId = config["OpenAI:EmbeddingModelId"] ?? "text-embedding-3-small";
|
||||
|
||||
string chatModelId = config["OpenAI:ChatModelId"] ?? "gpt-4o-mini";
|
||||
|
||||
return (embeddingModelId, chatModelId, apiKey);
|
||||
}
|
||||
static ResourceTemplateDefinition CreateVectorStoreSearchResourceTemplate(Kernel? kernel = null)
|
||||
{
|
||||
return new ResourceTemplateDefinition
|
||||
{
|
||||
Kernel = kernel,
|
||||
ResourceTemplate = new()
|
||||
{
|
||||
UriTemplate = "vectorStore://{collection}/{prompt}",
|
||||
Name = "Vector Store Record Retrieval",
|
||||
Description = "Retrieves relevant records from the vector store based on the provided prompt."
|
||||
},
|
||||
Handler = async (
|
||||
RequestContext<ReadResourceRequestParams> context,
|
||||
string collection,
|
||||
string prompt,
|
||||
[FromKernelServices] IEmbeddingGenerator<string, Embedding<float>> embeddingGenerator,
|
||||
[FromKernelServices] VectorStore vectorStore,
|
||||
CancellationToken cancellationToken) =>
|
||||
{
|
||||
// Get the vector store collection
|
||||
VectorStoreCollection<Guid, TextDataModel> vsCollection = vectorStore.GetCollection<Guid, TextDataModel>(collection);
|
||||
|
||||
// Check if the collection exists, if not create and populate it
|
||||
if (!await vsCollection.CollectionExistsAsync(cancellationToken))
|
||||
{
|
||||
static TextDataModel CreateRecord(string text, ReadOnlyMemory<float> embedding)
|
||||
{
|
||||
return new()
|
||||
{
|
||||
Key = Guid.NewGuid(),
|
||||
Text = text,
|
||||
Embedding = embedding
|
||||
};
|
||||
}
|
||||
|
||||
string content = EmbeddedResource.ReadAsString("semantic-kernel-info.txt");
|
||||
|
||||
// Create a collection from the lines in the file
|
||||
await vectorStore.CreateCollectionFromListAsync<Guid, TextDataModel>(collection, content.Split('\n'), embeddingGenerator, CreateRecord);
|
||||
}
|
||||
|
||||
// Generate embedding for the prompt
|
||||
ReadOnlyMemory<float> promptEmbedding = (await embeddingGenerator.GenerateAsync(prompt, cancellationToken: cancellationToken)).Vector;
|
||||
|
||||
// Retrieve top three matching records from the vector store
|
||||
var result = vsCollection.SearchAsync(promptEmbedding, top: 3, cancellationToken: cancellationToken);
|
||||
|
||||
// Return the records as resource contents
|
||||
List<ResourceContents> contents = [];
|
||||
|
||||
await foreach (var record in result)
|
||||
{
|
||||
contents.Add(new TextResourceContents()
|
||||
{
|
||||
Text = record.Record.Text,
|
||||
Uri = context.Params!.Uri!,
|
||||
MimeType = "text/plain",
|
||||
});
|
||||
}
|
||||
|
||||
return new ReadResourceResult { Contents = contents };
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
static Agent CreateSalesAssistantAgent(string chatModelId, string apiKey)
|
||||
{
|
||||
IKernelBuilder kernelBuilder = Kernel.CreateBuilder();
|
||||
|
||||
// Register the SK plugin for the agent to use
|
||||
kernelBuilder.Plugins.AddFromType<OrderProcessingUtils>();
|
||||
|
||||
// Register chat completion service
|
||||
kernelBuilder.Services.AddOpenAIChatCompletion(chatModelId, apiKey);
|
||||
|
||||
// Using a dedicated kernel with the `OrderProcessingUtils` plugin instead of the global kernel has a few advantages:
|
||||
// - The agent has access to only relevant plugins, leading to better decision-making regarding which plugin to use.
|
||||
// Fewer plugins mean less ambiguity in selecting the most appropriate one for a given task.
|
||||
// - The plugin is isolated from other plugins exposed by the MCP server. As a result the client's Agent/AI model does
|
||||
// not have access to irrelevant plugins.
|
||||
Kernel kernel = kernelBuilder.Build();
|
||||
|
||||
// Define the agent
|
||||
return new ChatCompletionAgent()
|
||||
{
|
||||
Name = "SalesAssistant",
|
||||
Instructions = "You are a sales assistant. Place orders for items the user requests and handle refunds.",
|
||||
Description = "Agent to invoke to place orders for items the user requests and handle refunds.",
|
||||
Kernel = kernel,
|
||||
Arguments = new KernelArguments(new PromptExecutionSettings() { FunctionChoiceBehavior = FunctionChoiceBehavior.Auto() }),
|
||||
};
|
||||
}
|
||||
+60
@@ -0,0 +1,60 @@
|
||||
// Copyright (c) Microsoft. All rights reserved.
|
||||
|
||||
using System.Reflection;
|
||||
|
||||
namespace MCPServer.ProjectResources;
|
||||
|
||||
/// <summary>
|
||||
/// Reads embedded resources.
|
||||
/// </summary>
|
||||
public static class EmbeddedResource
|
||||
{
|
||||
private static readonly string? s_namespace = typeof(EmbeddedResource).Namespace;
|
||||
|
||||
/// <summary>
|
||||
/// Read an embedded resource as a string.
|
||||
/// </summary>
|
||||
/// <param name="resourcePath">The path to the resource, relative to the assembly namespace.</param>
|
||||
/// <returns>A string containing the resource content.</returns>
|
||||
public static string ReadAsString(string resourcePath)
|
||||
{
|
||||
Stream stream = ReadAsStream(resourcePath);
|
||||
|
||||
using StreamReader reader = new(stream);
|
||||
return reader.ReadToEnd();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Read an embedded resource as a byte array.
|
||||
/// </summary>
|
||||
/// <param name="resourcePath">The path to the resource, relative to the assembly namespace.</param>
|
||||
/// <returns>A byte array containing the resource content.</returns>
|
||||
public static byte[] ReadAsBytes(string resourcePath)
|
||||
{
|
||||
Stream stream = ReadAsStream(resourcePath);
|
||||
|
||||
using MemoryStream memoryStream = new();
|
||||
stream.CopyTo(memoryStream);
|
||||
return memoryStream.ToArray();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Read an embedded resource as a stream.
|
||||
/// </summary>
|
||||
/// <param name="resourcePath">The path to the resource, relative to the assembly namespace.</param>
|
||||
/// <returns>A stream containing the resource content.</returns>
|
||||
public static Stream ReadAsStream(string resourcePath)
|
||||
{
|
||||
// Get the current assembly. Note: this class is in the same assembly where the embedded resources are stored.
|
||||
Assembly assembly =
|
||||
typeof(EmbeddedResource).GetTypeInfo().Assembly ??
|
||||
throw new InvalidOperationException($"[{s_namespace}] {resourcePath} assembly not found");
|
||||
|
||||
// Resources are mapped like types, using the namespace and appending "." (dot) and the file name
|
||||
string resourceName = $"{s_namespace}.{resourcePath}";
|
||||
|
||||
return
|
||||
assembly.GetManifestResourceStream(resourceName) ??
|
||||
throw new InvalidOperationException($"{resourceName} resource not found");
|
||||
}
|
||||
}
|
||||
BIN
Binary file not shown.
|
After Width: | Height: | Size: 114 KiB |
BIN
Binary file not shown.
|
After Width: | Height: | Size: 288 KiB |
BIN
Binary file not shown.
|
After Width: | Height: | Size: 37 KiB |
+18
@@ -0,0 +1,18 @@
|
||||
{
|
||||
"name": "GetCurrentWeatherForCity",
|
||||
"description": "Provides current weather information for a specified city.",
|
||||
"template_format": "handlebars",
|
||||
"template": "It's {{WeatherUtils-GetWeatherForCity city time}} in {{city}} as of {{time}}",
|
||||
"input_variables": [
|
||||
{
|
||||
"name": "city",
|
||||
"description": "The city for which to get the weather.",
|
||||
"is_required": true
|
||||
},
|
||||
{
|
||||
"name": "time",
|
||||
"description": "The UTC time for which to get the weather.",
|
||||
"is_required": true
|
||||
}
|
||||
]
|
||||
}
|
||||
+26
@@ -0,0 +1,26 @@
|
||||
Semantic Kernel is a lightweight, open-source development kit that lets you easily build AI agents and integrate the latest AI models into your C#, Python, or Java codebase. It serves as an efficient middleware that enables rapid delivery of enterprise-grade solutions.
|
||||
Semantic Kernel is a new AI SDK, and a simple and yet powerful programming model that lets you add large language capabilities to your app in just a matter of minutes. It uses natural language prompting to create and execute semantic kernel AI tasks across multiple languages and platforms.
|
||||
In this guide, you learned how to quickly get started with Semantic Kernel by building a simple AI agent that can interact with an AI service and run your code. To see more examples and learn how to build more complex AI agents, check out our in-depth samples.
|
||||
The Semantic Kernel extension for Visual Studio Code makes it easy to design and test semantic functions. The extension provides an interface for designing semantic functions and allows you to test them with the push of a button with your existing models and data.
|
||||
The kernel is the central component of Semantic Kernel. At its simplest, the kernel is a Dependency Injection container that manages all of the services and plugins necessary to run your AI application.
|
||||
Semantic Kernel (SK) is a lightweight SDK that lets you mix conventional programming languages, like C# and Python, with the latest in Large Language Model (LLM) AI “prompts” with prompt templating, chaining, and planning capabilities.
|
||||
Semantic Kernel is a lightweight, open-source development kit that lets you easily build AI agents and integrate the latest AI models into your C#, Python, or Java codebase. It serves as an efficient middleware that enables rapid delivery of enterprise-grade solutions. Enterprise ready.
|
||||
With Semantic Kernel, you can easily build agents that can call your existing code. This power lets you automate your business processes with models from OpenAI, Azure OpenAI, Hugging Face, and more! We often get asked though, “How do I architect my solution?” and “How does it actually work?”
|
||||
Semantic Kernel for Java is an open source library that empowers developers to harness the power of AI while coding in Java. It is compatible with Java 8 and above, ensuring flexibility and accessibility to a wide range of Java developers.
|
||||
Semantic Kernel enables developers to easily blend cutting-edge AI with native code, opening up a world of new possibilities for AI applications. This article could go on to discuss...
|
||||
Semantic Kernel distinguishes between semantic functions, templated prompts, and native functions, i.e. the native computer code that processes data for use in the LLM’s semantic functions.
|
||||
Semantic Kernel (SK) is a lightweight SDK enabling integration of AI Large Language Models (LLMs) with conventional programming languages. The SK extensible programming model combines natural language semantic functions, traditional code native functions, and embeddings-based memory unlocking new potential and adding value to applications with AI.
|
||||
So what is Semantic Kernel? We also call it SK as an abbreviation. It is a lightweight SDK software development kit. Lightweight is super important because the last thing you want to do is...
|
||||
Semantic Kernel documentation. Learn to build robust, future-proof AI solutions that evolve with technological advancements.
|
||||
Prompt Templates. Chat Prompting. Filtering. Dependency Injection. A Glimpse into the Getting Started Steps: In the guide below we’ll start from scratch and navigate with you through each of the example steps, clarifying the code, details and running them in real time.
|
||||
Using Semantic Kernel and Kernel Memory together can greatly accelerate the time to deliver new AI solutions. Here’s how: Rapid Prototyping: The modular and extensible nature of Semantic Kernel allows you to quickly prototype and test new features. You can integrate existing code and leverage out-of-the-box connectors to build functional ...
|
||||
The semantic kernel (SK) is this beautiful orchestrator that passes the ball between the model and available plugins, thus producing the desired output by getting a collaborative effort.
|
||||
The kernel integrates the OpenAI chat completion interface for generating chat responses and manages plugin execution for custom functionalities. Host Instructions. In the context of the Semantic Kernel, prompt instructions serve as a guiding light for the LLM, influencing its decision-making process when choosing the appropriate plugin to execute.
|
||||
Semantic Kernel is a powerful and recommended choice for working with AI in .NET applications. In the sections ahead, you learn: How to add semantic kernel to your project. Semantic Kernel core concepts. The sections ahead serve as an introductory overview of Semantic Kernel specifically in the context of .NET.
|
||||
This monthly beginner series will walk through the fundamentals of using Semantic Kernel SDK to build intelligent applications that automate tasks and performance
|
||||
Semantic Kernel (SK) is a lightweight SDK that lets you mix conventional programming languages, like C# and Python, with the latest in Large Language Model (LLM) AI “prompts” with prompt templating, chaining, and planning capabilities. Its Planner Skill allows users to create and execute plans based on semantic queries.
|
||||
Semantic Kernel provides a wide range of integrations to help you build powerful AI agents. These integrations include AI services, memory connectors. Additionally, Semantic Kernel integrates with other Microsoft services to provide additional functionality via plugins.
|
||||
Filesystems in the Linux kernel ¶. Filesystems in the Linux kernel. ¶. This under-development manual will, some glorious day, provide comprehensive information on how the Linux virtual filesystem (VFS) layer works, along with the filesystems that sit below it. For now, what we have can be found below.
|
||||
Semantic Kernel allows prompts to be automatically converted to ChatHistory instances. Developers can create prompts which include <message> tags and ...
|
||||
Anatomy of a plugin. At a high-level, a plugin is a group of functions that can be exposed to AI apps and services. The functions within plugins can then be orchestrated by an AI application to accomplish user requests. Within Semantic Kernel, you can invoke these functions automatically with function calling. Note.
|
||||
The biggest benefit of having a dedicated connector for Ollama is that it allows us to support Semantic Kernel features that targeted for Ollama deployed models. What is Ollama? Ollama is an open-source MIT license platform that facilitates the local operation of AI models directly on personal or corporate hardware.
|
||||
+115
@@ -0,0 +1,115 @@
|
||||
// Copyright (c) Microsoft. All rights reserved.
|
||||
|
||||
using Microsoft.SemanticKernel;
|
||||
using Microsoft.SemanticKernel.PromptTemplates.Handlebars;
|
||||
using ModelContextProtocol.Protocol;
|
||||
using ModelContextProtocol.Server;
|
||||
|
||||
namespace MCPServer.Prompts;
|
||||
|
||||
/// <summary>
|
||||
/// Represents a prompt definition.
|
||||
/// </summary>
|
||||
public sealed class PromptDefinition
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets or sets the prompt.
|
||||
/// </summary>
|
||||
public required Prompt Prompt { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the handler for the prompt.
|
||||
/// </summary>
|
||||
public required Func<RequestContext<GetPromptRequestParams>, CancellationToken, Task<GetPromptResult>> Handler { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets this prompt definition.
|
||||
/// </summary>
|
||||
/// <param name="jsonPrompt">The JSON prompt template.</param>
|
||||
/// <param name="kernel">An instance of the kernel to render the prompt.
|
||||
/// If not provided, an instance registered in DI container will be used.
|
||||
/// </param>
|
||||
/// <returns>The prompt definition.</returns>
|
||||
public static PromptDefinition Create(string jsonPrompt, Kernel? kernel = null)
|
||||
{
|
||||
PromptTemplateConfig promptTemplateConfig = PromptTemplateConfig.FromJson(jsonPrompt);
|
||||
|
||||
IPromptTemplate promptTemplate = new HandlebarsPromptTemplateFactory().Create(promptTemplateConfig);
|
||||
|
||||
return new PromptDefinition()
|
||||
{
|
||||
Prompt = GetPrompt(promptTemplateConfig),
|
||||
Handler = (context, cancellationToken) =>
|
||||
{
|
||||
return GetPromptHandlerAsync(context, promptTemplateConfig, promptTemplate, kernel, cancellationToken);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates an MCP prompt from SK prompt template.
|
||||
/// </summary>
|
||||
/// <param name="promptTemplateConfig">The prompt template configuration.</param>
|
||||
/// <returns>The MCP prompt.</returns>
|
||||
private static Prompt GetPrompt(PromptTemplateConfig promptTemplateConfig)
|
||||
{
|
||||
// Create the MCP prompt arguments
|
||||
List<PromptArgument>? arguments = null;
|
||||
|
||||
foreach (var inputVariable in promptTemplateConfig.InputVariables)
|
||||
{
|
||||
(arguments ??= []).Add(new()
|
||||
{
|
||||
Name = inputVariable.Name,
|
||||
Description = inputVariable.Description,
|
||||
Required = inputVariable.IsRequired
|
||||
});
|
||||
}
|
||||
|
||||
// Create the MCP prompt
|
||||
return new Prompt
|
||||
{
|
||||
Name = promptTemplateConfig.Name!,
|
||||
Description = promptTemplateConfig.Description,
|
||||
Arguments = arguments
|
||||
};
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Handles the prompt request by rendering the prompt.
|
||||
/// </summary>
|
||||
/// <param name="context">The MCP request context.</param>
|
||||
/// <param name="promptTemplateConfig">The prompt template configuration.</param>
|
||||
/// <param name="promptTemplate">The prompt template.</param>
|
||||
/// <param name="kernel">The kernel to render the prompt.</param>
|
||||
/// <param name="cancellationToken">The cancellation token.</param>
|
||||
/// <returns>The prompt.</returns>
|
||||
private static async Task<GetPromptResult> GetPromptHandlerAsync(RequestContext<GetPromptRequestParams> context, PromptTemplateConfig promptTemplateConfig, IPromptTemplate promptTemplate, Kernel? kernel, CancellationToken cancellationToken)
|
||||
{
|
||||
// Use either explicitly provided kernel or the one registered in DI container
|
||||
kernel ??= context.Server.Services?.GetRequiredService<Kernel>() ?? throw new InvalidOperationException("Kernel is not available.");
|
||||
|
||||
// Render the prompt
|
||||
string renderedPrompt = await promptTemplate.RenderAsync(
|
||||
kernel: kernel,
|
||||
arguments: context.Params?.Arguments is { } args ? new KernelArguments(args.ToDictionary(kvp => kvp.Key, kvp => (object?)kvp.Value)) : null,
|
||||
cancellationToken: cancellationToken);
|
||||
|
||||
// Create prompt result
|
||||
return new GetPromptResult()
|
||||
{
|
||||
Description = promptTemplateConfig.Description,
|
||||
Messages =
|
||||
[
|
||||
new PromptMessage()
|
||||
{
|
||||
Content = new TextContentBlock()
|
||||
{
|
||||
Text = renderedPrompt
|
||||
},
|
||||
Role = Role.Assistant
|
||||
}
|
||||
]
|
||||
};
|
||||
}
|
||||
}
|
||||
+94
@@ -0,0 +1,94 @@
|
||||
// Copyright (c) Microsoft. All rights reserved.
|
||||
|
||||
using Microsoft.SemanticKernel;
|
||||
using ModelContextProtocol.Protocol;
|
||||
using ModelContextProtocol.Server;
|
||||
|
||||
namespace MCPServer.Resources;
|
||||
|
||||
/// <summary>
|
||||
/// Represents a resource definition.
|
||||
/// </summary>
|
||||
public sealed class ResourceDefinition
|
||||
{
|
||||
/// <summary>
|
||||
/// The kernel function to invoke the resource handler.
|
||||
/// </summary>
|
||||
private KernelFunction? _kernelFunction = null;
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the MCP resource.
|
||||
/// </summary>
|
||||
public required Resource Resource { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the handler for the MCP resource.
|
||||
/// </summary>
|
||||
public required Delegate Handler { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the kernel instance to invoke the resource handler.
|
||||
/// If not provided, an instance registered in DI container will be used.
|
||||
/// </summary>
|
||||
public Kernel? Kernel { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Creates a new blob resource definition.
|
||||
/// </summary>
|
||||
/// <param name="uri">The URI of the resource.</param>
|
||||
/// <param name="name">The name of the resource.</param>
|
||||
/// <param name="content">The content of the resource.</param>
|
||||
/// <param name="mimeType">The MIME type of the resource.</param>
|
||||
/// <param name="description">The description of the resource.</param>
|
||||
/// <param name="kernel">The kernel instance to invoke the resource handler.
|
||||
/// If not provided, an instance registered in DI container will be used.
|
||||
/// </param>
|
||||
/// <returns>The created resource definition.</returns>
|
||||
public static ResourceDefinition CreateBlobResource(string uri, string name, byte[] content, string mimeType, string? description = null, Kernel? kernel = null)
|
||||
{
|
||||
return new()
|
||||
{
|
||||
Kernel = kernel,
|
||||
Resource = new() { Uri = uri, Name = name, Description = description },
|
||||
Handler = async (RequestContext<ReadResourceRequestParams> context, CancellationToken cancellationToken) =>
|
||||
{
|
||||
return new ReadResourceResult()
|
||||
{
|
||||
Contents =
|
||||
[
|
||||
new BlobResourceContents()
|
||||
{
|
||||
Blob = Convert.ToBase64String(content),
|
||||
Uri = uri,
|
||||
MimeType = mimeType,
|
||||
}
|
||||
],
|
||||
};
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Invokes the resource handler.
|
||||
/// </summary>
|
||||
/// <param name="context">The MCP server context.</param>
|
||||
/// <param name="cancellationToken">The cancellation token.</param>
|
||||
/// <returns>The result of the invocation.</returns>
|
||||
public async ValueTask<ReadResourceResult> InvokeHandlerAsync(RequestContext<ReadResourceRequestParams> context, CancellationToken cancellationToken)
|
||||
{
|
||||
this._kernelFunction ??= KernelFunctionFactory.CreateFromMethod(this.Handler);
|
||||
|
||||
this.Kernel
|
||||
??= context.Server.Services?.GetRequiredService<Kernel>()
|
||||
?? throw new InvalidOperationException("Kernel is not available.");
|
||||
|
||||
KernelArguments args = new()
|
||||
{
|
||||
{ "context", context },
|
||||
};
|
||||
|
||||
FunctionResult result = await this._kernelFunction.InvokeAsync(kernel: this.Kernel, arguments: args, cancellationToken: cancellationToken);
|
||||
|
||||
return result.GetValue<ReadResourceResult>() ?? throw new InvalidOperationException("The handler did not return a valid result.");
|
||||
}
|
||||
}
|
||||
+100
@@ -0,0 +1,100 @@
|
||||
// Copyright (c) Microsoft. All rights reserved.
|
||||
|
||||
using System.Text.RegularExpressions;
|
||||
using Microsoft.SemanticKernel;
|
||||
using ModelContextProtocol.Protocol;
|
||||
using ModelContextProtocol.Server;
|
||||
|
||||
namespace MCPServer.Resources;
|
||||
|
||||
/// <summary>
|
||||
/// Represents a resource template definition.
|
||||
/// </summary>
|
||||
public sealed class ResourceTemplateDefinition
|
||||
{
|
||||
/// <summary>
|
||||
/// The regular expression to match the resource template.
|
||||
/// </summary>
|
||||
private Regex? _regex = null;
|
||||
|
||||
/// <summary>
|
||||
/// The kernel function to invoke the resource template handler.
|
||||
/// </summary>
|
||||
private KernelFunction? _kernelFunction = null;
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the MCP resource template.
|
||||
/// </summary>
|
||||
public required ResourceTemplate ResourceTemplate { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the handler for the MCP resource template.
|
||||
/// </summary>
|
||||
public required Delegate Handler { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the kernel instance to invoke the resource template handler.
|
||||
/// If not provided, an instance registered in DI container will be used.
|
||||
/// </summary>
|
||||
public Kernel? Kernel { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Checks if the given Uri matches the resource template.
|
||||
/// </summary>
|
||||
/// <param name="uri">The Uri to check for match.</param>
|
||||
public bool IsMatch(string uri)
|
||||
{
|
||||
return this.GetRegex().IsMatch(uri);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Invokes the resource template handler.
|
||||
/// </summary>
|
||||
/// <param name="context">The MCP server context.</param>
|
||||
/// <param name="cancellationToken">The cancellation token.</param>
|
||||
/// <returns>The result of the invocation.</returns>
|
||||
public async ValueTask<ReadResourceResult> InvokeHandlerAsync(RequestContext<ReadResourceRequestParams> context, CancellationToken cancellationToken)
|
||||
{
|
||||
this._kernelFunction ??= KernelFunctionFactory.CreateFromMethod(this.Handler);
|
||||
|
||||
this.Kernel
|
||||
??= context.Server.Services?.GetRequiredService<Kernel>()
|
||||
?? throw new InvalidOperationException("Kernel is not available.");
|
||||
|
||||
KernelArguments args = new(source: this.GetArguments(context.Params!.Uri!))
|
||||
{
|
||||
{ "context", context },
|
||||
};
|
||||
|
||||
FunctionResult result = await this._kernelFunction.InvokeAsync(kernel: this.Kernel, arguments: args, cancellationToken: cancellationToken);
|
||||
|
||||
return result.GetValue<ReadResourceResult>() ?? throw new InvalidOperationException("The handler did not return a valid result.");
|
||||
}
|
||||
|
||||
private Regex GetRegex()
|
||||
{
|
||||
if (this._regex != null)
|
||||
{
|
||||
return this._regex;
|
||||
}
|
||||
|
||||
var pattern = "^" +
|
||||
Regex.Escape(this.ResourceTemplate.UriTemplate)
|
||||
.Replace("\\{", "(?<")
|
||||
.Replace("}", ">[^/]+)") +
|
||||
"$";
|
||||
|
||||
return this._regex = new(pattern, RegexOptions.Compiled);
|
||||
}
|
||||
|
||||
private Dictionary<string, object?> GetArguments(string uri)
|
||||
{
|
||||
var match = this.GetRegex().Match(uri);
|
||||
if (!match.Success)
|
||||
{
|
||||
throw new ArgumentException($"The uri '{uri}' does not match the template '{this.ResourceTemplate.UriTemplate}'.");
|
||||
}
|
||||
|
||||
return match.Groups.Cast<Group>().Where(g => g.Name != "0").ToDictionary(g => g.Name, g => (object?)g.Value);
|
||||
}
|
||||
}
|
||||
+29
@@ -0,0 +1,29 @@
|
||||
// Copyright (c) Microsoft. All rights reserved.
|
||||
|
||||
using Microsoft.Extensions.VectorData;
|
||||
|
||||
namespace MCPServer.Resources;
|
||||
|
||||
/// <summary>
|
||||
/// A simple data model for a record in the vector store.
|
||||
/// </summary>
|
||||
public class TextDataModel
|
||||
{
|
||||
/// <summary>
|
||||
/// Unique identifier for the record.
|
||||
/// </summary>
|
||||
[VectorStoreKey]
|
||||
public required Guid Key { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// The text content of the record.
|
||||
/// </summary>
|
||||
[VectorStoreData]
|
||||
public required string Text { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// The embedding for the record.
|
||||
/// </summary>
|
||||
[VectorStoreVector(1536)]
|
||||
public required ReadOnlyMemory<float> Embedding { get; init; }
|
||||
}
|
||||
+22
@@ -0,0 +1,22 @@
|
||||
// Copyright (c) Microsoft. All rights reserved.
|
||||
|
||||
using System.ComponentModel;
|
||||
using Microsoft.SemanticKernel;
|
||||
|
||||
namespace MCPServer.Tools;
|
||||
|
||||
/// <summary>
|
||||
/// A collection of utility methods for working with date time.
|
||||
/// </summary>
|
||||
internal sealed class DateTimeUtils
|
||||
{
|
||||
/// <summary>
|
||||
/// Retrieves the current date time in UTC.
|
||||
/// </summary>
|
||||
/// <returns>The current date time in UTC.</returns>
|
||||
[KernelFunction, Description("Retrieves the current date time in UTC.")]
|
||||
public static string GetCurrentDateTimeInUtc()
|
||||
{
|
||||
return DateTime.UtcNow.ToString("yyyy-MM-dd HH:mm:ss");
|
||||
}
|
||||
}
|
||||
+126
@@ -0,0 +1,126 @@
|
||||
// Copyright (c) Microsoft. All rights reserved.
|
||||
|
||||
using MCPServer.ProjectResources;
|
||||
using Microsoft.SemanticKernel;
|
||||
using ModelContextProtocol.Protocol;
|
||||
using ModelContextProtocol.Server;
|
||||
|
||||
namespace MCPServer.Tools;
|
||||
|
||||
/// <summary>
|
||||
/// A collection of utility methods for working with mailbox.
|
||||
/// </summary>
|
||||
internal sealed class MailboxUtils
|
||||
{
|
||||
/// <summary>
|
||||
/// Summarizes unread emails in the mailbox by using MCP sampling
|
||||
/// mechanism for summarization.
|
||||
/// </summary>
|
||||
[KernelFunction]
|
||||
public static async Task<string> SummarizeUnreadEmailsAsync([FromKernelServices] McpServer server)
|
||||
{
|
||||
if (server.ClientCapabilities?.Sampling is null)
|
||||
{
|
||||
throw new InvalidOperationException("The client does not support sampling.");
|
||||
}
|
||||
|
||||
// Create two sample emails with attachments
|
||||
var email1 = new Email
|
||||
{
|
||||
Sender = "sales.report@example.com",
|
||||
Subject = "Carretera Sales Report - Jan & Jun 2014",
|
||||
Body = "Hi there, I hope this email finds you well! Please find attached the sales report for the first half of 2014. " +
|
||||
"Please review the report and provide your feedback today, if possible." +
|
||||
"By the way, we're having a BBQ this Saturday at my place, and you're welcome to join. Let me know if you can make it!",
|
||||
Attachments = [EmbeddedResource.ReadAsBytes("SalesReport2014.png")]
|
||||
};
|
||||
|
||||
var email2 = new Email
|
||||
{
|
||||
Sender = "hr.department@example.com",
|
||||
Subject = "Employee Birthdays and Positions",
|
||||
Body = "Attached is the list of employee birthdays and their positions. Please check it and let me know of any updates by tomorrow." +
|
||||
"Also, we're planning a hike this Sunday morning. It would be great if you could join us. Let me know if you're interested!",
|
||||
Attachments = [EmbeddedResource.ReadAsBytes("EmployeeBirthdaysAndPositions.png")]
|
||||
};
|
||||
|
||||
CreateMessageRequestParams request = new()
|
||||
{
|
||||
SystemPrompt = "You are a helpful assistant. You will be provided with a list of emails. Please summarize them. Each email is followed by its attachments.",
|
||||
Messages = CreateMessagesFromEmails(email1, email2),
|
||||
Temperature = 0
|
||||
};
|
||||
|
||||
// Send the sampling request to the client to summarize the emails
|
||||
CreateMessageResult result = await server.SampleAsync(request, cancellationToken: CancellationToken.None);
|
||||
|
||||
// Assuming the response is a text message
|
||||
return (result.Content as TextContentBlock)!.Text;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates a list of SamplingMessage objects from a list of emails.
|
||||
/// </summary>
|
||||
/// <param name="emails">The list of emails.</param>
|
||||
/// <returns>A list of SamplingMessage objects.</returns>
|
||||
private static List<SamplingMessage> CreateMessagesFromEmails(params Email[] emails)
|
||||
{
|
||||
var messages = new List<SamplingMessage>();
|
||||
|
||||
foreach (var email in emails)
|
||||
{
|
||||
messages.Add(new SamplingMessage
|
||||
{
|
||||
Role = Role.User,
|
||||
Content = new TextContentBlock
|
||||
{
|
||||
Text = $"Email from {email.Sender} with subject {email.Subject}. Body: {email.Body}",
|
||||
}
|
||||
});
|
||||
|
||||
if (email.Attachments != null && email.Attachments.Count != 0)
|
||||
{
|
||||
foreach (var attachment in email.Attachments)
|
||||
{
|
||||
messages.Add(new SamplingMessage
|
||||
{
|
||||
Role = Role.User,
|
||||
Content = new ImageContentBlock
|
||||
{
|
||||
Data = Convert.ToBase64String(attachment),
|
||||
MimeType = "image/png",
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return messages;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Represents an email.
|
||||
/// </summary>
|
||||
private sealed class Email
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets or sets the email sender.
|
||||
/// </summary>
|
||||
public required string Sender { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the email subject.
|
||||
/// </summary>
|
||||
public required string Subject { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the email body.
|
||||
/// </summary>
|
||||
public required string Body { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the email attachments.
|
||||
/// </summary>
|
||||
public List<byte[]>? Attachments { get; set; }
|
||||
}
|
||||
}
|
||||
+33
@@ -0,0 +1,33 @@
|
||||
// Copyright (c) Microsoft. All rights reserved.
|
||||
|
||||
using Microsoft.SemanticKernel;
|
||||
|
||||
namespace MCPServer.Tools;
|
||||
|
||||
/// <summary>
|
||||
/// A collection of utility methods for working with orders.
|
||||
/// </summary>
|
||||
internal sealed class OrderProcessingUtils
|
||||
{
|
||||
/// <summary>
|
||||
/// Places an order for the specified item.
|
||||
/// </summary>
|
||||
/// <param name="itemName">The name of the item to be ordered.</param>
|
||||
/// <returns>A string indicating the result of the order placement.</returns>
|
||||
[KernelFunction]
|
||||
public string PlaceOrder(string itemName)
|
||||
{
|
||||
return "success";
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Executes a refund for the specified item.
|
||||
/// </summary>
|
||||
/// <param name="itemName">The name of the item to be refunded.</param>
|
||||
/// <returns>A string indicating the result of the refund execution.</returns>
|
||||
[KernelFunction]
|
||||
public string ExecuteRefund(string itemName)
|
||||
{
|
||||
return "success";
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,34 @@
|
||||
// Copyright (c) Microsoft. All rights reserved.
|
||||
|
||||
using System.ComponentModel;
|
||||
using Microsoft.SemanticKernel;
|
||||
|
||||
namespace MCPServer.Tools;
|
||||
|
||||
/// <summary>
|
||||
/// A collection of utility methods for working with weather.
|
||||
/// </summary>
|
||||
internal sealed class WeatherUtils
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets the current weather for the specified city.
|
||||
/// </summary>
|
||||
/// <param name="cityName">The name of the city.</param>
|
||||
/// <param name="currentDateTimeInUtc">The current date time in UTC.</param>
|
||||
/// <returns>The current weather for the specified city.</returns>
|
||||
[KernelFunction, Description("Gets the current weather for the specified city and specified date time.")]
|
||||
public static string GetWeatherForCity(string cityName, string currentDateTimeInUtc)
|
||||
{
|
||||
return cityName switch
|
||||
{
|
||||
"Boston" => "61 and rainy",
|
||||
"London" => "55 and cloudy",
|
||||
"Miami" => "80 and sunny",
|
||||
"Paris" => "60 and rainy",
|
||||
"Tokyo" => "50 and sunny",
|
||||
"Sydney" => "75 and sunny",
|
||||
"Tel Aviv" => "80 and sunny",
|
||||
_ => "31 and snowing",
|
||||
};
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
{
|
||||
"Logging": {
|
||||
"LogLevel": {
|
||||
"Default": "Information",
|
||||
"Microsoft.Hosting.Lifetime": "Information"
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user