Use DescriptionAttribute and HasDefaultValue in server builder extensions (#46)

This commit is contained in:
Stephen Toub 2025-03-09 06:16:00 -04:00 • committed by GitHub
commit 33d2b0d762
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
6 changed files with 27 additions and 61 deletions

View file

@ -1,12 +1,13 @@
using McpDotNet.Server;
using System.ComponentModel;
namespace TestServerWithHosting.Tools;
[McpToolType]
public static class EchoTool
{
[McpTool(description: "Echoes the input back to the client.")]
public static string Echo([McpParameter(true)] string message)
[McpTool, Description("Echoes the input back to the client.")]
public static string Echo(string message)
{
return "hello " + message;
}

View file

@ -1,5 +1,6 @@
using McpDotNet.Protocol.Types;
using McpDotNet.Server;
using System.ComponentModel;
namespace TestServerWithHosting.Tools;
@ -16,10 +17,10 @@ public class SampleLlmTool
_server = server ?? throw new ArgumentNullException(nameof(server));
}
[McpTool(name: "sampleLLM", description: "Samples from an LLM using MCP's sampling feature")]
[McpTool("sampleLLM"), Description("Samples from an LLM using MCP's sampling feature")]
public async Task<string> SampleLLM(
[McpParameter(true, description: "The prompt to send to the LLM")] string prompt,
[McpParameter(true, description: "Maximum number of tokens to generate")] int maxTokens,
[Description("The prompt to send to the LLM")] string prompt,
[Description("Maximum number of tokens to generate")] int maxTokens,
CancellationToken cancellationToken)
{
var samplingParams = CreateRequestSamplingParams(prompt ?? string.Empty, "sampleLLM", maxTokens);

View file

@ -1,4 +1,5 @@
using System.Globalization;
using System.ComponentModel;
using System.Globalization;
using System.Reflection;
using System.Text.Json;
using McpDotNet.Configuration;
@ -117,7 +118,7 @@ public static partial class McpServerBuilderExtensions
return WithTools(builder, toolTypes.ToArray());
}
private static Tool CreateTool(MethodInfo method, McpToolAttribute attribute)
private static Tool CreateTool(MethodInfo method, McpToolAttribute mcpAttribute)
{
Dictionary<string, JsonSchemaProperty> properties = [];
List<string>? requiredProperties = null;
@ -127,15 +128,15 @@ public static partial class McpServerBuilderExtensions
if (parameter.ParameterType == typeof(CancellationToken))
continue;
var parameterAttribute = parameter.GetCustomAttribute<McpParameterAttribute>();
var parameterDescriptionAttr = parameter.GetCustomAttribute<DescriptionAttribute>();
properties.Add(parameter.Name ?? "NoName", new JsonSchemaProperty()
{
Type = GetParameterType(parameter.ParameterType),
Description = parameterAttribute?.Description
Description = parameterDescriptionAttr?.Description,
});
if (parameterAttribute?.Required == true)
if (!parameter.HasDefaultValue)
{
requiredProperties ??= [];
requiredProperties.Add(parameter.Name ?? "NoName");
@ -144,8 +145,8 @@ public static partial class McpServerBuilderExtensions
return new Tool()
{
Name = attribute.Name ?? method.Name,
Description = attribute.Description,
Name = mcpAttribute.Name ?? method.Name,
Description = method.GetCustomAttribute<DescriptionAttribute>()?.Description,
InputSchema = new JsonSchema()
{
Type = "object",
@ -229,12 +230,10 @@ public static partial class McpServerBuilderExtensions
}
else
{
var parameterAttribute = parameter.GetCustomAttribute<McpParameterAttribute>();
if (parameterAttribute?.Required == true)
if (!parameter.HasDefaultValue)
throw new McpServerException($"Missing required argument '{parameter.Name}'.");
parameters.Add(parameter.HasDefaultValue ? parameter.DefaultValue : null);
parameters.Add(parameter.DefaultValue);
}
}

View file

@ -1,29 +0,0 @@
namespace McpDotNet.Server;
/// <summary>
/// Attribute to mark a method parameter
/// </summary>
[AttributeUsage(AttributeTargets.Parameter)]
public sealed class McpParameterAttribute : Attribute
{
/// <summary>
/// Defines if the parameter is required.
/// </summary>
public bool Required { get; }
/// <summary>
/// A description of the parameter.
/// </summary>
public string? Description { get; }
/// <summary>
/// Attribute to mark a method as an MCP tool.
/// </summary>
/// <param name="required">True if the parameter is mandatory.</param>
/// <param name="description">A description of the tool.</param>
public McpParameterAttribute(bool required = false, string? description = null)
{
Required = required;
Description = description;
}
}

View file

@ -6,22 +6,15 @@
[AttributeUsage(AttributeTargets.Method)]
public sealed class McpToolAttribute : Attribute
{
/// The name of the tool. If not provided, the class name will be used.
/// <summary>Gets the name of the tool.</summary>
/// <remarks>If not provided, the method name will be used.</remarks>
public string? Name { get; }
/// <summary>
/// A description of the tool.
/// </summary>
public string? Description { get; }
/// <summary>
/// Attribute to mark a method as an MCP tool.
/// </summary>
/// <param name="name">The name of the tool. If not provided, the class name will be used.</param>
/// <param name="description">A description of the tool.</param>
public McpToolAttribute(string? name = null, string? description = null)
public McpToolAttribute(string? name = null)
{
Name = name;
Description = description;
}
}

View file

@ -3,6 +3,7 @@ using McpDotNet.Server;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Options;
using Moq;
using System.ComponentModel;
namespace McpDotNet.Tests;
@ -131,14 +132,14 @@ public class McpServerBuilderExtensionsToolsTests
private static class EchoTool
{
[McpTool(description: "Echoes the input back to the client.")]
public static string Echo([McpParameter(true, description: "the echoes message")] string message)
[McpTool, Description("Echoes the input back to the client.")]
public static string Echo([Description("the echoes message")] string message)
{
return "hello " + message;
}
[McpTool(name: "double_echo", description: "Echoes the input back to the client.")]
public static string Echo2([McpParameter(true)] string message)
[McpTool("double_echo"), Description("Echoes the input back to the client.")]
public static string Echo2(string message)
{
return "hello hello" + message;
}
@ -154,8 +155,8 @@ public class McpServerBuilderExtensionsToolsTests
{
}
[McpTool(description: "Echoes the input back to the client.")]
public Task<string> Echo([McpParameter(true)] string message)
[McpTool, Description("Echoes the input back to the client.")]
public Task<string> Echo(string message)
{
return Task.FromResult("hello " + message);
}