microsoft/aspire · error · ArgumentException

MCP tools with read_only set to

Error message

MCP tools with read_only set to '{alwaysReadOnly.ToString().ToLowerInvariant()}' cannot both always and never require approval.

What it means

The OpenAI MCP approval filters support a read_only predicate instead of explicit tool names. Setting the same read_only boolean (true in both, or false in both) on the Always and Never filters is contradictory and rejected with this ArgumentException during policy creation.

Solutions

  1. Keep ReadOnly on only one filter (usually Never for read-only tools).
  2. Give the two filters different ReadOnly values (true vs false) if both are needed.
  3. Prefer explicit ToolNames filters instead of the read_only predicate to avoid the overlap.
  4. Validate policy JSON/config so read_only appears in at most one of always/never.

Example fix

// before
Always = new() { ReadOnly = true },
Never  = new() { ReadOnly = true }
// after
Never = new() { ReadOnly = true }
Defensive patterns

Strategy: validation

Validate before calling

if (always.ReadOnly is { } ar && never?.ReadOnly == ar)
    throw new ArgumentException("read_only cannot be set to the same value on both filters.");

Type guard

static bool ReadOnlyNotDuplicated(FoundryToolboxMcpApprovalFilter? a, FoundryToolboxMcpApprovalFilter? n) =>
    a?.ReadOnly is null || n?.ReadOnly != a.ReadOnly;

Try / catch

try { toolDefinition = CreateMcpTool(...); }
catch (ArgumentException ex) when (ex.Message.Contains("read_only"))
{ logger.LogError("Set read_only on only one of always/never filters."); }

Prevention

When it happens

Trigger: always: { ReadOnly = true } and never: { ReadOnly = true } (or both false) in the same FoundryToolboxMcpApprovalPolicy — e.g. intending 'read-only ops need no approval' but duplicating the flag on both filters.

Common situations: Copy-pasting a filter and only renaming always↔never while keeping ReadOnly set; config-driven filters where the same read_only value was templated into both buckets.

Understand the failure class

Background: Conflicting config options: "cannot be used together" — configuration validation errors across open-source libraries — this error's family across 162 libraries.

Related errors


AI-assisted analysis of microsoft/aspire@25830f84bd (2026-09-16). Data as JSON: /api/errors/9ae36bdb1f03f107. Report an issue: GitHub.

Appendix: source

Thrown at src/Aspire.Hosting.Foundry/Toolbox/FoundryToolboxToolDefinition.cs:261

            throw new ArgumentOutOfRangeException(
                nameof(policy),
                policy.Global,
                "The global MCP approval mode is not supported.");
        }

        var overlap = always?.ToolNames
            .Intersect(never?.ToolNames ?? [], StringComparer.Ordinal)
            .FirstOrDefault();
        if (overlap is not null)
        {
            throw new ArgumentException(
                $"MCP tool '{overlap}' cannot both always and never require approval.",
                nameof(policy));
        }

        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;

View on GitHub (pinned to 25830f84bd)