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); } }