Skip to content

Commit e6ac8eb

Browse files
committed
Scaffolding out a MCP Inspector resource
This is only designed to work with SSE transport type, as it will configure that through Aspire. If you want to use SSE, you can still launch it, but it will require some manual configuration in the inspector UI once started. Currently it runs the inspector via `npx`, since there's no docker image available. Fixes #622
1 parent 0ebb2d6 commit e6ac8eb

14 files changed

+1463
-6
lines changed

CommunityToolkit.Aspire.sln

+1,252
Large diffs are not rendered by default.

Directory.Packages.props

+4-6
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@
77
<PackageVersion Include="Aspire.Hosting" Version="$(AspireVersion)" />
88
<PackageVersion Include="Aspire.Hosting.AppHost" Version="$(AspireVersion)" />
99
<PackageVersion Include="Aspire.Hosting.Dapr" Version="$(AspireVersion)" />
10-
<PackageVersion Include="Aspire.Hosting.Azure.AppContainers" Version="$(AspireVersion)" />
10+
<PackageVersion Include="Aspire.Hosting.Azure.AppContainers" Version="$(AspireVersion)" />
1111
<PackageVersion Include="Aspire.Hosting.Azure.Redis" Version="$(AspireVersion)" />
1212
<PackageVersion Include="Aspire.Hosting.NodeJS" Version="$(AspireVersion)" />
1313
<PackageVersion Include="Aspire.Hosting.PostgreSQL" Version="$(AspireVersion)" />
@@ -16,6 +16,8 @@
1616
<PackageVersion Include="Aspire.Hosting.Redis" Version="$(AspireVersion)" />
1717
<PackageVersion Include="Aspire.Hosting.MongoDB" Version="$(AspireVersion)" />
1818
<PackageVersion Include="Aspire.Hosting.SqlServer" Version="$(AspireVersion)" />
19+
<PackageVersion Include="ModelContextProtocol" Version="0.1.0-preview.6" />
20+
<PackageVersion Include="ModelContextProtocol.AspNetCore" Version="0.1.0-preview.6" />
1921
</ItemGroup>
2022
<ItemGroup Label="Core Packages">
2123
<!-- AspNetCore packages -->
@@ -53,11 +55,9 @@
5355
<PackageVersion Include="OpenTelemetry.Instrumentation.Runtime" Version="$(OpenTelemetryVersion)" />
5456
<PackageVersion Include="OpenTelemetry.Exporter.InMemory" Version="$(OpenTelemetryVersion)" />
5557
</ItemGroup>
56-
5758
<ItemGroup Label="Build Dependencies">
5859
<PackageVersion Include="Microsoft.DotNet.GenAPI.Task" Version="9.0.103-servicing.25065.25" />
5960
</ItemGroup>
60-
6161
<ItemGroup Label="Integration Packages">
6262
<PackageVersion Include="Azure.Provisioning.AppContainers" Version="1.0.0" />
6363
<PackageVersion Include="JsonSchema.Net" Version="7.3.3" />
@@ -82,7 +82,6 @@
8282
<PackageVersion Include="YamlDotNet" Version="16.3.0" />
8383
<PackageVersion Include="Swashbuckle.AspNetCore" Version="7.3.1" />
8484
</ItemGroup>
85-
8685
<ItemGroup Label="Testing">
8786
<!-- Testing packages -->
8887
<PackageVersion Include="Aspire.Hosting.Testing" Version="$(AspireVersion)" />
@@ -98,8 +97,7 @@
9897
<PackageVersion Include="Testcontainers" Version="$(TestContainersVersion)" />
9998
<PackageVersion Include="Testcontainers.MsSql" Version="$(TestContainersVersion)" />
10099
</ItemGroup>
101-
102100
<ItemGroup Label=".NET 9 Overrides" Condition="'$(TargetFramework)' == 'net9.0'">
103101
<PackageVersion Update="Microsoft.Extensions.Logging.Abstractions" Version="9.0.3" />
104102
</ItemGroup>
105-
</Project>
103+
</Project>

docs/diagnostics.md

+4
Original file line numberDiff line numberDiff line change
@@ -13,3 +13,7 @@ Once a release of .NET Aspire with that API is available, the API in the .NET As
1313
## CTASPIRE002
1414

1515
Support for loading extensions into SQLite requires either a NuGet package or folder path to the library to be provided, and as a result there is some custom logic to load the extension based on the path or NuGet package. This logic will require some experimenting to figure out edge cases, so the feature for extension loading will be kept as experimental until it is proven to be stable.
16+
17+
## CTASPIRE003
18+
19+
The underlying type used here may change to a different resource type in the future. Avoid taking a direct dependency on the Aspire type, instead rely on the CommunityToolkit resource type returned or the `Resource` type from Aspire.
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
<Project Sdk="Microsoft.NET.Sdk">
2+
3+
<Sdk Name="Aspire.AppHost.Sdk" Version="$(AspireAppHostSdkVersion)" />
4+
5+
<PropertyGroup>
6+
<OutputType>Exe</OutputType>
7+
<ImplicitUsings>enable</ImplicitUsings>
8+
<Nullable>enable</Nullable>
9+
<IsAspireHost>true</IsAspireHost>
10+
<UserSecretsId>985811f2-fd0c-480a-885b-bc6cc0574b62</UserSecretsId>
11+
</PropertyGroup>
12+
13+
<ItemGroup>
14+
<PackageReference Include="Aspire.Hosting.AppHost" />
15+
</ItemGroup>
16+
17+
<ItemGroup>
18+
<ProjectReference Include="../../../src/CommunityToolkit.Aspire.Hosting.McpInspector/CommunityToolkit.Aspire.Hosting.McpInspector.csproj" IsAspireProjectResource="false" />
19+
<ProjectReference Include="../CommunityToolkit.Aspire.Hosting.McpInspector.McpServer/CommunityToolkit.Aspire.Hosting.McpInspector.McpServer.csproj" />
20+
</ItemGroup>
21+
22+
</Project>
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
var builder = DistributedApplication.CreateBuilder(args);
2+
3+
var server = builder.AddProject<Projects.CommunityToolkit_Aspire_Hosting_McpInspector_McpServer>("mcp-server");
4+
5+
builder.AddMcpInspector("mcp-inspector")
6+
.WithMcpServer(server)
7+
;
8+
9+
builder.Build().Run();
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
{
2+
"$schema": "https://json.schemastore.org/launchsettings.json",
3+
"profiles": {
4+
"https": {
5+
"commandName": "Project",
6+
"dotnetRunMessages": true,
7+
"launchBrowser": true,
8+
"applicationUrl": "https://localhost:17118;http://localhost:15207",
9+
"environmentVariables": {
10+
"ASPNETCORE_ENVIRONMENT": "Development",
11+
"DOTNET_ENVIRONMENT": "Development",
12+
"DOTNET_DASHBOARD_OTLP_ENDPOINT_URL": "https://localhost:21173",
13+
"DOTNET_RESOURCE_SERVICE_ENDPOINT_URL": "https://localhost:22290"
14+
}
15+
},
16+
"http": {
17+
"commandName": "Project",
18+
"dotnetRunMessages": true,
19+
"launchBrowser": true,
20+
"applicationUrl": "http://localhost:15207",
21+
"environmentVariables": {
22+
"ASPNETCORE_ENVIRONMENT": "Development",
23+
"DOTNET_ENVIRONMENT": "Development",
24+
"DOTNET_DASHBOARD_OTLP_ENDPOINT_URL": "http://localhost:19133",
25+
"DOTNET_RESOURCE_SERVICE_ENDPOINT_URL": "http://localhost:20023"
26+
}
27+
}
28+
}
29+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
{
2+
"Logging": {
3+
"LogLevel": {
4+
"Default": "Information",
5+
"Microsoft.AspNetCore": "Warning",
6+
"Aspire.Hosting.Dcp": "Warning"
7+
}
8+
}
9+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
<Project Sdk="Microsoft.NET.Sdk.Web">
2+
<ItemGroup>
3+
<PackageReference Include="ModelContextProtocol" />
4+
<PackageReference Include="ModelContextProtocol.AspNetCore" />
5+
</ItemGroup>
6+
</Project>
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
using ModelContextProtocol.Server;
2+
using System.ComponentModel;
3+
4+
var builder = WebApplication.CreateBuilder(args);
5+
6+
builder.Services
7+
.AddMcpServer()
8+
.WithTools<McpServerTools>();
9+
10+
var app = builder.Build();
11+
12+
app.MapMcp();
13+
14+
app.Run();
15+
16+
[McpServerToolType]
17+
class McpServerTools
18+
{
19+
[McpServerTool, Description("An echo tool")]
20+
public static string Echo(string message) => $"Echo: {message}";
21+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
{
2+
"$schema": "https://json.schemastore.org/launchsettings.json",
3+
"profiles": {
4+
"http": {
5+
"commandName": "Project",
6+
"dotnetRunMessages": true,
7+
"launchBrowser": true,
8+
"applicationUrl": "http://localhost:5230",
9+
"environmentVariables": {
10+
"ASPNETCORE_ENVIRONMENT": "Development"
11+
}
12+
},
13+
"https": {
14+
"commandName": "Project",
15+
"dotnetRunMessages": true,
16+
"launchBrowser": true,
17+
"applicationUrl": "https://localhost:7295;http://localhost:5230",
18+
"environmentVariables": {
19+
"ASPNETCORE_ENVIRONMENT": "Development"
20+
}
21+
}
22+
}
23+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
{
2+
"Logging": {
3+
"LogLevel": {
4+
"Default": "Information",
5+
"Microsoft.AspNetCore": "Warning"
6+
}
7+
},
8+
"AllowedHosts": "*"
9+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
<Project Sdk="Microsoft.NET.Sdk">
2+
<PropertyGroup>
3+
<Description>An Aspire to run the MCP Inspector against a MCP server.</Description>
4+
<AdditionalPackageTags>ai mcp debugging hosting</AdditionalPackageTags>
5+
</PropertyGroup>
6+
7+
<ItemGroup>
8+
<PackageReference Include="Aspire.Hosting" />
9+
</ItemGroup>
10+
11+
</Project>
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
using System.Diagnostics.CodeAnalysis;
2+
3+
namespace Aspire.Hosting.ApplicationModel;
4+
5+
/// <summary>
6+
/// Resource for the MCP Inspector server.
7+
/// </summary>
8+
/// <param name="name">The name of the resource.</param>
9+
/// <remarks>
10+
/// This resource will run as a Node.js process using the npx command.
11+
///
12+
/// In future, it is likely to become a container resource, once <seealso href="https://github.com/modelcontextprotocol/inspector/issues/237"/> is resolved.
13+
/// </remarks>
14+
[Experimental("CTASPIRE003")]
15+
public class McpInspectorResource(string name) : ExecutableResource(name, "npx", "");
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
using Aspire.Hosting.ApplicationModel;
2+
#pragma warning disable CTASPIRE003
3+
4+
namespace Aspire.Hosting;
5+
6+
/// <summary>
7+
/// Provides extension methods for adding the MCP Inspector to an <see cref="IDistributedApplicationBuilder"/>.
8+
/// </summary>
9+
public static class McpInspectorResourceBuilderExtensions
10+
{
11+
/// <summary>
12+
/// Adds a MCP Inspector container resource to the <see cref="IDistributedApplicationBuilder"/>.
13+
/// </summary>
14+
/// <param name="builder">The <see cref="IDistributedApplicationBuilder"/> to which the MCP Inspector resource will be added.</param>
15+
/// <param name="name">The name of the MCP Inspector container resource.</param>
16+
public static IResourceBuilder<McpInspectorResource> AddMcpInspector(this IDistributedApplicationBuilder builder, [ResourceName] string name)
17+
{
18+
var resource = builder.AddResource(new McpInspectorResource(name))
19+
.WithArgs(["-y", "@modelcontextprotocol/inspector"])
20+
.ExcludeFromManifest()
21+
.WithHttpEndpoint(isProxied: false, port: Random.Shared.Next(3000, 4000), env: "CLIENT_PORT", name: "client")
22+
.WithHttpEndpoint(isProxied: false, port: Random.Shared.Next(4000, 5000), env: "SERVER_PORT", name: "server-proxy");
23+
24+
return resource
25+
.WithEnvironment(ctx =>
26+
{
27+
ctx.EnvironmentVariables["MCP_PROXY_FULL_ADDRESS"] = resource.GetEndpoint("server-proxy").Url;
28+
});
29+
}
30+
31+
/// <summary>
32+
/// Configures the MCP Inspector resource to use a specified MCP server resource that uses SSE as the transport type.
33+
/// </summary>
34+
/// <typeparam name="TResource">The type of the MCP server resource.</typeparam>
35+
/// <param name="builder">The <see cref="IResourceBuilder{T}"/> used to configure the MCP Inspector resource.</param>
36+
/// <param name="mcpServer">The <see cref="IResourceBuilder{T}"/> for the MCP server resource.</param>
37+
/// <param name="route">The route that the SSE connection will use.</param>
38+
/// <returns>A reference to the <see cref="IResourceBuilder{McpInspectorResource}"/> for further configuration.</returns>
39+
public static IResourceBuilder<McpInspectorResource> WithMcpServer<TResource>(this IResourceBuilder<McpInspectorResource> builder, IResourceBuilder<TResource> mcpServer, string route = "/sse")
40+
where TResource : IResourceWithEndpoints
41+
{
42+
return builder.WithArgs(ctx =>
43+
{
44+
var httpEndpoint = mcpServer.Resource.GetEndpoint("http");
45+
46+
ctx.Args.Add($"{httpEndpoint.Url}{route}");
47+
});
48+
}
49+
}

0 commit comments

Comments
 (0)