File: Mcp\ListAppHostsTool.cs
Web Access
Project: src\src\Aspire.Cli\Aspire.Cli.Tool.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 System.Globalization;
using System.Text;
using System.Text.Json;
using System.Text.Json.Serialization;
using Aspire.Cli.Backchannel;
using ModelContextProtocol.Protocol;
 
namespace Aspire.Cli.Mcp;
 
/// <summary>
/// Represents information about a detected AppHost.
/// </summary>
internal sealed record AppHostListInfo(string AppHostPath, int AppHostPid, int? CliPid);
 
[JsonSerializable(typeof(List<AppHostListInfo>))]
[JsonSourceGenerationOptions(WriteIndented = true, PropertyNamingPolicy = JsonKnownNamingPolicy.CamelCase)]
internal sealed partial class AppHostListInfoSerializerContext : JsonSerializerContext
{
}
 
/// <summary>
/// MCP tool for listing all AppHost connections detected by the Aspire MCP server.
/// </summary>
internal sealed class ListAppHostsTool(IAuxiliaryBackchannelMonitor auxiliaryBackchannelMonitor, CliExecutionContext executionContext) : CliMcpTool
{
    public override string Name => "list_apphosts";
 
    public override string Description => "Lists all AppHost connections currently detected by the Aspire MCP server, showing which AppHosts are within the working directory scope and which are outside.";
 
    public override JsonElement GetInputSchema()
    {
        return JsonDocument.Parse("{ \"type\": \"object\", \"properties\": {} }").RootElement;
    }
 
    public override ValueTask<CallToolResult> CallToolAsync(ModelContextProtocol.Client.McpClient mcpClient, IReadOnlyDictionary<string, JsonElement>? arguments, CancellationToken cancellationToken)
    {
        // This tool does not use the MCP client as it operates locally
        _ = mcpClient;
        _ = arguments;
        _ = cancellationToken;
 
        var workingDirectory = executionContext.WorkingDirectory.FullName;
 
        var connections = auxiliaryBackchannelMonitor.Connections.Values.ToList();
 
        var inScopeAppHosts = connections
            .Where(c => c.IsInScope)
            .Select(c => new AppHostListInfo(
                c.AppHostInfo?.AppHostPath ?? "Unknown",
                c.AppHostInfo?.ProcessId ?? 0,
                c.AppHostInfo?.CliProcessId))
            .ToList();
 
        var outOfScopeAppHosts = connections
            .Where(c => !c.IsInScope)
            .Select(c => new AppHostListInfo(
                c.AppHostInfo?.AppHostPath ?? "Unknown",
                c.AppHostInfo?.ProcessId ?? 0,
                c.AppHostInfo?.CliProcessId))
            .ToList();
 
        var responseBuilder = new StringBuilder();
        responseBuilder.AppendLine("The following is a list of apphosts which are currently running on this machine which can be detected by the Aspire MCP server:");
        responseBuilder.AppendLine();
        responseBuilder.AppendLine(CultureInfo.InvariantCulture, $"App hosts within scope of working directory: {workingDirectory}");
        responseBuilder.AppendLine();
 
        var inScopeJson = JsonSerializer.Serialize(inScopeAppHosts, AppHostListInfoSerializerContext.Default.ListAppHostListInfo);
        responseBuilder.AppendLine(inScopeJson);
 
        responseBuilder.AppendLine();
        responseBuilder.AppendLine("App hosts outside scope of working directory:");
        responseBuilder.AppendLine();
 
        var outOfScopeJson = JsonSerializer.Serialize(outOfScopeAppHosts, AppHostListInfoSerializerContext.Default.ListAppHostListInfo);
        responseBuilder.AppendLine(outOfScopeJson);
 
        return ValueTask.FromResult(new CallToolResult
        {
            Content = [new TextContentBlock { Text = responseBuilder.ToString() }]
        });
    }
}