microsoft / microsoft/aspire

AuxiliaryBackchannelMonitor may silently disable itself when command selection loses a race with the first-run banner

Open
#18,959 1 comment 0 reactions 1 assignee Claimed by @mitchdenny View on GitHub
area-cli area-mcp bug triage:bot-seen
Dominant language
C#
Stars
6.3k
Forks
991
Avg merge
2d 15h
Merged PRs (30d)
196

Description

`AuxiliaryBackchannelMonitor` waits one second for the command to be selected, then treats "didn't get one" as "not `mcp start`, disable monitoring":

```csharp
// src/Aspire.Cli/Backchannel/AuxiliaryBackchannelMonitor.cs:229-238
using var timeout = new CancellationTokenSource(TimeSpan.FromSeconds(1));
using var combined = CancellationTokenSource.CreateLinkedTokenSource(stoppingToken, timeout.Token);

var command = await executionContext.CommandSelected.Task.WaitAsync(combined.Token).ConfigureAwait(false);

if (command is not McpStartCommand)
{
logger.LogDebug("Current command is not MCP start command. Auxiliary backchannel monitoring disabled.");
return;
}
```

On timeout the `await` throws, `command` is never assigned, and the service returns. Monitoring is off and the only trace is one debug-level line.

I have not reproduced it. I am filing it because I just fixed the identical pattern in `NuGetPackagePrefetcher` (#18958), where it was a real defect — there the timeout made the service do work it should have skipped, here it would make it skip work it should do.

### Why one second can lose

Command selection happens when the command's action runs (`BaseCommand.cs:61`). This `BackgroundService` starts earlier at `Program.cs:1061`, and the first-run banner plays between the two at `Program.cs:1093`.

`BannerService.DisplayBannerAsync` spends **1660 ms** in fixed `Task.Delay` calls — 80 + (5 x 40) + (7 x 70) + (8 x 50) + (14 x 35), using `BannerWelcomeText` = "Welcome to the" (14 chars), `s_letterPositions.Length` = 6, and `s_aspireLines[0].TrimEnd().Length` = 41. Against a 1000 ms timeout, the timeout loses every time. That arithmetic is not theoretical: it is what made the `ls`/`ps` opt-out in #18958 fail on a first run.

### What I did not check

Whether the banner renders at all for `aspire mcp start`. `DisplayFirstTimeUseNoticeIfNeededAsync` gates on `isFirstRun`, `noLogo`, `HasMachineReadableOutput(args)`, informational options and `hostEnvironment.SupportsInteractiveOutput`, and I did not work out whether `mcp start`'s arguments and stdio environment trip any of them. If they always do, this lead is empty.

No repro steps either — I have not run it.

### Suggested direction

The banner is only the trigger I can point at. The shape is the real problem: guessing the command after a fixed timeout is unsound whenever something slow can run between host start and the command's action, and anything added to that window later re-opens it.

#18958 drops the pattern by waiting for selection until shutdown and treating "never selected" as "the CLI is shutting down, do nothing". The same fits here, since a monitor that starts late beats one that silently never starts. Selecting the parsed command earlier in `Program` would close both call sites at once.

I can send a PR, but someone who can run `aspire mcp start` on a genuine first run should confirm the trigger is real first.

Contributor guide

Open the contributing guide

Assessment

This issue has not been assessed yet.

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.