Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -197,6 +197,9 @@ public static IResourceBuilder<McpInspectorResource> WithMcpServer<TResource>(
ArgumentNullException.ThrowIfNull(builder);

builder.Resource.AddMcpServer(mcpServer.Resource, isDefault, transportType, path);

mcpServer.WithRelationship(builder.Resource, "InspectedBy");

return builder;
}

Expand Down Expand Up @@ -239,11 +242,12 @@ internal static Uri Combine(string baseUrl, params string[] segments)
if (Uri.IsWellFormedUriString(segments[0], UriKind.Absolute))
return new Uri(segments[0], UriKind.Absolute);

var escaped = segments
var escapedSegments = segments
.Where(s => !string.IsNullOrEmpty(s))
.Select(s => Uri.EscapeDataString(s.Trim('/')));
.SelectMany(s => s.Trim('/').Split('/', StringSplitOptions.RemoveEmptyEntries))
.Select(Uri.EscapeDataString);

var relative = string.Join("/", escaped);
var relative = string.Join("/", escapedSegments);

return new Uri(baseUri, relative);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -297,4 +297,81 @@ public void AddMcpInspectorWithConfigurationDelegateCreatesResourceCorrectly()
Assert.Equal(3333, clientEndpoint.Port);
Assert.Equal(4444, serverEndpoint.Port);
}

[Fact]
public void WithMcpServerCreatesResourceRelationshipAnnotations()
{
// Arrange
var appBuilder = DistributedApplication.CreateBuilder();

// Create a mock MCP server resource
var mockServer = appBuilder.AddProject<Projects.CommunityToolkit_Aspire_Hosting_McpInspector_McpServer>("mcpServer");

// Act
var inspector = appBuilder.AddMcpInspector("inspector")
.WithMcpServer(mockServer, isDefault: true);

using var app = appBuilder.Build();

// Assert
var appModel = app.Services.GetRequiredService<DistributedApplicationModel>();

var inspectorResource = Assert.Single(appModel.Resources.OfType<McpInspectorResource>());
var serverResource = appModel.Resources.Single(r => r.Name == "mcpServer");

// The inspector and/or server should have a resource relationship annotation linking them.
var serverHasRelationship = serverResource.TryGetAnnotationsOfType<ResourceRelationshipAnnotation>(out var serverRelationships);
var inspectorHasRelationship = inspectorResource.TryGetAnnotationsOfType<ResourceRelationshipAnnotation>(out var inspectorRelationships);

Assert.True(serverHasRelationship || inspectorHasRelationship, "Expected a ResourceRelationshipAnnotation on either the server or inspector resource.");
}

[Fact]
public void WithMcpServerPreservesCustomPathSegments()
{
// Arrange
var appBuilder = DistributedApplication.CreateBuilder();

// Create a mock MCP server resource
var mockServer = appBuilder.AddProject<Projects.CommunityToolkit_Aspire_Hosting_McpInspector_McpServer>("mcpServer");

// Act
var inspector = appBuilder.AddMcpInspector("inspector")
.WithMcpServer(mockServer, isDefault: true, path: "/route/mcp");

using var app = appBuilder.Build();

// Assert
var appModel = app.Services.GetRequiredService<DistributedApplicationModel>();

var inspectorResource = Assert.Single(appModel.Resources.OfType<McpInspectorResource>());

Assert.Single(inspectorResource.McpServers);
var serverMeta = inspectorResource.McpServers.Single();

// Path should be preserved exactly as provided (not url-encoded)
Assert.Equal("/route/mcp", serverMeta.Path);
}

[Fact]
public void CombineHandlesMultipleSegmentsAndDoesNotEncodeSlashes()
{
// Arrange
var baseUrl = "http://localhost:1234";
var segments = new[] { "/route/mcp", "nested/path" };

// Use reflection to call internal Combine
var type = typeof(McpInspectorResourceBuilderExtensions);
var method = type.GetMethod("Combine", System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Static);
Assert.NotNull(method);

// Act
var result = method!.Invoke(null, new object[] { baseUrl, segments }) as Uri;

// Assert
Assert.NotNull(result);
// Ensure that slashes from segments are preserved and not percent-encoded
var expected = new Uri("http://localhost:1234/route/mcp/nested/path");
Assert.Equal(expected, result);
}
}
Loading