microsoft/aspire · error · InvalidOperationException

Existing MCP server configuration

Error message

Existing MCP server configuration '{builder.Resource.Name}' is read-only and cannot create an access policy.

What it means

WithAccessPolicy on an MCP server configuration adds an access policy child resource, which is impossible for existing (AsExisting) configurations because Aspire does not own or deploy them. The read-only invariant is enforced at model-build time with InvalidOperationException before any resource is added.

Solutions

  1. Grant the access policy on the existing MCP server configuration directly in Azure; remove the WithAccessPolicy call.
  2. Model the MCP server configuration as Aspire-created if policies must be code-managed.
  3. Skip WithAccessPolicy conditionally when builder.Resource.IsExisting is true.

Example fix

// before
var mcp = ns.AddMcpServerConfig("tools", options).AsExisting();
mcp.WithAccessPolicy("api", policyOptions); // throws

// after
var mcp = ns.AddMcpServerConfig("tools", options).AsExisting();
// Configure the access policy on the existing config outside Aspire.
Defensive patterns

Strategy: validation

Validate before calling

if (!mcp.Resource.IsExisting)
{
    mcp.WithAccessPolicy("api", policyOptions);
}

Try / catch

try
{
    mcp.WithAccessPolicy("api", policyOptions);
}
catch (InvalidOperationException ex) when (ex.Message.Contains("is read-only"))
{
    // manage the policy on the existing MCP config outside Aspire
}

Prevention

When it happens

Trigger: Calling WithAccessPolicy(name, options) on an AzureConnectorNamespaceMcpServerConfigResource builder whose Resource.IsExisting is true.

Common situations: Pointing an MCP config at pre-provisioned infrastructure and then trying to grant a client compute resource access via WithAccessPolicy; reusing a modeling extension that assumes create mode.

Understand the failure class

Background: "Invalid state transition" errors: "status must be X, actually Y", "already rejected/charging/uninstalled", "cannot ... while running" — what they mean when a library rejects your call — this error's family across 31 libraries.

Related errors


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

Appendix: source

Thrown at src/Aspire.Hosting.Azure.ConnectorNamespace/AzureConnectorNamespaceExtensions.cs:546

    /// <remarks>
    /// Managed MCP endpoints reject callers that do not have a config-scoped access policy.
    /// Connector Namespace currently supports Microsoft Entra users and groups for these policies.
    /// The Azure child resource name is set to the principal object ID as required by the service.
    /// </remarks>
    /// <ats-returns>The resource builder.</ats-returns>
    [AspireExport("withMcpServerConfigAccessPolicy", MethodName = "withAccessPolicy")]
    public static IResourceBuilder<AzureConnectorNamespaceMcpServerConfigResource> WithAccessPolicy(
        this IResourceBuilder<AzureConnectorNamespaceMcpServerConfigResource> builder,
        [ResourceName] string name,
        AzureConnectorNamespaceMcpAccessPolicyOptions options)
    {
        ArgumentNullException.ThrowIfNull(builder);
        ArgumentException.ThrowIfNullOrWhiteSpace(name);
        ArgumentNullException.ThrowIfNull(options);
        ValidateMcpAccessPolicyOptions(options);
        if (builder.Resource.IsExisting)
        {
            throw new InvalidOperationException(
                $"Existing MCP server configuration '{builder.Resource.Name}' is read-only and cannot create an access policy.");
        }

        var resourceName = GetValidatedMcpAccessPolicyResourceName(builder.Resource, name, options.ObjectId);
        builder.Resource.AccessPolicies.Add(new AzureConnectorNamespaceMcpAccessPolicyResource(
            resourceName,
            builder.Resource,
            options.ObjectId,
            options.TenantId,
            options.PrincipalType));
        return builder;
    }

    /// <summary>
    /// Adds a connector route and an explicit operation allow-list to a managed MCP server configuration.
    /// </summary>
    /// <param name="builder">The MCP server configuration resource builder.</param>
    /// <param name="connectorName">The connector route name.</param>

View on GitHub (pinned to 25830f84bd)