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

  1. Always construct policies through ResolvedFoundryToolboxMcpApprovalPolicy.Create rather than the record constructor.
  2. Only use FoundryToolboxMcpGlobalApprovalMode.Never and .Always.
  3. Update the switch in WriteTo if a new enum member is legitimately added.
  4. 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

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


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)