using Microsoft.Extensions.DependencyInjection;
using ModelContextProtocol.Server;
using Nexus.Api.Services;
using Xunit;
namespace Nexus.Api.Tests;
///
/// Verifies that the MCP server configuration is correct:
/// all tools are registered, the streamable-http transport is configured
/// via the service extensions, and MapMcp is called in Program.cs.
///
public sealed class McpServerConfigurationTests
{
[Fact]
public void McpServer_CanBeRegistered_WithoutError()
{
var services = new ServiceCollection();
services.AddLogging();
services.AddOptions();
services.AddHttpContextAccessor();
// Simulate what AddNexusApplicationServices does
services.AddMcpServer()
.WithHttpTransport(options => options.Stateless = true)
.WithTools();
// Build the container — this should not throw
var provider = services.BuildServiceProvider();
// Verify the ToolType is properly decorated
var attr = typeof(NexusMcpTools).GetCustomAttributes(
typeof(McpServerToolTypeAttribute), inherit: false);
Assert.Single(attr);
}
[Fact]
public void McpServer_ToolTypeAttribute_IsPresent()
{
var attr = typeof(NexusMcpTools).GetCustomAttributes(
typeof(McpServerToolTypeAttribute), inherit: false);
Assert.Single(attr);
}
[Fact]
public void McpEndpoint_MapMcp_IsCalledInProgram()
{
// Source path relative to the test output directory
var programPath = ResolveSourcePath("backend", "Program.cs");
Assert.True(File.Exists(programPath), $"Program.cs not found at {programPath}");
var source = File.ReadAllText(programPath);
Assert.Contains("MapMcp", source, StringComparison.Ordinal);
}
[Fact]
public void McpServerRegistration_IsCalledInServiceExtensions()
{
var extPath = ResolveSourcePath("backend", "Extensions", "ServiceCollectionExtensions.cs");
Assert.True(File.Exists(extPath), $"ServiceCollectionExtensions.cs not found at {extPath}");
var source = File.ReadAllText(extPath);
Assert.Contains("AddMcpServer", source, StringComparison.Ordinal);
Assert.Contains("WithHttpTransport", source, StringComparison.Ordinal);
Assert.Contains("Stateless", source, StringComparison.Ordinal);
Assert.Contains("WithTools", source, StringComparison.Ordinal);
}
[Fact]
public void NuGetPackage_ModelContextProtocol_AspNetCore_IsReferenced()
{
var csprojPath = ResolveSourcePath("backend", "Nexus.Api.csproj");
Assert.True(File.Exists(csprojPath), $"Nexus.Api.csproj not found at {csprojPath}");
var source = File.ReadAllText(csprojPath);
Assert.Contains("ModelContextProtocol.AspNetCore", source, StringComparison.Ordinal);
}
private static string ResolveSourcePath(params string[] segments)
{
// Navigate from test output directory to the repo root
// Test DLL is at: backend-tests/bin/Debug/net10.0/Nexus.Api.Tests.dll
// We go up 4 levels (net10.0 → Debug → bin → backend-tests) to reach repo root
var baseDir = AppContext.BaseDirectory;
var repoRoot = Path.GetFullPath(Path.Combine(baseDir, "..", "..", "..", ".."));
return Path.Combine(new[] { repoRoot }.Concat(segments).ToArray());
}
}