using ModelContextProtocol.Configuration;
using ModelContextProtocol.Server;
using ModelContextProtocol.Utils;
using System.Diagnostics.CodeAnalysis;
using System.Reflection;
using Microsoft.Extensions.DependencyInjection;
namespace ModelContextProtocol;
///
/// Extension to configure the MCP server with tools
///
public static partial class McpServerBuilderExtensions
{
private const string WithToolsRequiresUnreferencedCodeMessage =
$"The non-generic {nameof(WithTools)} and {nameof(WithToolsFromAssembly)} methods require dynamic lookup of method metadata" +
$"and may not work in Native AOT. Use the generic {nameof(WithTools)} method instead.";
/// Adds instances to the service collection backing .
/// The tool type.
/// The builder instance.
/// is .
///
/// This method discovers all instance and static methods (public and non-public) on the specified
/// type, where the methods are attributed as , and adds an
/// instance for each. For instance methods, an instance will be constructed for each invocation of the tool.
///
public static IMcpServerBuilder WithTools<[DynamicallyAccessedMembers(
DynamicallyAccessedMemberTypes.PublicMethods |
DynamicallyAccessedMemberTypes.NonPublicMethods |
DynamicallyAccessedMemberTypes.PublicConstructors)] TTool>(
this IMcpServerBuilder builder)
{
Throw.IfNull(builder);
foreach (var toolMethod in typeof(TTool).GetMethods(BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Static | BindingFlags.Instance))
{
if (toolMethod.GetCustomAttribute() is not null)
{
if (toolMethod.IsStatic)
{
builder.Services.AddSingleton(services => McpServerTool.Create(toolMethod, services: services));
}
else
{
builder.Services.AddSingleton(services => McpServerTool.Create(toolMethod, typeof(TTool), services: services));
}
}
}
return builder;
}
/// Adds instances to the service collection backing .
/// The builder instance.
/// Types with marked methods to add as tools to the server.
/// is .
/// is .
///
/// This method discovers all instance and static methods (public and non-public) on the specified
/// types, where the methods are attributed as , and adds an
/// instance for each. For instance methods, an instance will be constructed for each invocation of the tool.
///
[RequiresUnreferencedCode(WithToolsRequiresUnreferencedCodeMessage)]
public static IMcpServerBuilder WithTools(this IMcpServerBuilder builder, params IEnumerable toolTypes)
{
Throw.IfNull(builder);
Throw.IfNull(toolTypes);
foreach (var toolType in toolTypes)
{
if (toolType is not null)
{
foreach (var method in toolType.GetMethods(BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Static | BindingFlags.Instance))
{
if (method.GetCustomAttribute() is not null)
{
if (method.IsStatic)
{
builder.Services.AddSingleton(services => McpServerTool.Create(method, services: services));
}
else
{
builder.Services.AddSingleton(services => McpServerTool.Create(method, toolType, services: services));
}
}
}
}
}
return builder;
}
///
/// Adds types marked with the attribute from the given assembly as tools to the server.
///
/// The builder instance.
/// The assembly to load the types from. Null to get the current assembly
/// is .
[RequiresUnreferencedCode(WithToolsRequiresUnreferencedCodeMessage)]
public static IMcpServerBuilder WithToolsFromAssembly(this IMcpServerBuilder builder, Assembly? toolAssembly = null)
{
Throw.IfNull(builder);
toolAssembly ??= Assembly.GetCallingAssembly();
return builder.WithTools(
from t in toolAssembly.GetTypes()
where t.GetCustomAttribute() is not null
select t);
}
}