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 @@ -54,9 +54,7 @@ public static IResourceBuilder<McpInspectorResource> AddMcpInspector(this IDistr
var packageName = $"@modelcontextprotocol/inspector@{options.InspectorVersion}";

var resourceBuilder = builder.AddResource(new McpInspectorResource(name, packageName))
.WithNpm(install: true, installArgs: ["-y", $"@modelcontextprotocol/inspector@{options.InspectorVersion}", "--no-save", "--no-package-lock"])
.WithCommand("npx")
.WithArgs(["-y", $"@modelcontextprotocol/inspector@{options.InspectorVersion}"])
.WithCertificateTrustConfiguration(ctx =>
{
if (ctx.Scope == CertificateTrustScope.Append)
Expand Down Expand Up @@ -320,6 +318,9 @@ private static IResourceBuilder<McpInspectorResource> WithInspectorArgs(this IRe
ctx.Args.Insert(0, packageName);
ctx.Args.Insert(0, "dlx");
break;
case "bunx":
ctx.Args.Insert(0, packageName);
break;
default: // npm/npx
ctx.Args.Insert(0, packageName);
ctx.Args.Insert(0, "-y");
Expand Down Expand Up @@ -351,4 +352,16 @@ public static IResourceBuilder<McpInspectorResource> WithPnpm(this IResourceBuil

return builder.WithCommand("pnpm");
}

/// <summary>
/// Configures the MCP Inspector to use bun as the package manager.
/// </summary>
/// <param name="builder">The MCP Inspector resource builder.</param>
/// <returns>A reference to the <see cref="IResourceBuilder{T}"/>.</returns>
public static IResourceBuilder<McpInspectorResource> WithBun(this IResourceBuilder<McpInspectorResource> builder)
{
ArgumentNullException.ThrowIfNull(builder);

return builder.WithCommand("bunx");
}
}
9 changes: 7 additions & 2 deletions src/CommunityToolkit.Aspire.Hosting.McpInspector/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ You can specify the transport type (`StreamableHttp`) and set which server is th

#### Using alternative package managers

By default, the MCP Inspector uses npm/npx. You can configure it to use yarn or pnpm instead by chaining the appropriate method:
By default, the MCP Inspector uses npm/npx. You can configure it to use yarn, pnpm, or bun instead by chaining the appropriate method:

```csharp
// Using yarn
Expand All @@ -39,9 +39,14 @@ var inspector = builder.AddMcpInspector("inspector")
var inspector = builder.AddMcpInspector("inspector")
.WithPnpm()
.WithMcpServer(mcpServer);

// Using bun
var inspector = builder.AddMcpInspector("inspector")
.WithBun()
.WithMcpServer(mcpServer);
```

When using yarn or pnpm, the inspector will use `yarn dlx` or `pnpm dlx` respectively to run the MCP Inspector package.
When using yarn, pnpm, or bun, the inspector will use `yarn dlx`, `pnpm dlx`, or `bunx` respectively to run the MCP Inspector package.

#### Using options for complex configurations

Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
using Aspire.Hosting;
using Aspire.Hosting.ApplicationModel;
using Aspire.Hosting.JavaScript;
using Aspire.Hosting.Eventing;

namespace CommunityToolkit.Aspire.Hosting.McpInspector.Tests;

Expand Down Expand Up @@ -517,7 +517,7 @@ public void WithYarnSetsCommand()
var appBuilder = DistributedApplication.CreateBuilder();

// Act
var inspector = appBuilder.AddMcpInspector("inspector")
appBuilder.AddMcpInspector("inspector", new McpInspectorOptions())
.WithYarn();

using var app = appBuilder.Build();
Expand All @@ -536,7 +536,7 @@ public void WithPnpmSetsCommand()
var appBuilder = DistributedApplication.CreateBuilder();

// Act
var inspector = appBuilder.AddMcpInspector("inspector")
appBuilder.AddMcpInspector("inspector", new McpInspectorOptions())
.WithPnpm();

using var app = appBuilder.Build();
Expand All @@ -548,6 +548,25 @@ public void WithPnpmSetsCommand()
Assert.Equal("pnpm", inspectorResource.Command);
}

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

// Act
appBuilder.AddMcpInspector("inspector", new McpInspectorOptions())
.WithBun();

using var app = appBuilder.Build();

// Assert
var appModel = app.Services.GetRequiredService<DistributedApplicationModel>();
var inspectorResource = Assert.Single(appModel.Resources.OfType<McpInspectorResource>());

Assert.Equal("bunx", inspectorResource.Command);
}

[Fact]
public async Task WithYarnSetsCorrectArguments()
{
Expand All @@ -567,12 +586,9 @@ public async Task WithYarnSetsCorrectArguments()
var appModel = app.Services.GetRequiredService<DistributedApplicationModel>();
var inspectorResource = Assert.Single(appModel.Resources.OfType<McpInspectorResource>());

var args = await inspectorResource.GetArgumentValuesAsync();
var argsList = args.ToList();
var args = await GetArgsAsync(inspectorResource);

// For yarn, the first arg should be "dlx"
Assert.Equal("dlx", argsList[0]);
Assert.Equal("@modelcontextprotocol/inspector@0.15.0", argsList[1]);
Assert.Equal(new[] { "dlx", "@modelcontextprotocol/inspector@0.15.0" }, args);
}

[Fact]
Expand All @@ -594,12 +610,33 @@ public async Task WithPnpmSetsCorrectArguments()
var appModel = app.Services.GetRequiredService<DistributedApplicationModel>();
var inspectorResource = Assert.Single(appModel.Resources.OfType<McpInspectorResource>());

var args = await inspectorResource.GetArgumentValuesAsync();
var argsList = args.ToList();
var args = await GetArgsAsync(inspectorResource);

// For pnpm, the first arg should be "dlx"
Assert.Equal("dlx", argsList[0]);
Assert.Equal("@modelcontextprotocol/inspector@0.15.0", argsList[1]);
Assert.Equal(new[] { "dlx", "@modelcontextprotocol/inspector@0.15.0" }, args);
}

[Fact]
public async Task WithBunSetsCorrectArguments()
{
// Arrange
var appBuilder = DistributedApplication.CreateBuilder();

// Act
var inspector = appBuilder.AddMcpInspector("inspector", options =>
{
options.InspectorVersion = "0.15.0";
})
.WithBun();

using var app = appBuilder.Build();

// Assert
var appModel = app.Services.GetRequiredService<DistributedApplicationModel>();
var inspectorResource = Assert.Single(appModel.Resources.OfType<McpInspectorResource>());

var args = await GetArgsAsync(inspectorResource);

Assert.Equal(new[] { "@modelcontextprotocol/inspector@0.15.0" }, args);
}

[Fact]
Expand All @@ -620,12 +657,9 @@ public async Task DefaultNpxUsesCorrectArguments()
var appModel = app.Services.GetRequiredService<DistributedApplicationModel>();
var inspectorResource = Assert.Single(appModel.Resources.OfType<McpInspectorResource>());

var args = await inspectorResource.GetArgumentValuesAsync();
var argsList = args.ToList();
var args = await GetArgsAsync(inspectorResource);

// For npm/npx, the first arg should be "-y"
Assert.Equal("-y", argsList[0]);
Assert.Equal("@modelcontextprotocol/inspector@0.15.0", argsList[1]);
Assert.Equal(new[] { "-y", "@modelcontextprotocol/inspector@0.15.0" }, args);
}

[Fact]
Expand All @@ -648,9 +682,20 @@ public async Task AddMcpInspectorWithOptionsRespectsInspectorVersion()
var appModel = app.Services.GetRequiredService<DistributedApplicationModel>();
var inspectorResource = Assert.Single(appModel.Resources.OfType<McpInspectorResource>());

var args = await inspectorResource.GetArgumentValuesAsync();
var argsList = args.ToList();
var args = await GetArgsAsync(inspectorResource);

Assert.Equal("@modelcontextprotocol/inspector@1.2.3", args[1]);
}

private static async Task<IReadOnlyList<string>> GetArgsAsync(McpInspectorResource resource)
{
CommandLineArgsCallbackContext context = new([]);

foreach (var annotation in resource.Annotations.OfType<CommandLineArgsCallbackAnnotation>())
{
await annotation.Callback(context);
}

Assert.Equal("@modelcontextprotocol/inspector@1.2.3", argsList[1]);
return context.Args.Select(arg => arg?.ToString() ?? string.Empty).ToArray();
}
}
Loading