-
Notifications
You must be signed in to change notification settings - Fork 110
Support auth token on MCP Inspector #780
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from 5 commits
249f9ae
8a3ccd0
8a64990
49b2271
9491d75
b723015
4d9bce3
982b02f
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,30 @@ | ||
using Aspire.Hosting.ApplicationModel; | ||
|
||
namespace Aspire.Hosting; | ||
|
||
/// <summary> | ||
/// Options for configuring the MCP Inspector resource. | ||
/// </summary> | ||
public class McpInspectorOptions | ||
{ | ||
/// <summary> | ||
/// Gets or sets the port for the client application. Defaults to "6274". | ||
/// </summary> | ||
public int ClientPort { get; set; } = 6274; | ||
|
||
/// <summary> | ||
/// Gets or sets the port for the server proxy application. Defaults to "6277". | ||
/// </summary> | ||
public int ServerPort { get; set; } = 6277; | ||
|
||
/// <summary> | ||
/// Gets or sets the version of the Inspector app to use. Defaults to <see cref="McpInspectorResource.InspectorVersion"/>. | ||
/// </summary> | ||
public string InspectorVersion { get; set; } = McpInspectorResource.InspectorVersion; | ||
|
||
/// <summary> | ||
/// Gets or sets the parameter used to provide the proxy authentication token for the MCP Inspector resource. | ||
/// If <see langword="null"/> a random token will be generated. | ||
/// </summary> | ||
public IResourceBuilder<ParameterResource>? ProxyToken { get; set; } | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,4 +1,5 @@ | ||
using Aspire.Hosting.ApplicationModel; | ||
using Microsoft.Extensions.DependencyInjection; | ||
|
||
namespace Aspire.Hosting; | ||
|
||
|
@@ -15,16 +16,39 @@ public static class McpInspectorResourceBuilderExtensions | |
/// <param name="clientPort">The port for the client application. Defaults to 6274.</param> | ||
/// <param name="serverPort">The port for the server proxy application. Defaults to 6277.</param> | ||
/// <param name="inspectorVersion">The version of the Inspector app to use</param> | ||
[Obsolete("Use the overload with McpInspectorOptions instead. This overload will be removed in the next version.")] | ||
public static IResourceBuilder<McpInspectorResource> AddMcpInspector(this IDistributedApplicationBuilder builder, [ResourceName] string name, int clientPort = 6274, int serverPort = 6277, string inspectorVersion = McpInspectorResource.InspectorVersion) | ||
{ | ||
ArgumentNullException.ThrowIfNull(builder); | ||
|
||
return AddMcpInspector(builder, name, options => | ||
{ | ||
options.ClientPort = clientPort; | ||
options.ServerPort = serverPort; | ||
options.InspectorVersion = inspectorVersion; | ||
}); | ||
} | ||
|
||
/// <summary> | ||
/// Adds a MCP Inspector container resource to the <see cref="IDistributedApplicationBuilder"/> using an options object. | ||
/// </summary> | ||
/// <param name="builder">The <see cref="IDistributedApplicationBuilder"/> to which the MCP Inspector resource will be added.</param> | ||
/// <param name="name">The name of the MCP Inspector container resource.</param> | ||
/// <param name="options">The <see cref="McpInspectorOptions"/> to configure the MCP Inspector resource.</param> | ||
/// <returns>A reference to the <see cref="IResourceBuilder{McpInspectorResource}"/> for further configuration.</returns> | ||
public static IResourceBuilder<McpInspectorResource> AddMcpInspector(this IDistributedApplicationBuilder builder, [ResourceName] string name, McpInspectorOptions options) | ||
{ | ||
ArgumentNullException.ThrowIfNull(builder); | ||
ArgumentNullException.ThrowIfNull(options); | ||
|
||
var proxyTokenParameter = options.ProxyToken?.Resource ?? ParameterResourceBuilderExtensions.CreateDefaultPasswordParameter(builder, $"{name}-proxyToken"); | ||
|
||
var resource = builder.AddResource(new McpInspectorResource(name)) | ||
.WithArgs(["-y", $"@modelcontextprotocol/inspector@{inspectorVersion}"]) | ||
.WithArgs(["-y", $"@modelcontextprotocol/inspector@{options.InspectorVersion}"]) | ||
.ExcludeFromManifest() | ||
.WithHttpEndpoint(isProxied: false, port: clientPort, env: "CLIENT_PORT", name: McpInspectorResource.ClientEndpointName) | ||
.WithHttpEndpoint(isProxied: false, port: serverPort, env: "SERVER_PORT", name: McpInspectorResource.ServerProxyEndpointName) | ||
.WithEnvironment("DANGEROUSLY_OMIT_AUTH", "true") | ||
.WithHttpEndpoint(isProxied: false, port: options.ClientPort, env: "CLIENT_PORT", name: McpInspectorResource.ClientEndpointName) | ||
.WithHttpEndpoint(isProxied: false, port: options.ServerPort, env: "SERVER_PORT", name: McpInspectorResource.ServerProxyEndpointName) | ||
.WithHttpHealthCheck("/", endpointName: McpInspectorResource.ClientEndpointName) | ||
.WithHttpHealthCheck("/config", endpointName: McpInspectorResource.ServerProxyEndpointName) | ||
.WithUrlForEndpoint(McpInspectorResource.ClientEndpointName, annotation => | ||
{ | ||
annotation.DisplayText = "Client"; | ||
|
@@ -34,7 +58,45 @@ public static IResourceBuilder<McpInspectorResource> AddMcpInspector(this IDistr | |
{ | ||
annotation.DisplayText = "Server Proxy"; | ||
annotation.DisplayOrder = 1; | ||
annotation.DisplayLocation = UrlDisplayLocation.DetailsOnly; | ||
}) | ||
.WithUrls(async context => | ||
{ | ||
var token = await proxyTokenParameter.GetValueAsync(CancellationToken.None); | ||
|
||
foreach (var url in context.Urls) | ||
{ | ||
if (url.Endpoint is not null) | ||
{ | ||
var uriBuilder = new UriBuilder(url.Url); | ||
uriBuilder.Query = $"MCP_PROXY_AUTH_TOKEN={Uri.EscapeDataString(token!)}"; | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Hmmmmm, interesting. This might not be the best practice right? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. It definitely feels dirty. No link and defer to getting it (the link) rom console log? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. However, this is the URI that is emitted in the console...so it's not like this surfaces a secret of sorts that isn't surfaced already. |
||
url.Url = uriBuilder.ToString(); | ||
} | ||
} | ||
}); | ||
|
||
resource.Resource.ProxyTokenParameter = proxyTokenParameter; | ||
|
||
// Add authenticated health check for server proxy /config endpoint | ||
var healthCheckKey = $"{name}_proxy_config_check"; | ||
builder.Services.AddHealthChecks().AddUrlGroup(options => | ||
{ | ||
var serverProxyEndpoint = resource.GetEndpoint(McpInspectorResource.ServerProxyEndpointName); | ||
var uri = serverProxyEndpoint.Url; | ||
if (uri is null) | ||
{ | ||
throw new DistributedApplicationException("The MCP Inspector 'server-proxy' endpoint URL is not set. Ensure that the resource has been allocated before the health check is executed."); | ||
} | ||
|
||
var healthCheckUri = new Uri(new Uri(uri), "/config"); | ||
options.AddUri(healthCheckUri, async setup => | ||
timheuer marked this conversation as resolved.
Show resolved
Hide resolved
|
||
{ | ||
var token = await proxyTokenParameter.GetValueAsync(CancellationToken.None); | ||
setup.AddCustomHeader("X-MCP-Proxy-Auth", $"Bearer {token}"); | ||
}); | ||
}, healthCheckKey); | ||
|
||
resource.WithHealthCheck(healthCheckKey); | ||
|
||
builder.Eventing.Subscribe<BeforeResourceStartedEvent>(resource.Resource, async (@event, ct) => | ||
{ | ||
|
@@ -80,6 +142,7 @@ public static IResourceBuilder<McpInspectorResource> AddMcpInspector(this IDistr | |
ctx.EnvironmentVariables["MCP_PROXY_FULL_ADDRESS"] = serverProxyEndpoint.Url; | ||
ctx.EnvironmentVariables["CLIENT_PORT"] = clientEndpoint.TargetPort?.ToString() ?? throw new InvalidOperationException("The MCP Inspector 'client' endpoint must have a target port defined."); | ||
ctx.EnvironmentVariables["SERVER_PORT"] = serverProxyEndpoint.TargetPort?.ToString() ?? throw new InvalidOperationException("The MCP Inspector 'server-proxy' endpoint must have a target port defined."); | ||
ctx.EnvironmentVariables["MCP_PROXY_AUTH_TOKEN"] = proxyTokenParameter; | ||
}) | ||
.WithArgs(ctx => | ||
{ | ||
|
@@ -90,7 +153,6 @@ public static IResourceBuilder<McpInspectorResource> AddMcpInspector(this IDistr | |
throw new InvalidOperationException("No default MCP server has been configured for the MCP Inspector resource, yet servers have been provided."); | ||
} | ||
|
||
|
||
if (defaultMcpServer is null && inspectorResource.McpServers.Count == 0) | ||
{ | ||
return; | ||
|
@@ -103,6 +165,39 @@ public static IResourceBuilder<McpInspectorResource> AddMcpInspector(this IDistr | |
}); | ||
} | ||
|
||
/// <summary> | ||
/// Adds a MCP Inspector container resource to the <see cref="IDistributedApplicationBuilder"/> using a configuration delegate. | ||
/// </summary> | ||
/// <param name="builder">The <see cref="IDistributedApplicationBuilder"/> to which the MCP Inspector resource will be added.</param> | ||
/// <param name="name">The name of the MCP Inspector container resource.</param> | ||
/// <param name="configureOptions">A delegate to configure the <see cref="McpInspectorOptions"/>.</param> | ||
/// <returns>A reference to the <see cref="IResourceBuilder{McpInspectorResource}"/> for further configuration.</returns> | ||
public static IResourceBuilder<McpInspectorResource> AddMcpInspector(this IDistributedApplicationBuilder builder, [ResourceName] string name, Action<McpInspectorOptions> configureOptions) | ||
{ | ||
ArgumentNullException.ThrowIfNull(builder); | ||
ArgumentNullException.ThrowIfNull(configureOptions); | ||
|
||
var options = new McpInspectorOptions(); | ||
configureOptions(options); | ||
|
||
return builder.AddMcpInspector(name, options); | ||
} | ||
|
||
/// <summary> | ||
/// Adds a MCP Inspector container resource to the <see cref="IDistributedApplicationBuilder"/> using a configuration delegate. | ||
/// </summary> | ||
/// <param name="builder">The <see cref="IDistributedApplicationBuilder"/> to which the MCP Inspector resource will be added.</param> | ||
/// <param name="name">The name of the MCP Inspector container resource.</param> | ||
/// <returns>A reference to the <see cref="IResourceBuilder{McpInspectorResource}"/> for further configuration.</returns> | ||
public static IResourceBuilder<McpInspectorResource> AddMcpInspector(this IDistributedApplicationBuilder builder, [ResourceName] string name) | ||
{ | ||
ArgumentNullException.ThrowIfNull(builder); | ||
|
||
var options = new McpInspectorOptions(); | ||
|
||
return builder.AddMcpInspector(name, options); | ||
} | ||
|
||
/// <summary> | ||
/// Configures the MCP Inspector resource to use a specified MCP server resource that uses SSE as the transport type. | ||
/// </summary> | ||
|
Uh oh!
There was an error while loading. Please reload this page.