using System.Reflection; using System.Text.RegularExpressions; using FluentAssertions; using ModelContextProtocol.Server; using Xunit; namespace PaperlessMCP.Tests.Tools; public class ToolNamingTests { private static readonly Regex AnthropicToolNamePattern = new("^[a-zA-Z0-9_-]{1,64}$"); [Fact] public void AllToolNames_ShouldMatchAnthropicApiNamingRules() { var toolAssembly = typeof(PaperlessMCP.Tools.HealthTools).Assembly; var toolNames = toolAssembly.GetTypes() .SelectMany(t => t.GetMethods(BindingFlags.Public | BindingFlags.Static | BindingFlags.Instance)) .SelectMany(m => m.GetCustomAttributes()) .Where(a => a.Name is not null) .Select(a => a.Name!) .ToList(); toolNames.Should().NotBeEmpty("expected to find at least one McpServerTool attribute"); var violations = toolNames .Where(name => !AnthropicToolNamePattern.IsMatch(name)) .ToList(); violations.Should().BeEmpty( "tool names must match ^[a-zA-Z0-9_-]{{1,64}}$ per Anthropic API rules, " + $"but found: {string.Join(", ", violations)}"); } [Fact] public void AllToolNames_ShouldNotContainDots() { var toolAssembly = typeof(PaperlessMCP.Tools.HealthTools).Assembly; var toolNames = toolAssembly.GetTypes() .SelectMany(t => t.GetMethods(BindingFlags.Public | BindingFlags.Static | BindingFlags.Instance)) .SelectMany(m => m.GetCustomAttributes()) .Where(a => a.Name is not null) .Select(a => a.Name!) .ToList(); var dotNames = toolNames.Where(name => name.Contains('.')).ToList(); dotNames.Should().BeEmpty( $"dots are not allowed in tool names, but found: {string.Join(", ", dotNames)}"); } }