microsoft/aspire · error · InvalidOperationException
Unsupported MCP approval mode
Error message
Unsupported MCP approval mode '{global}'. What it means
WriteTo serializes the global approval mode to its wire string via a switch over Never/Always. If Global holds an unsupported enum value at serialization time, the default arm throws this InvalidOperationException. This is a defensive guard — Create normally rejects such values first, so hitting it indicates the value bypassed validation.
Solutions
- Always construct policies through ResolvedFoundryToolboxMcpApprovalPolicy.Create rather than the record constructor.
- Only use FoundryToolboxMcpGlobalApprovalMode.Never and .Always.
- Update the switch in WriteTo if a new enum member is legitimately added.
- Catch InvalidOperationException during serialization and inspect the Global value in the message.
Example fix
// before
var resolved = new ResolvedFoundryToolboxMcpApprovalPolicy((FoundryToolboxMcpGlobalApprovalMode)7, null, null);
// after
var resolved = ResolvedFoundryToolboxMcpApprovalPolicy.Create(new FoundryToolboxMcpApprovalPolicy { Global = FoundryToolboxMcpGlobalApprovalMode.Never }); Defensive patterns
Strategy: try-catch
Validate before calling
if (policy.Global is { } g && g is not (FoundryToolboxMcpGlobalApprovalMode.Never or FoundryToolboxMcpGlobalApprovalMode.Always))
throw new InvalidOperationException($"Unsupported MCP approval mode '{g}'."); Type guard
static bool Serializable(ResolvedFoundryToolboxMcpApprovalPolicy p) =>
p.Global is null || p.Global is FoundryToolboxMcpGlobalApprovalMode.Never or FoundryToolboxMcpGlobalApprovalMode.Always; Try / catch
try { policy.WriteTo(writer); }
catch (InvalidOperationException ex) when (ex.Message.StartsWith("Unsupported MCP approval mode"))
{ logger.LogError(ex, "Policy bypassed Create validation; rebuild via Create()."); } Prevention
- Build resolved policies only via ResolvedFoundryToolboxMcpApprovalPolicy.Create
- Update WriteTo's switch when the enum gains new members
- Add a serialization round-trip test for every enum member
When it happens
Trigger: A ResolvedFoundryToolboxMcpApprovalPolicy constructed directly (record constructor) with an out-of-range Global, skipping Create's validation; a new enum member added to FoundryToolboxMcpGlobalApprovalMode without updating this switch.
Common situations: Unit tests or other integrations building the resolved record manually; SDK/enum evolution between package versions.
Related errors
- The global MCP approval mode is not supported.
- A global MCP approval policy cannot be combined with custom…
- An MCP approval policy must specify a global mode or at…
- MCP tool ' ' cannot both always and never require approval.
- MCP tools with read_only set to
AI-assisted analysis of microsoft/aspire@25830f84bd (2026-09-16).
Data as JSON: /api/errors/6b5b9886e97fe7f3.
Report an issue: GitHub.
Appendix: source
Thrown at src/Aspire.Hosting.Foundry/Toolbox/FoundryToolboxToolDefinition.cs:277
if (always?.ReadOnly is { } alwaysReadOnly && never?.ReadOnly == alwaysReadOnly)
{
throw new ArgumentException(
$"MCP tools with read_only set to '{alwaysReadOnly.ToString().ToLowerInvariant()}' cannot both always and never require approval.",
nameof(policy));
}
return new(policy.Global, always, never);
}
public void WriteTo(Utf8JsonWriter writer)
{
if (Global is { } global)
{
writer.WriteStringValue(global switch
{
FoundryToolboxMcpGlobalApprovalMode.Never => "never",
FoundryToolboxMcpGlobalApprovalMode.Always => "always",
_ => throw new InvalidOperationException($"Unsupported MCP approval mode '{global}'.")
});
return;
}
writer.WriteStartObject();
Always?.WriteTo(writer, "always");
Never?.WriteTo(writer, "never");
writer.WriteEndObject();
}
}
internal sealed record ResolvedFoundryToolboxMcpApprovalFilter(
IReadOnlyList<string> ToolNames,
bool? ReadOnly)
{
public static ResolvedFoundryToolboxMcpApprovalFilter? Create(
FoundryToolboxMcpApprovalFilter? filter,
string parameterName)View on GitHub (pinned to 25830f84bd)