| File: Mcp\Tools\IDashboardInfoProvider.cs | Web Access |
| Project: src\src\Aspire.Cli\Aspire.Cli.csproj (aspire) |
// Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. using Aspire.Cli.Backchannel; using Aspire.Cli.Commands; using Aspire.Dashboard.Utils; using Microsoft.Extensions.Logging; using ModelContextProtocol; namespace Aspire.Cli.Mcp.Tools; /// <summary> /// Provides dashboard connection info (API token, base URL, dashboard UI URL) for telemetry access. /// </summary> internal interface IDashboardInfoProvider { /// <summary> /// Whether the dashboard URL was provided directly (e.g. via --dashboard-url) rather than discovered through an AppHost. /// </summary> bool IsDirectConnection { get; } /// <summary> /// Gets dashboard connection info for telemetry API access. /// </summary> /// <returns>A tuple of (apiToken, apiBaseUrl, dashboardBaseUrl). apiToken may be empty for unsecured dashboards.</returns> Task<(string apiToken, string apiBaseUrl, string? dashboardBaseUrl)> GetDashboardInfoAsync(CancellationToken cancellationToken); } /// <summary> /// Gets dashboard info from the AppHost backchannel (default behavior). /// </summary> internal sealed class BackchannelDashboardInfoProvider( IAuxiliaryBackchannelMonitor auxiliaryBackchannelMonitor, ILogger logger) : IDashboardInfoProvider { public bool IsDirectConnection => false; public Task<(string apiToken, string apiBaseUrl, string? dashboardBaseUrl)> GetDashboardInfoAsync(CancellationToken cancellationToken) { return McpToolHelpers.GetDashboardInfoAsync(auxiliaryBackchannelMonitor, logger, cancellationToken); } } /// <summary> /// Returns dashboard info from statically-provided URL and optional API key (for standalone dashboards). /// </summary> internal sealed class StaticDashboardInfoProvider( string dashboardUrl, string? apiKey, IHttpClientFactory? httpClientFactory = null, ILogger? logger = null) : IDashboardInfoProvider { public bool IsDirectConnection => true; public async Task<(string apiToken, string apiBaseUrl, string? dashboardBaseUrl)> GetDashboardInfoAsync(CancellationToken cancellationToken) { var loginToken = DashboardUrls.ExtractDashboardLoginToken(dashboardUrl); var apiBaseUrl = DashboardUrls.NormalizeDashboardRequestUrl(dashboardUrl, stripLoginPath: true); if (apiBaseUrl is null) { throw new McpProtocolException( "The dashboard URL must be an absolute HTTP or HTTPS URL.", McpErrorCode.InvalidParams); } if (loginToken is not null) { apiBaseUrl = DashboardUrls.RemoveDashboardLoginToken(apiBaseUrl) ?? apiBaseUrl; } var apiToken = apiKey; if (apiToken is null && loginToken is not null) { if (httpClientFactory is null || logger is null) { throw new McpProtocolException( "The configured dashboard login token could not be exchanged for an API key.", McpErrorCode.InternalError); } var exchange = await TelemetryCommandHelpers.ExchangeLoginTokenForApiKeyAsync( httpClientFactory, apiBaseUrl, loginToken, logger, cancellationToken).ConfigureAwait(false); if (!exchange.Success) { throw new McpProtocolException( "The configured dashboard login token could not be exchanged for an API key.", McpErrorCode.InternalError); } apiToken = exchange.ApiKey; } var dashboardBaseUrl = McpToolHelpers.StripLoginPath(dashboardUrl); return (apiToken ?? string.Empty, apiBaseUrl, dashboardBaseUrl); } }