microsoft/aspire · error · InvalidOperationException

Existing MCP server configuration

Error message

Existing MCP server configuration '{builder.Resource.Name}' is read-only.

What it means

WithConnector attaches a connector route to an MCP server configuration, but existing (AsExisting) configurations are read-only references: adding a connector would imply deployment changes Aspire cannot make. The method checks IsExisting first and throws immediately.

Solutions

  1. Remove the WithConnector call and associate the connector with the existing configuration in Azure directly.
  2. Model the MCP server configuration as Aspire-created when connector routes must be declared.
  3. Guard the call with if (!builder.Resource.IsExisting).

Example fix

// before
var mcp = ns.AddMcpServerConfig("tools", options).AsExisting();
mcp.WithConnector("db-conn", connection, connectorOptions); // throws

// after
var mcp = ns.AddMcpServerConfig("tools", options).AsExisting();
// Associate connectors on the existing configuration outside Aspire.
Defensive patterns

Strategy: validation

Validate before calling

if (!mcp.Resource.IsExisting)
{
    mcp.WithConnector("db-conn", connection, connectorOptions);
}

Try / catch

try
{
    mcp.WithConnector("db-conn", connection, connectorOptions);
}
catch (InvalidOperationException ex) when (ex.Message.Contains("is read-only"))
{
    // associate connectors on the existing configuration outside Aspire
}

Prevention

When it happens

Trigger: Calling WithConnector(connectorName, connection, options) on an MCP server configuration builder whose Resource.IsExisting is true.

Common situations: Wiring an imported MCP server configuration to a connector; shared helper methods that always call WithConnector regardless of create/existing 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/a0cf30d7cc1e7442. Report an issue: GitHub.

Appendix: source

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

    /// are connector-specific and should be verified against the connector operation metadata. The current
    /// Connector Namespace preview supports one connector per managed MCP server configuration.
    /// </remarks>
    /// <ats-returns>The resource builder.</ats-returns>
    [AspireExport]
    public static IResourceBuilder<AzureConnectorNamespaceMcpServerConfigResource> WithConnector(
        this IResourceBuilder<AzureConnectorNamespaceMcpServerConfigResource> builder,
        string connectorName,
        IResourceBuilder<AzureConnectorNamespaceConnectionResource> connection,
        AzureConnectorNamespaceMcpConnectorOptions options)
    {
        ArgumentNullException.ThrowIfNull(builder);
        ArgumentException.ThrowIfNullOrWhiteSpace(connectorName);
        ArgumentNullException.ThrowIfNull(connection);
        ArgumentNullException.ThrowIfNull(options);

        if (builder.Resource.IsExisting)
        {
            throw new InvalidOperationException(
                $"Existing MCP server configuration '{builder.Resource.Name}' is read-only.");
        }

        if (!ReferenceEquals(builder.Resource.Parent, connection.Resource.Parent))
        {
            throw new InvalidOperationException(
                $"Connector connection '{connection.Resource.Name}' belongs to a different Connector Namespace.");
        }

        if (builder.Resource.Connectors.Count > 0)
        {
            throw new InvalidOperationException(
                $"MCP server configuration '{builder.Resource.Name}' already has a connector. " +
                "The current Connector Namespace preview supports one connector per MCP server configuration.");
        }

        if (options.Operations is null || options.Operations.Length == 0)
        {

View on GitHub (pinned to 25830f84bd)