Skip to content

Commit ccc566c

Browse files
[release/13.4] Filter resources with resource.excludeFromMcp from CLI MCP tools (#18150)
* Filter resources with resource.excludeFromMcp from CLI MCP tools Resources marked with the resource.excludeFromMcp property are now excluded from all MCP tool results: - ListResourcesTool filters them from resource listings - ListConsoleLogsTool and ExecuteResourceCommandTool reject requests targeting excluded resources - ListStructuredLogsTool, ListTracesTool, and ListTraceStructuredLogsTool filter out telemetry from excluded resources - McpResourceToolRefreshService skips excluded resources Added McpToolHelpers with IsExcludedFromMcp, CheckResourceExcludedAsync, GetExcludedResourceNamesAsync, and GetResourceNotAvailableMessage helpers. Includes comprehensive unit tests covering all filtering scenarios. * Add ListTraceStructuredLogsTool exclusion tests and E2E test - Add two tests for ListTraceStructuredLogsTool filtering: - FiltersExcludedResourceLogs: verifies excluded resource logs are removed from trace-scoped structured log results - ReturnsAllLogs_WhenNoResourcesExcluded: verifies no filtering when no resources are excluded - Add doesNotContainMarker parameter to CallAgentMcpToolAsync helper - Add AgentMcpExcludeFromMcpTests E2E test that verifies list_resources excludes resources marked with ExcludeFromMcp() * Use quoted markers in E2E test for precise JSON matching * Avoid redundant connection lookup in exclusion checks Add overloads of CheckResourceExcludedAsync and GetExcludedResourceNamesAsync that accept IAppHostAuxiliaryBackchannel directly. Update ListConsoleLogsTool and ExecuteResourceCommandTool to use the connection they already obtained, eliminating a redundant GetSelectedConnectionAsync + GetResourceSnapshotsAsync call per request. * Use StringComparers.ResourceName and extract CreateExcludedResult helper * Fix AgentMcpExcludeFromMcp E2E test: disable Redis cache The test was timing out because the Redis container's health check never passed in the Docker-in-Docker CI environment, causing webfrontend (which has WaitFor(cache)) to stay stuck in Waiting state. Redis is irrelevant to this test — it only verifies ExcludeFromMcp() filtering. Disabling Redis removes the container dependency. --------- Co-authored-by: James Newton-King <james@newtonking.com>
1 parent 163dd34 commit ccc566c

14 files changed

Lines changed: 902 additions & 10 deletions

src/Aspire.Cli/Commands/AgentMcpCommand.cs

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -110,9 +110,9 @@ protected override async Task<CommandResult> ExecuteAsync(ParseResult parseResul
110110
_dashboardOnlyMode = true;
111111
var staticProvider = new StaticDashboardInfoProvider(dashboardUrl, apiKey);
112112

113-
_knownTools[KnownMcpTools.ListStructuredLogs] = new ListStructuredLogsTool(staticProvider, _httpClientFactory, _loggerFactory.CreateLogger<ListStructuredLogsTool>());
114-
_knownTools[KnownMcpTools.ListTraces] = new ListTracesTool(staticProvider, _httpClientFactory, _loggerFactory.CreateLogger<ListTracesTool>());
115-
_knownTools[KnownMcpTools.ListTraceStructuredLogs] = new ListTraceStructuredLogsTool(staticProvider, _httpClientFactory, _loggerFactory.CreateLogger<ListTraceStructuredLogsTool>());
113+
_knownTools[KnownMcpTools.ListStructuredLogs] = new ListStructuredLogsTool(staticProvider, null, _httpClientFactory, _loggerFactory.CreateLogger<ListStructuredLogsTool>());
114+
_knownTools[KnownMcpTools.ListTraces] = new ListTracesTool(staticProvider, null, _httpClientFactory, _loggerFactory.CreateLogger<ListTracesTool>());
115+
_knownTools[KnownMcpTools.ListTraceStructuredLogs] = new ListTraceStructuredLogsTool(staticProvider, null, _httpClientFactory, _loggerFactory.CreateLogger<ListTraceStructuredLogsTool>());
116116
}
117117
else
118118
{
@@ -121,9 +121,9 @@ protected override async Task<CommandResult> ExecuteAsync(ParseResult parseResul
121121
_knownTools[KnownMcpTools.ListResources] = new ListResourcesTool(_auxiliaryBackchannelMonitor, _loggerFactory.CreateLogger<ListResourcesTool>());
122122
_knownTools[KnownMcpTools.ListConsoleLogs] = new ListConsoleLogsTool(_auxiliaryBackchannelMonitor, _loggerFactory.CreateLogger<ListConsoleLogsTool>());
123123
_knownTools[KnownMcpTools.ExecuteResourceCommand] = new ExecuteResourceCommandTool(_auxiliaryBackchannelMonitor, _loggerFactory.CreateLogger<ExecuteResourceCommandTool>());
124-
_knownTools[KnownMcpTools.ListStructuredLogs] = new ListStructuredLogsTool(dashboardInfoProvider, _httpClientFactory, _loggerFactory.CreateLogger<ListStructuredLogsTool>());
125-
_knownTools[KnownMcpTools.ListTraces] = new ListTracesTool(dashboardInfoProvider, _httpClientFactory, _loggerFactory.CreateLogger<ListTracesTool>());
126-
_knownTools[KnownMcpTools.ListTraceStructuredLogs] = new ListTraceStructuredLogsTool(dashboardInfoProvider, _httpClientFactory, _loggerFactory.CreateLogger<ListTraceStructuredLogsTool>());
124+
_knownTools[KnownMcpTools.ListStructuredLogs] = new ListStructuredLogsTool(dashboardInfoProvider, _auxiliaryBackchannelMonitor, _httpClientFactory, _loggerFactory.CreateLogger<ListStructuredLogsTool>());
125+
_knownTools[KnownMcpTools.ListTraces] = new ListTracesTool(dashboardInfoProvider, _auxiliaryBackchannelMonitor, _httpClientFactory, _loggerFactory.CreateLogger<ListTracesTool>());
126+
_knownTools[KnownMcpTools.ListTraceStructuredLogs] = new ListTraceStructuredLogsTool(dashboardInfoProvider, _auxiliaryBackchannelMonitor, _httpClientFactory, _loggerFactory.CreateLogger<ListTraceStructuredLogsTool>());
127127
_knownTools[KnownMcpTools.SelectAppHost] = new SelectAppHostTool(_auxiliaryBackchannelMonitor, _executionContext);
128128
_knownTools[KnownMcpTools.ListAppHosts] = new ListAppHostsTool(_auxiliaryBackchannelMonitor, _executionContext);
129129
_knownTools[KnownMcpTools.ListIntegrations] = new ListIntegrationsTool(_packagingService, _executionContext, _auxiliaryBackchannelMonitor);

src/Aspire.Cli/Mcp/McpResourceToolRefreshService.cs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33

44
using System.Diagnostics;
55
using Aspire.Cli.Backchannel;
6+
using Aspire.Cli.Mcp.Tools;
67
using Microsoft.Extensions.Logging;
78
using ModelContextProtocol.Protocol;
89
using ModelContextProtocol.Server;
@@ -87,7 +88,7 @@ public async Task SendToolsListChangedNotificationAsync(CancellationToken cancel
8788
selectedAppHostPath = connection.AppHostInfo?.AppHostPath;
8889

8990
var allResources = await connection.GetResourceSnapshotsAsync(includeHidden: true, cancellationToken).ConfigureAwait(false);
90-
var resourcesWithTools = allResources.Where(r => r.McpServer is not null).ToList();
91+
var resourcesWithTools = allResources.Where(r => r.McpServer is not null && !McpToolHelpers.IsExcludedFromMcp(r)).ToList();
9192

9293
_logger.LogDebug("Resources with MCP tools received: {Count}", resourcesWithTools.Count);
9394

src/Aspire.Cli/Mcp/Tools/ExecuteResourceCommandTool.cs

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -90,6 +90,13 @@ public override async ValueTask<CallToolResult> CallToolAsync(CallToolContext co
9090
throw new McpProtocolException(McpErrorMessages.NoAppHostRunning, McpErrorCode.InternalError);
9191
}
9292

93+
// Check if the resource is excluded from MCP before executing commands.
94+
var excludedResult = await McpToolHelpers.CheckResourceExcludedAsync(connection, resourceName, cancellationToken).ConfigureAwait(false);
95+
if (excludedResult is not null)
96+
{
97+
return excludedResult;
98+
}
99+
93100
try
94101
{
95102
logger.LogDebug("Executing command '{CommandName}' on resource '{ResourceName}' via backchannel", commandName, resourceName);

src/Aspire.Cli/Mcp/Tools/ListConsoleLogsTool.cs

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -71,6 +71,14 @@ public override async ValueTask<CallToolResult> CallToolAsync(CallToolContext co
7171
throw new McpProtocolException(McpErrorMessages.NoAppHostRunning, McpErrorCode.InternalError);
7272
}
7373

74+
// Check if the resource is excluded from MCP before fetching logs.
75+
// This is the only check needed because the resource name is required for this tool.
76+
var excludedResult = await McpToolHelpers.CheckResourceExcludedAsync(connection, resourceName, cancellationToken).ConfigureAwait(false);
77+
if (excludedResult is not null)
78+
{
79+
return excludedResult;
80+
}
81+
7482
try
7583
{
7684
var logParser = new LogParser(ConsoleColor.Black);

src/Aspire.Cli/Mcp/Tools/ListResourcesTool.cs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -78,6 +78,9 @@ public override async ValueTask<CallToolResult> CallToolAsync(CallToolContext co
7878
var dashboardUrls = await dashboardUrlsTask.ConfigureAwait(false);
7979
var snapshots = await snapshotsTask.ConfigureAwait(false);
8080

81+
// Filter out resources that have opted out of MCP.
82+
snapshots = snapshots.Where(s => !McpToolHelpers.IsExcludedFromMcp(s)).ToList();
83+
8184
if (snapshots.Count == 0)
8285
{
8386
return new CallToolResult

src/Aspire.Cli/Mcp/Tools/ListStructuredLogsTool.cs

Lines changed: 24 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33

44
using System.Net.Http.Json;
55
using System.Text.Json;
6+
using Aspire.Cli.Backchannel;
67
using Aspire.Cli.Commands;
78
using Aspire.Dashboard.Otlp.Model;
89
using Aspire.Otlp.Serialization;
@@ -18,7 +19,7 @@ namespace Aspire.Cli.Mcp.Tools;
1819
/// MCP tool for listing structured logs.
1920
/// Gets log data directly from the Dashboard telemetry API.
2021
/// </summary>
21-
internal sealed class ListStructuredLogsTool(IDashboardInfoProvider dashboardInfoProvider, IHttpClientFactory httpClientFactory, ILogger<ListStructuredLogsTool> logger) : CliMcpTool
22+
internal sealed class ListStructuredLogsTool(IDashboardInfoProvider dashboardInfoProvider, IAuxiliaryBackchannelMonitor? auxiliaryBackchannelMonitor, IHttpClientFactory httpClientFactory, ILogger<ListStructuredLogsTool> logger) : CliMcpTool
2223
{
2324
public override string Name => KnownMcpTools.ListStructuredLogs;
2425

@@ -70,6 +71,16 @@ public override async ValueTask<CallToolResult> CallToolAsync(CallToolContext co
7071
// Resolve resource name to specific instances (handles replicas)
7172
var resources = await TelemetryCommandHelpers.GetAllResourcesAsync(client, apiBaseUrl, cancellationToken).ConfigureAwait(false);
7273

74+
// If a specific resource was requested, check if it's excluded from MCP.
75+
if (!string.IsNullOrEmpty(resourceName) && auxiliaryBackchannelMonitor is not null)
76+
{
77+
var excludedResult = await McpToolHelpers.CheckResourceExcludedAsync(auxiliaryBackchannelMonitor, resourceName, cancellationToken).ConfigureAwait(false);
78+
if (excludedResult is not null)
79+
{
80+
return excludedResult;
81+
}
82+
}
83+
7384
// If a resource was specified but not found, return error
7485
if (!TelemetryCommandHelpers.TryResolveResourceNames(resourceName, resources, out var resolvedResources))
7586
{
@@ -91,6 +102,18 @@ public override async ValueTask<CallToolResult> CallToolAsync(CallToolContext co
91102
var apiResponse = await response.Content.ReadFromJsonAsync(OtlpJsonSerializerContext.Default.TelemetryApiResponse, cancellationToken).ConfigureAwait(false);
92103
var resourceLogs = apiResponse?.Data?.ResourceLogs;
93104

105+
// Filter out logs from resources that are excluded from MCP.
106+
if (resourceLogs is not null && string.IsNullOrEmpty(resourceName) && auxiliaryBackchannelMonitor is not null)
107+
{
108+
var excludedNames = await McpToolHelpers.GetExcludedResourceNamesAsync(auxiliaryBackchannelMonitor, cancellationToken).ConfigureAwait(false);
109+
if (excludedNames.Count > 0)
110+
{
111+
resourceLogs = resourceLogs
112+
.Where(rl => rl.Resource?.GetServiceName() is not { } name || !excludedNames.Contains(name))
113+
.ToArray();
114+
}
115+
}
116+
94117
var (logsData, limitMessage) = SharedAIHelpers.GetStructuredLogsJson(
95118
resourceLogs,
96119
getResourceName: s => OtlpHelpers.GetResourceName(s, resources.Select(r => new SimpleOtlpResource(r.Name, r.InstanceId)).ToList()),

src/Aspire.Cli/Mcp/Tools/ListTraceStructuredLogsTool.cs

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33

44
using System.Net.Http.Json;
55
using System.Text.Json;
6+
using Aspire.Cli.Backchannel;
67
using Aspire.Cli.Commands;
78
using Aspire.Dashboard.Otlp.Model;
89
using Aspire.Otlp.Serialization;
@@ -18,7 +19,7 @@ namespace Aspire.Cli.Mcp.Tools;
1819
/// MCP tool for listing structured logs for a specific distributed trace.
1920
/// Gets log data directly from the Dashboard telemetry API.
2021
/// </summary>
21-
internal sealed class ListTraceStructuredLogsTool(IDashboardInfoProvider dashboardInfoProvider, IHttpClientFactory httpClientFactory, ILogger<ListTraceStructuredLogsTool> logger) : CliMcpTool
22+
internal sealed class ListTraceStructuredLogsTool(IDashboardInfoProvider dashboardInfoProvider, IAuxiliaryBackchannelMonitor? auxiliaryBackchannelMonitor, IHttpClientFactory httpClientFactory, ILogger<ListTraceStructuredLogsTool> logger) : CliMcpTool
2223
{
2324
public override string Name => KnownMcpTools.ListTraceStructuredLogs;
2425

@@ -91,6 +92,18 @@ public override async ValueTask<CallToolResult> CallToolAsync(CallToolContext co
9192
var apiResponse = await response.Content.ReadFromJsonAsync(OtlpJsonSerializerContext.Default.TelemetryApiResponse, cancellationToken).ConfigureAwait(false);
9293
var resourceLogs = apiResponse?.Data?.ResourceLogs;
9394

95+
// Filter out logs from resources that are excluded from MCP.
96+
if (resourceLogs is not null && auxiliaryBackchannelMonitor is not null)
97+
{
98+
var excludedNames = await McpToolHelpers.GetExcludedResourceNamesAsync(auxiliaryBackchannelMonitor, cancellationToken).ConfigureAwait(false);
99+
if (excludedNames.Count > 0)
100+
{
101+
resourceLogs = resourceLogs
102+
.Where(rl => rl.Resource?.GetServiceName() is not { } name || !excludedNames.Contains(name))
103+
.ToArray();
104+
}
105+
}
106+
94107
var (logsData, limitMessage) = SharedAIHelpers.GetStructuredLogsJson(
95108
resourceLogs,
96109
getResourceName: s => OtlpHelpers.GetResourceName(s, resources.Select(r => new SimpleOtlpResource(r.Name, r.InstanceId)).ToList()),

src/Aspire.Cli/Mcp/Tools/ListTracesTool.cs

Lines changed: 24 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33

44
using System.Net.Http.Json;
55
using System.Text.Json;
6+
using Aspire.Cli.Backchannel;
67
using Aspire.Cli.Commands;
78
using Aspire.Dashboard.Otlp.Model;
89
using Aspire.Otlp.Serialization;
@@ -18,7 +19,7 @@ namespace Aspire.Cli.Mcp.Tools;
1819
/// MCP tool for listing distributed traces.
1920
/// Gets trace data directly from the Dashboard telemetry API.
2021
/// </summary>
21-
internal sealed class ListTracesTool(IDashboardInfoProvider dashboardInfoProvider, IHttpClientFactory httpClientFactory, ILogger<ListTracesTool> logger) : CliMcpTool
22+
internal sealed class ListTracesTool(IDashboardInfoProvider dashboardInfoProvider, IAuxiliaryBackchannelMonitor? auxiliaryBackchannelMonitor, IHttpClientFactory httpClientFactory, ILogger<ListTracesTool> logger) : CliMcpTool
2223
{
2324
public override string Name => KnownMcpTools.ListTraces;
2425

@@ -70,6 +71,16 @@ public override async ValueTask<CallToolResult> CallToolAsync(CallToolContext co
7071
// Resolve resource name to specific instances (handles replicas)
7172
var resources = await TelemetryCommandHelpers.GetAllResourcesAsync(client, apiBaseUrl, cancellationToken).ConfigureAwait(false);
7273

74+
// If a specific resource was requested, check if it's excluded from MCP.
75+
if (!string.IsNullOrEmpty(resourceName) && auxiliaryBackchannelMonitor is not null)
76+
{
77+
var excludedResult = await McpToolHelpers.CheckResourceExcludedAsync(auxiliaryBackchannelMonitor, resourceName, cancellationToken).ConfigureAwait(false);
78+
if (excludedResult is not null)
79+
{
80+
return excludedResult;
81+
}
82+
}
83+
7384
// If a resource was specified but not found, return error
7485
if (!TelemetryCommandHelpers.TryResolveResourceNames(resourceName, resources, out var resolvedResources))
7586
{
@@ -91,6 +102,18 @@ public override async ValueTask<CallToolResult> CallToolAsync(CallToolContext co
91102
var apiResponse = await response.Content.ReadFromJsonAsync(OtlpJsonSerializerContext.Default.TelemetryApiResponse, cancellationToken).ConfigureAwait(false);
92103
var resourceSpans = apiResponse?.Data?.ResourceSpans;
93104

105+
// Filter out spans from resources that are excluded from MCP.
106+
if (resourceSpans is not null && string.IsNullOrEmpty(resourceName) && auxiliaryBackchannelMonitor is not null)
107+
{
108+
var excludedNames = await McpToolHelpers.GetExcludedResourceNamesAsync(auxiliaryBackchannelMonitor, cancellationToken).ConfigureAwait(false);
109+
if (excludedNames.Count > 0)
110+
{
111+
resourceSpans = resourceSpans
112+
.Where(rs => rs.Resource?.GetServiceName() is not { } name || !excludedNames.Contains(name))
113+
.ToArray();
114+
}
115+
}
116+
94117
var (tracesData, limitMessage) = SharedAIHelpers.GetTracesJson(
95118
resourceSpans,
96119
getResourceName: s => OtlpHelpers.GetResourceName(s, resources.Select(r => new SimpleOtlpResource(r.Name, r.InstanceId)).ToList()),

src/Aspire.Cli/Mcp/Tools/McpToolHelpers.cs

Lines changed: 116 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,10 +2,14 @@
22
// The .NET Foundation licenses this file to you under the MIT license.
33

44
using System.Globalization;
5+
using System.Text.Json.Nodes;
56
using System.Web;
67
using Aspire.Cli.Backchannel;
8+
using Aspire.Dashboard.Model;
79
using Microsoft.Extensions.Logging;
10+
using Microsoft.Extensions.Logging.Abstractions;
811
using ModelContextProtocol;
12+
using ModelContextProtocol.Protocol;
913

1014
namespace Aspire.Cli.Mcp.Tools;
1115

@@ -111,4 +115,116 @@ private static bool IsLocalhostTld(string host)
111115

112116
return null;
113117
}
118+
119+
/// <summary>
120+
/// Checks whether a resource snapshot has the <c>resource.excludeFromMcp</c> property set to true.
121+
/// Resources with this property should be excluded from all MCP tool results.
122+
/// </summary>
123+
internal static bool IsExcludedFromMcp(ResourceSnapshot snapshot)
124+
{
125+
if (snapshot.Properties.TryGetValue(KnownProperties.Resource.ExcludeFromMcp, out var value) && value is not null)
126+
{
127+
if (value is JsonValue jsonValue)
128+
{
129+
if (jsonValue.TryGetValue<bool>(out var boolValue))
130+
{
131+
return boolValue;
132+
}
133+
134+
if (jsonValue.TryGetValue<string>(out var stringValue) && bool.TryParse(stringValue, out var parsedBool))
135+
{
136+
return parsedBool;
137+
}
138+
}
139+
}
140+
141+
return false;
142+
}
143+
144+
/// <summary>
145+
/// Gets the error message text for a resource that is excluded from MCP.
146+
/// </summary>
147+
internal static string GetResourceNotAvailableMessage(string resourceName) =>
148+
$"Resource '{resourceName}' is not available.";
149+
150+
/// <summary>
151+
/// Gets resource snapshots from the backchannel and checks whether the specified resource is excluded from MCP.
152+
/// Returns an error <see cref="CallToolResult"/> if the resource is excluded, or <c>null</c> if it is not excluded.
153+
/// </summary>
154+
internal static async Task<CallToolResult?> CheckResourceExcludedAsync(
155+
IAuxiliaryBackchannelMonitor auxiliaryBackchannelMonitor,
156+
string resourceName,
157+
CancellationToken cancellationToken)
158+
{
159+
var excludedNames = await GetExcludedResourceNamesAsync(auxiliaryBackchannelMonitor, cancellationToken).ConfigureAwait(false);
160+
return CreateExcludedResult(excludedNames, resourceName);
161+
}
162+
163+
/// <summary>
164+
/// Checks whether the specified resource is excluded from MCP using an existing connection.
165+
/// Returns an error <see cref="CallToolResult"/> if the resource is excluded, or <c>null</c> if it is not excluded.
166+
/// </summary>
167+
internal static async Task<CallToolResult?> CheckResourceExcludedAsync(
168+
IAppHostAuxiliaryBackchannel connection,
169+
string resourceName,
170+
CancellationToken cancellationToken)
171+
{
172+
var excludedNames = await GetExcludedResourceNamesAsync(connection, cancellationToken).ConfigureAwait(false);
173+
return CreateExcludedResult(excludedNames, resourceName);
174+
}
175+
176+
private static CallToolResult? CreateExcludedResult(HashSet<string> excludedNames, string resourceName)
177+
{
178+
if (excludedNames.Contains(resourceName))
179+
{
180+
return new CallToolResult
181+
{
182+
Content = [new TextContentBlock { Text = GetResourceNotAvailableMessage(resourceName) }],
183+
IsError = true
184+
};
185+
}
186+
187+
return null;
188+
}
189+
190+
/// <summary>
191+
/// Gets the set of resource names that are excluded from MCP.
192+
/// </summary>
193+
internal static async Task<HashSet<string>> GetExcludedResourceNamesAsync(
194+
IAuxiliaryBackchannelMonitor auxiliaryBackchannelMonitor,
195+
CancellationToken cancellationToken)
196+
{
197+
var connection = await AppHostConnectionHelper.GetSelectedConnectionAsync(auxiliaryBackchannelMonitor, NullLogger.Instance, cancellationToken).ConfigureAwait(false);
198+
if (connection is null)
199+
{
200+
return [];
201+
}
202+
203+
return await GetExcludedResourceNamesAsync(connection, cancellationToken).ConfigureAwait(false);
204+
}
205+
206+
/// <summary>
207+
/// Gets the set of resource names that are excluded from MCP using an existing connection.
208+
/// </summary>
209+
internal static async Task<HashSet<string>> GetExcludedResourceNamesAsync(
210+
IAppHostAuxiliaryBackchannel connection,
211+
CancellationToken cancellationToken)
212+
{
213+
var snapshots = await connection.GetResourceSnapshotsAsync(includeHidden: true, cancellationToken).ConfigureAwait(false);
214+
var excludedNames = new HashSet<string>(StringComparers.ResourceName);
215+
216+
foreach (var snapshot in snapshots)
217+
{
218+
if (IsExcludedFromMcp(snapshot))
219+
{
220+
excludedNames.Add(snapshot.Name);
221+
if (snapshot.DisplayName is not null)
222+
{
223+
excludedNames.Add(snapshot.DisplayName);
224+
}
225+
}
226+
}
227+
228+
return excludedNames;
229+
}
114230
}

0 commit comments

Comments
 (0)