microsoft/aspire · error · ArgumentException
An MCP approval filter must specify at least one tool name…
Error message
An MCP approval filter must specify at least one tool name or a read-only value.
What it means
ResolvedFoundryToolboxMcpApprovalFilter.Create normalizes and deduplicates ToolNames, then requires that the filter actually selects something: at least one tool name, or a non-null ReadOnly value. A filter with no names and no ReadOnly has no wire representation, so an ArgumentException naming the offending property (Always or Never) is thrown.
Solutions
- Add at least one tool name to ToolNames.
- Set ReadOnly = true (or false) on the filter.
- Pass null for the filter instead of an empty one.
- Log the filter contents before creating the policy to confirm names loaded from config.
Example fix
// before
new FoundryToolboxMcpApprovalFilter { ToolNames = [] }
// after
new FoundryToolboxMcpApprovalFilter { ToolNames = ["search"], ReadOnly = false } Defensive patterns
Strategy: validation
Validate before calling
bool selects = filter.ToolNames is { Count: > 0 } || filter.ReadOnly is not null;
if (!selects) throw new ArgumentException("Filter needs a tool name or a read_only value."); Type guard
static bool IsMeaningful(FoundryToolboxMcpApprovalFilter f) =>
(f.ToolNames is { Count: > 0 }) || f.ReadOnly is not null; Try / catch
try { toolDefinition = CreateMcpTool(...); }
catch (ArgumentException ex) when (ex.Message.Contains("at least one tool name or a read-only value"))
{ logger.LogError("Approval filter {Param} is empty; add tool names or set ReadOnly.", ex.ParamName); } Prevention
- Pass null instead of an empty filter object
- Validate that config-sourced tool name arrays are non-empty before binding
- Always set ReadOnly or names when declaring a filter
When it happens
Trigger: Passing new FoundryToolboxMcpApprovalFilter { ToolNames = [] } (or null ToolNames) with ReadOnly unset as policy.Always or policy.Never; a filter whose entries were all whitespace (those throw earlier via ThrowIfNullOrWhiteSpace) or deduplicated away to zero.
Common situations: Deserialized config where tool_names was an empty array; code that builds the filter conditionally and adds no names; YAML/JSON tool lists that failed to bind.
Understand the failure class
Background: "must not be empty", "cannot be empty" — required-field validation errors across open-source libraries — this error's family across 41 libraries.
Related errors
- An MCP approval policy must specify a global mode or at…
- A global MCP approval policy cannot be combined with custom…
- MCP tool ' ' does not have a resolvable endpoint URI.
- MCP tool ' ' must resolve to a Foundry-reachable absolute…
- MCP tool ' ' cannot both always and never require approval.
AI-assisted analysis of microsoft/aspire@25830f84bd (2026-09-16).
Data as JSON: /api/errors/7cf5f52b56369104.
Report an issue: GitHub.
Appendix: source
Thrown at src/Aspire.Hosting.Foundry/Toolbox/FoundryToolboxToolDefinition.cs:314
{
if (filter is null)
{
return null;
}
var toolNames = (filter.ToolNames ?? [])
.Select(name =>
{
ArgumentException.ThrowIfNullOrWhiteSpace(name, parameterName);
return name;
})
.Distinct(StringComparer.Ordinal)
.Order(StringComparer.Ordinal)
.ToArray();
if (toolNames.Length == 0 && filter.ReadOnly is null)
{
throw new ArgumentException(
"An MCP approval filter must specify at least one tool name or a read-only value.",
parameterName);
}
return new(toolNames, filter.ReadOnly);
}
public void WriteTo(Utf8JsonWriter writer, string propertyName)
{
writer.WriteStartObject(propertyName);
if (ToolNames.Count > 0)
{
writer.WriteStartArray("tool_names");
foreach (var toolName in ToolNames)
{
writer.WriteStringValue(toolName);
}
writer.WriteEndArray();View on GitHub (pinned to 25830f84bd)