-
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 3 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 |
---|---|---|
@@ -1,4 +1,5 @@ | ||
using Aspire.Hosting.ApplicationModel; | ||
using Microsoft.Extensions.DependencyInjection; | ||
|
||
namespace Aspire.Hosting; | ||
|
||
|
@@ -15,16 +16,17 @@ 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> | ||
public static IResourceBuilder<McpInspectorResource> AddMcpInspector(this IDistributedApplicationBuilder builder, [ResourceName] string name, int clientPort = 6274, int serverPort = 6277, string inspectorVersion = McpInspectorResource.InspectorVersion) | ||
/// <param name="proxyToken">The parameter used to provide the proxy authentication token for the MCP Inspector resource. If <see langword="null"/> a random token will be generated.</param> | ||
public static IResourceBuilder<McpInspectorResource> AddMcpInspector(this IDistributedApplicationBuilder builder, [ResourceName] string name, int clientPort = 6274, int serverPort = 6277, string inspectorVersion = McpInspectorResource.InspectorVersion, IResourceBuilder<ParameterResource>? proxyToken = null) | ||
timheuer marked this conversation as resolved.
Show resolved
Hide resolved
|
||
{ | ||
var proxyTokenParameter = proxyToken?.Resource ?? ParameterResourceBuilderExtensions.CreateDefaultPasswordParameter(builder, $"{name}-proxyToken"); | ||
|
||
var resource = builder.AddResource(new McpInspectorResource(name)) | ||
.WithArgs(["-y", $"@modelcontextprotocol/inspector@{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") | ||
.WithHttpHealthCheck("/", endpointName: McpInspectorResource.ClientEndpointName) | ||
.WithHttpHealthCheck("/config", endpointName: McpInspectorResource.ServerProxyEndpointName) | ||
.WithUrlForEndpoint(McpInspectorResource.ClientEndpointName, annotation => | ||
{ | ||
annotation.DisplayText = "Client"; | ||
|
@@ -34,8 +36,46 @@ 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) => | ||
{ | ||
if (@event.Resource is not McpInspectorResource inspectorResource) | ||
|
@@ -80,6 +120,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 +131,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; | ||
|
Uh oh!
There was an error while loading. Please reload this page.