using ModelContextProtocol; using ModelContextProtocol.Protocol; using ModelContextProtocol.Server; using System.ComponentModel; using System.Text.Json; using System.Text.Json.Serialization; namespace ConformanceServer.Tools; [McpServerToolType] public class ConformanceTools { // Sample base64 encoded 1x1 red PNG pixel for testing private const string TestImageBase64 = "iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAADUlEQVR42mP8z8DwHwAFBQIAX8jx0gAAAABJRU5ErkJggg=="; // Sample base64 encoded minimal WAV file for testing private const string TestAudioBase64 = "UklGRiYAAABXQVZFZm10IBAAAAABAAEAQB8AAAB9AAACABAAZGF0YQIAAAA="; /// /// Simple text tool - returns simple text content for testing /// [McpServerTool(Name = "test_simple_text")] [Description("Tests simple text content response")] public static string SimpleText() { return "This is a simple text response for testing."; } /// /// Image content tool - returns base64-encoded image content /// [McpServerTool(Name = "test_image_content")] [Description("Tests image content response")] public static ImageContentBlock ImageContent() { return new ImageContentBlock { Data = TestImageBase64, MimeType = "image/png" }; } /// /// Audio content tool - returns base64-encoded audio content /// [McpServerTool(Name = "test_audio_content")] [Description("Tests audio content response")] public static AudioContentBlock AudioContent() { return new AudioContentBlock { Data = TestAudioBase64, MimeType = "audio/wav" }; } /// /// Embedded resource tool - returns embedded resource content /// [McpServerTool(Name = "test_embedded_resource")] [Description("Tests embedded resource content response")] public static EmbeddedResourceBlock EmbeddedResource() { return new EmbeddedResourceBlock { Resource = new TextResourceContents { Uri = "test://embedded-resource", MimeType = "text/plain", Text = "This is an embedded resource content." } }; } /// /// Multiple content types tool - returns mixed content types (text, image, resource) /// [McpServerTool(Name = "test_multiple_content_types")] [Description("Tests response with multiple content types (text, image, resource)")] public static ContentBlock[] MultipleContentTypes() { return [ new TextContentBlock { Text = "Multiple content types test:" }, new ImageContentBlock { Data = TestImageBase64, MimeType = "image/png" }, new EmbeddedResourceBlock { Resource = new TextResourceContents { Uri = "test://mixed-content-resource", MimeType = "application/json", Text = "{ \"test\" = \"data\", \"value\" = 123 }" } } ]; } /// /// Tool with logging - emits log messages during execution /// [McpServerTool(Name = "test_tool_with_logging")] [Description("Tests tool that emits log messages during execution")] public static async Task ToolWithLogging( RequestContext context, CancellationToken cancellationToken) { var server = context.Server; // Use ILogger for logging (will be forwarded to client if supported) ILoggerProvider loggerProvider = server.AsClientLoggerProvider(); ILogger logger = loggerProvider.CreateLogger("ConformanceTools"); logger.LogInformation("Tool execution started"); await Task.Delay(50, cancellationToken); logger.LogInformation("Tool processing data"); await Task.Delay(50, cancellationToken); logger.LogInformation("Tool execution completed"); return "Tool with logging executed successfully"; } /// /// Tool with progress - reports progress notifications /// [McpServerTool(Name = "test_tool_with_progress")] [Description("Tests tool that reports progress notifications")] public static async Task ToolWithProgress( McpServer server, RequestContext context, CancellationToken cancellationToken) { var progressToken = context.Params?.ProgressToken; if (progressToken is not null) { await server.NotifyProgressAsync(progressToken.Value, new ProgressNotificationValue { Progress = 0, Total = 100, }, cancellationToken: cancellationToken); await Task.Delay(50, cancellationToken); await server.NotifyProgressAsync(progressToken.Value, new ProgressNotificationValue { Progress = 50, Total = 100, }, cancellationToken: cancellationToken); await Task.Delay(50, cancellationToken); await server.NotifyProgressAsync(progressToken.Value, new ProgressNotificationValue { Progress = 100, Total = 100, }, cancellationToken: cancellationToken); } return progressToken?.ToString() ?? "No progress token provided"; } /// /// Error handling tool - intentionally throws an error for testing /// [McpServerTool(Name = "test_error_handling")] [Description("Tests error response handling")] public static string ErrorHandling() { throw new Exception("This tool intentionally returns an error for testing"); } /// /// Sampling tool - requests LLM completion from client /// [McpServerTool(Name = "test_sampling")] [Description("Tests server-initiated sampling (LLM completion request)")] public static async Task Sampling( McpServer server, [Description("The prompt to send to the LLM")] string prompt, CancellationToken cancellationToken) { try { var samplingParams = new CreateMessageRequestParams { Messages = [new SamplingMessage { Role = Role.User, Content = [new TextContentBlock { Text = prompt }], }], MaxTokens = 100, Temperature = 0.7f }; var result = await server.SampleAsync(samplingParams, cancellationToken); return $"Sampling result: {(result.Content.FirstOrDefault() as TextContentBlock)?.Text ?? "No text content"}"; } catch (Exception ex) { return $"Sampling not supported or error: {ex.Message}"; } } /// /// Elicitation tool - requests user input from client /// [McpServerTool(Name = "test_elicitation")] [Description("Tests elicitation (user input request from client)")] public static async Task Elicitation( McpServer server, [Description("Message to show to the user")] string message, CancellationToken cancellationToken) { try { var schema = new ElicitRequestParams.RequestSchema { Properties = { ["response"] = new ElicitRequestParams.StringSchema() { Description = "User's response to the message" } } }; var result = await server.ElicitAsync(new ElicitRequestParams { Message = message, RequestedSchema = schema }, cancellationToken); if (result.Action == "accept" && result.Content != null) { return $"User responded: {result.Content["response"].GetString()}"; } else { return $"Elicitation {result.Action}"; } } catch (Exception ex) { return $"Elicitation not supported or error: {ex.Message}"; } } /// /// SEP-1034: Elicitation with default values for all primitive types /// [McpServerTool(Name = "test_elicitation_sep1034_defaults")] [Description("Tests elicitation with default values per SEP-1034")] public static async Task ElicitationSep1034Defaults( McpServer server, CancellationToken cancellationToken) { try { var schema = new ElicitRequestParams.RequestSchema { Properties = { ["name"] = new ElicitRequestParams.StringSchema() { Description = "Name", Default = "John Doe" }, ["age"] = new ElicitRequestParams.NumberSchema() { Type = "integer", Description = "Age", Default = 30 }, ["score"] = new ElicitRequestParams.NumberSchema() { Description = "Score", Default = 95.5 }, ["status"] = new ElicitRequestParams.UntitledSingleSelectEnumSchema() { Description = "Status", Enum = ["active", "inactive", "pending"], Default = "active" }, ["verified"] = new ElicitRequestParams.BooleanSchema() { Description = "Verified", Default = true } } }; var result = await server.ElicitAsync(new ElicitRequestParams { Message = "Test elicitation with default values for primitive types", RequestedSchema = schema }, cancellationToken); if (result.Action == "accept" && result.Content != null) { return $"Accepted with values: string={result.Content["stringField"].GetString()}, " + $"number={result.Content["numberField"].GetInt32()}, " + $"boolean={result.Content["booleanField"].GetBoolean()}"; } else { return $"Elicitation {result.Action}"; } } catch (Exception ex) { return $"Elicitation not supported or error: {ex.Message}"; } } /// /// SEP-1330: Elicitation with enum schema improvements /// [McpServerTool(Name = "test_elicitation_sep1330_enums")] [Description("Tests elicitation with enum schema improvements per SEP-1330")] public static async Task ElicitationSep1330Enums( McpServer server, CancellationToken cancellationToken) { try { var schema = new ElicitRequestParams.RequestSchema { Properties = { ["color"] = new ElicitRequestParams.UntitledSingleSelectEnumSchema() { Description = "Choose a color", Enum = ["red", "green", "blue"] }, ["size"] = new ElicitRequestParams.UntitledSingleSelectEnumSchema() { Description = "Choose a size", Enum = ["small", "medium", "large"], Default = "medium" } } }; var result = await server.ElicitAsync(new ElicitRequestParams { Message = "Test elicitation with enum schema", RequestedSchema = schema }, cancellationToken); if (result.Action == "accept" && result.Content != null) { return $"Accepted with values: color={result.Content["color"].GetString()}, " + $"size={result.Content["size"].GetString()}"; } else { return $"Elicitation {result.Action}"; } } catch (Exception ex) { return $"Elicitation not supported or error: {ex.Message}"; } } }