Skip to content

Add --dashboard-url and --api-key options to aspire agent mcp and aspire otel commands#15595

Open
JamesNK wants to merge 7 commits intomainfrom
cli-dashboard-url-apikey
Open

Add --dashboard-url and --api-key options to aspire agent mcp and aspire otel commands#15595
JamesNK wants to merge 7 commits intomainfrom
cli-dashboard-url-apikey

Conversation

@JamesNK
Copy link
Member

@JamesNK JamesNK commented Mar 26, 2026

Description

Add --dashboard-url and --api-key options to the aspire agent mcp and aspire otel logs/spans/traces commands, enabling direct connection to a standalone Aspire Dashboard without requiring an AppHost.

Motivation: Users running a standalone Aspire Dashboard (not managed by an AppHost) currently have no way to query telemetry data through the CLI or MCP tools. This change allows pointing the CLI directly at a dashboard URL.

Key changes:

  • IDashboardInfoProvider abstraction — New interface with two implementations: BackchannelDashboardInfoProvider (existing AppHost flow) and StaticDashboardInfoProvider (CLI-provided URL/key). MCP tools now depend on this abstraction instead of IAuxiliaryBackchannelMonitor directly.
  • --dashboard-url and --api-key on otel commandsaspire otel logs, aspire otel spans, and aspire otel traces accept an optional dashboard URL (mutually exclusive with --apphost) and optional API key for secured dashboards.
  • Dashboard-only MCP modeaspire agent mcp --dashboard-url starts the MCP server exposing only the three telemetry tools (list_structured_logs, list_traces, list_trace_structured_logs), filtering out AppHost-specific tools.
  • Smart error handling — HTTP 401 suggests using --api-key, HTTP 404 probes the base URL to distinguish "API not enabled" vs "wrong URL", and connection failures report the dashboard as unreachable.
  • Tests — New tests for all three otel commands and MCP dashboard-only mode. Refactored AgentMcpCommandTests to remove fields and use a reusable helper method.

Validation: All 1728 CLI tests pass (0 failures, 5 platform-specific skips).

Fixes # (issue)

Checklist

  • Is this feature complete?
    • Yes. Ready to ship.
    • No. Follow-up changes expected.
  • Are you including unit tests for the changes and scenario tests if relevant?
    • Yes
    • No
  • Did you add public API?
    • Yes
      • If yes, did you have an API Review for it?
        • Yes
        • No
      • Did you add <remarks /> and <code /> elements on your triple slash comments?
        • Yes
        • No
    • No
  • Does the change make any security assumptions or guarantees?
    • Yes
      • If yes, have you done a threat model and had a security review?
        • Yes
        • No
    • No
  • Does the change require an update in our Aspire docs?

Copilot AI review requested due to automatic review settings March 26, 2026 03:28
@github-actions
Copy link
Contributor

github-actions bot commented Mar 26, 2026

🚀 Dogfood this PR with:

⚠️ WARNING: Do not do this without first carefully reviewing the code of this PR to satisfy yourself it is safe.

curl -fsSL https://raw.githubusercontent.com/microsoft/aspire/main/eng/scripts/get-aspire-cli-pr.sh | bash -s -- 15595

Or

  • Run remotely in PowerShell:
iex "& { $(irm https://raw.githubusercontent.com/microsoft/aspire/main/eng/scripts/get-aspire-cli-pr.ps1) } 15595"

Copy link
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Adds support for connecting the CLI telemetry (“otel”) and MCP agent directly to a standalone Aspire Dashboard via --dashboard-url (and optional --api-key), instead of requiring AppHost discovery/backchannel.

Changes:

  • Introduces IDashboardInfoProvider and updates MCP telemetry tools to use it (backchannel vs. static URL/key).
  • Adds --dashboard-url / --api-key options to aspire otel logs|spans|traces and adds smarter dashboard-specific error messaging.
  • Adds “dashboard-only” MCP mode that exposes only telemetry MCP tools when started with --dashboard-url, plus new/updated tests and localized strings.

Reviewed changes

Copilot reviewed 29 out of 30 changed files in this pull request and generated 7 comments.

Show a summary per file
File Description
tests/Aspire.Cli.Tests/Mcp/ListTracesToolTests.cs Updates tool construction to use IDashboardInfoProvider.
tests/Aspire.Cli.Tests/Mcp/ListStructuredLogsToolTests.cs Updates tool construction to use IDashboardInfoProvider.
tests/Aspire.Cli.Tests/Commands/TelemetryTracesCommandTests.cs Adds tests for --dashboard-url behavior and 401 guidance.
tests/Aspire.Cli.Tests/Commands/TelemetrySpansCommandTests.cs Adds tests for --dashboard-url behavior and 401 guidance.
tests/Aspire.Cli.Tests/Commands/TelemetryLogsCommandTests.cs Adds tests for direct dashboard mode, api-key header, 404 probing, and connection errors.
tests/Aspire.Cli.Tests/Commands/AgentMcpCommandTests.cs Refactors MCP test setup and adds dashboard-only mode tests.
src/Aspire.Cli/Resources/xlf/TelemetryCommandStrings.zh-Hant.xlf Adds localized entries for new options/error messages.
src/Aspire.Cli/Resources/xlf/TelemetryCommandStrings.zh-Hans.xlf Adds localized entries for new options/error messages.
src/Aspire.Cli/Resources/xlf/TelemetryCommandStrings.tr.xlf Adds localized entries for new options/error messages.
src/Aspire.Cli/Resources/xlf/TelemetryCommandStrings.ru.xlf Adds localized entries for new options/error messages.
src/Aspire.Cli/Resources/xlf/TelemetryCommandStrings.pt-BR.xlf Adds localized entries for new options/error messages.
src/Aspire.Cli/Resources/xlf/TelemetryCommandStrings.pl.xlf Adds localized entries for new options/error messages.
src/Aspire.Cli/Resources/xlf/TelemetryCommandStrings.ko.xlf Adds localized entries for new options/error messages.
src/Aspire.Cli/Resources/xlf/TelemetryCommandStrings.ja.xlf Adds localized entries for new options/error messages.
src/Aspire.Cli/Resources/xlf/TelemetryCommandStrings.it.xlf Adds localized entries for new options/error messages.
src/Aspire.Cli/Resources/xlf/TelemetryCommandStrings.fr.xlf Adds localized entries for new options/error messages.
src/Aspire.Cli/Resources/xlf/TelemetryCommandStrings.es.xlf Adds localized entries for new options/error messages.
src/Aspire.Cli/Resources/xlf/TelemetryCommandStrings.de.xlf Adds localized entries for new options/error messages.
src/Aspire.Cli/Resources/xlf/TelemetryCommandStrings.cs.xlf Adds localized entries for new options/error messages.
src/Aspire.Cli/Resources/TelemetryCommandStrings.resx Adds new resource strings for options and dashboard-specific errors.
src/Aspire.Cli/Resources/TelemetryCommandStrings.Designer.cs Regenerates strongly-typed accessors for new resource strings.
src/Aspire.Cli/Mcp/Tools/ListTracesTool.cs Switches to IDashboardInfoProvider and uses shared dashboard error message helper.
src/Aspire.Cli/Mcp/Tools/ListTraceStructuredLogsTool.cs Switches to IDashboardInfoProvider and uses shared dashboard error message helper.
src/Aspire.Cli/Mcp/Tools/ListStructuredLogsTool.cs Switches to IDashboardInfoProvider and uses shared dashboard error message helper.
src/Aspire.Cli/Mcp/Tools/IDashboardInfoProvider.cs Adds provider abstraction + backchannel/static implementations.
src/Aspire.Cli/Commands/TelemetryTracesCommand.cs Adds --dashboard-url/--api-key support and dashboard-mode error messaging.
src/Aspire.Cli/Commands/TelemetrySpansCommand.cs Adds --dashboard-url/--api-key support and dashboard-mode error messaging.
src/Aspire.Cli/Commands/TelemetryLogsCommand.cs Adds --dashboard-url/--api-key support and dashboard-mode error messaging.
src/Aspire.Cli/Commands/TelemetryCommandHelpers.cs Adds option factories, validates exclusivity, adds direct dashboard mode + richer error messages.
src/Aspire.Cli/Commands/AgentMcpCommand.cs Adds dashboard-only MCP mode with --dashboard-url/--api-key and tool filtering.
Files not reviewed (1)
  • src/Aspire.Cli/Resources/TelemetryCommandStrings.Designer.cs: Language not supported

Comment on lines +168 to +176
if (dashboardUrl is not null)
{
var errorMessage = await TelemetryCommandHelpers.GetDashboardApiErrorMessageAsync(ex, baseUrl, _httpClientFactory, _logger, cancellationToken);
_interactionService.DisplayError(errorMessage);
}
else
{
_interactionService.DisplayError(string.Format(CultureInfo.CurrentCulture, TelemetryCommandStrings.FailedToFetchTelemetry, ex.Message));
}
Copy link

Copilot AI Mar 26, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In --dashboard-url mode a 404 can indicate "wrong URL" or "telemetry API not enabled", but FetchSingleTraceAsync still special-cases response.StatusCode == NotFound earlier and returns "Trace not found" before this new dashboard-url error handling runs. Consider gating the TraceNotFound early-return to AppHost mode only, and let 404s in --dashboard-url mode flow through GetDashboardApiErrorMessageAsync.

Copilot uses AI. Check for mistakes.
Comment on lines +221 to +228
using var probeClient = httpClientFactory.CreateClient();
var probeResponse = await probeClient.GetAsync(dashboardBaseUrl, cancellationToken).ConfigureAwait(false);

if (probeResponse.IsSuccessStatusCode)
{
// Dashboard is reachable but the API endpoint returned 404 — API not enabled
return string.Format(CultureInfo.CurrentCulture, TelemetryCommandStrings.DashboardApiNotEnabled, dashboardBaseUrl);
}
Copy link

Copilot AI Mar 26, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

GetDashboardApiErrorMessageAsync probes the base URL with probeClient.GetAsync(...) but never disposes the HttpResponseMessage. Even though this is an error path, not disposing responses can hold onto connections longer than necessary. Wrap the probe response in a using (or await using) to ensure it is disposed promptly.

Copilot uses AI. Check for mistakes.
@JamesNK JamesNK force-pushed the cli-dashboard-url-apikey branch 4 times, most recently from 76f22b6 to 11c3eac Compare March 26, 2026 04:15
JamesNK added 3 commits March 26, 2026 12:34
…ire otel commands

Add support for connecting directly to a standalone Aspire Dashboard
without an AppHost via --dashboard-url and optional --api-key options
on the 'aspire agent mcp' and 'aspire otel logs/spans/traces' commands.

- Introduce IDashboardInfoProvider abstraction (BackchannelDashboardInfoProvider
  and StaticDashboardInfoProvider) to decouple MCP tools from the backchannel
- Add --dashboard-url and --api-key options to TelemetryLogs/Spans/TracesCommand
- Add dashboard-only mode to AgentMcpCommand (exposes only telemetry tools)
- Add smart error handling: 401 suggests --api-key, 404 checks if API is
  enabled, connection refused reports unreachable dashboard
- Add tests for all new functionality
…cate error handling

- Add IsDirectConnection to IDashboardInfoProvider so MCP tools only show
  dashboard-url-specific error messages (e.g. --api-key hint) when actually
  using --dashboard-url, not in AppHost backchannel mode
- Validate --dashboard-url accepts only http/https schemes in both
  AgentMcpCommand and TelemetryCommandHelpers
- Extract duplicated if/else error handling from 5 catch blocks in otel
  commands into TelemetryCommandHelpers.FormatTelemetryErrorMessageAsync
@davidfowl
Copy link
Contributor

How does the agent get the API key from the CLI or is it expected that the user gives it after the agent sees the 401? Can you share a video of the flow?

@JamesNK JamesNK force-pushed the cli-dashboard-url-apikey branch from 8aa7b47 to c502d9a Compare March 26, 2026 04:35
@JamesNK
Copy link
Member Author

JamesNK commented Mar 26, 2026

How does the agent get the API key from the CLI or is it expected that the user gives it after the agent sees the 401? Can you share a video of the flow?

If the agent started the dashboard then it would have passed an API key in, so would have it that way.
If the user started the dashboard then they would have to give the API key to the agent, along with the dashboard URL.
Or the dashboard may have been started without authentication and so it's not a concern.

This has good error handling so if auth fails then there will be clear feedback to the agent/user that an API key is needed and how to give it to the command.

@github-actions
Copy link
Contributor

Re-running the failed jobs in the CI workflow for this pull request because 1 job was identified as retry-safe transient failures in the CI run attempt.
GitHub was asked to rerun all failed jobs for that attempt, and the rerun is being tracked in the rerun attempt.
The job links below point to the failed attempt jobs that matched the retry-safe transient failure rules.

JamesNK added 3 commits March 26, 2026 18:40
- ExportCommand supports --dashboard-url and --api-key for direct dashboard access
- Refactor GetDashboardApiAsync to return DashboardApiResult record with Connection
- Add requireDashboard parameter to GetDashboardApiAsync for optional dashboard
- Update ExportCommand to gracefully degrade when dashboard is unavailable
- Add DisplayedMessages tracking to TestInteractionService
- Add ExportCommand_DashboardUnavailable test using TestInteractionService
- Simplify CreateExportTestServices: remove outputWriter param, add interactionService
Verifies export command works with --dashboard-url and --api-key options,
bypassing AppHost backchannel discovery and fetching telemetry data
directly from the dashboard API.
@github-actions
Copy link
Contributor

🎬 CLI E2E Test Recordings — 52 recordings uploaded (commit 60ffd2a)

View recordings
Test Recording
AddPackageInteractiveWhileAppHostRunningDetached ▶️ View Recording
AddPackageWhileAppHostRunningDetached ▶️ View Recording
AgentCommands_AllHelpOutputs_AreCorrect ▶️ View Recording
AgentInitCommand_DefaultSelection_InstallsSkillOnly ▶️ View Recording
AgentInitCommand_MigratesDeprecatedConfig ▶️ View Recording
AspireAddPackageVersionToDirectoryPackagesProps ▶️ View Recording
AspireUpdateRemovesAppHostPackageVersionFromDirectoryPackagesProps ▶️ View Recording
Banner_DisplayedOnFirstRun ▶️ View Recording
Banner_DisplayedWithExplicitFlag ▶️ View Recording
Banner_NotDisplayedWithNoLogoFlag ▶️ View Recording
CertificatesClean_RemovesCertificates ▶️ View Recording
CertificatesTrust_WithNoCert_CreatesAndTrustsCertificate ▶️ View Recording
CertificatesTrust_WithUntrustedCert_TrustsCertificate ▶️ View Recording
ConfigSetGet_CreatesNestedJsonFormat ▶️ View Recording
CreateAndRunAspireStarterProject ▶️ View Recording
CreateAndRunAspireStarterProjectWithBundle ▶️ View Recording
CreateAndRunEmptyAppHostProject ▶️ View Recording
CreateAndRunJavaEmptyAppHostProject ▶️ View Recording
CreateAndRunJsReactProject ▶️ View Recording
CreateAndRunPythonReactProject ▶️ View Recording
CreateAndRunTypeScriptEmptyAppHostProject ▶️ View Recording
CreateAndRunTypeScriptStarterProject ▶️ View Recording
CreateJavaAppHostWithViteApp ▶️ View Recording
CreateStartAndStopAspireProject ▶️ View Recording
CreateTypeScriptAppHostWithViteApp ▶️ View Recording
DescribeCommandResolvesReplicaNames ▶️ View Recording
DescribeCommandShowsRunningResources ▶️ View Recording
DetachFormatJsonProducesValidJson ▶️ View Recording
DoctorCommand_DetectsDeprecatedAgentConfig ▶️ View Recording
DoctorCommand_WithSslCertDir_ShowsTrusted ▶️ View Recording
DoctorCommand_WithoutSslCertDir_ShowsPartiallyTrusted ▶️ View Recording
GlobalMigration_HandlesCommentsAndTrailingCommas ▶️ View Recording
GlobalMigration_HandlesMalformedLegacyJson ▶️ View Recording
GlobalMigration_PreservesAllValueTypes ▶️ View Recording
GlobalMigration_SkipsWhenNewConfigExists ▶️ View Recording
GlobalSettings_MigratedFromLegacyFormat ▶️ View Recording
InvalidAppHostPathWithComments_IsHealedOnRun ▶️ View Recording
LogsCommandShowsResourceLogs ▶️ View Recording
PsCommandListsRunningAppHost ▶️ View Recording
PsFormatJsonOutputsOnlyJsonToStdout ▶️ View Recording
PublishWithDockerComposeServiceCallbackSucceeds ▶️ View Recording
RestoreGeneratesSdkFiles ▶️ View Recording
RunWithMissingAwaitShowsHelpfulError ▶️ View Recording
SecretCrudOnDotNetAppHost ▶️ View Recording
SecretCrudOnTypeScriptAppHost ▶️ View Recording
StagingChannel_ConfigureAndVerifySettings_ThenSwitchChannels ▶️ View Recording
StopAllAppHostsFromAppHostDirectory ▶️ View Recording
StopAllAppHostsFromUnrelatedDirectory ▶️ View Recording
StopNonInteractiveMultipleAppHostsShowsError ▶️ View Recording
StopNonInteractiveSingleAppHost ▶️ View Recording
StopWithNoRunningAppHostExitsSuccessfully ▶️ View Recording
TypeScriptAppHostWithProjectReferenceIntegration ▶️ View Recording

📹 Recordings uploaded automatically from CI run #23591840590

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants