microsoft/aspire · error · InvalidOperationException

A discovered Toolbox tool did not have a name.

Error message

A discovered Toolbox tool did not have a name.

What it means

Thrown while enumerating the tools array from the MCP tools/list response when a tool entry's 'name' property is null. GetProperty throws KeyNotFoundException if 'name' is absent; the ??-throw fires when the property exists but holds null, so this is an internal-shape violation of the MCP response contract.

Solutions

  1. Inspect the MCP server backing the Toolbox and ensure every declared tool has a non-null name.
  2. Check the raw tools/list response (capture the JSON) to identify which tool entry is malformed.
  3. Update or fix the MCP server/tool registration, then re-run the readiness check.
  4. If caused by a Foundry service change, upgrade the Aspire.Hosting.Foundry package to a version matching the server's MCP schema.

Example fix

// before
server declares: { "description": "search tool" } // no name
// after
server declares: { "name": "search", "description": "search tool" }
Defensive patterns

Strategy: try-catch

Try / catch

try
{
    await probe.WaitForToolsAsync(accessToken, ct);
}
catch (InvalidOperationException ex) when (ex.Message.Contains("did not have a name"))
{
    logger.LogError(ex, "MCP server behind the Toolbox advertised an unnamed tool; fix the tool registration.");
    throw;
}

Prevention

When it happens

Trigger: A tool registered in the Foundry Toolbox has no name (malformed tool registration), or the server returns a tools/list entry missing/null 'name'.

Common situations: A custom MCP server behind the Toolbox advertises a tool without a name; a Toolbox deployment bug or schema change returns unnamed tool entries; a partial/corrupt response from an intermediate proxy.

Related errors


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

Appendix: source

Thrown at src/Aspire.Hosting.Foundry/Toolbox/FoundryToolboxReadinessProbe.cs:84

                {
                    var response = await SendRequestAsync(
                        endpoint,
                        accessToken,
                        initialize.SessionId,
                        negotiatedProtocol,
                        CreateToolsListPayload(requestId++, cursor),
                        discoveryCancellation.Token,
                        retryInternalServerError: true).ConfigureAwait(false);
                    if (response.IsRetryableFailure)
                    {
                        retryDiscovery = true;
                        break;
                    }

                    foreach (var tool in response.Result.GetProperty("tools").EnumerateArray())
                    {
                        discoveredToolNames.Add(tool.GetProperty("name").GetString()
                            ?? throw new InvalidOperationException("A discovered Toolbox tool did not have a name."));
                    }

                    // MCP paginates tools/list as:
                    //   {"result":{"tools":[...],"nextCursor":"opaque continuation token"}}
                    cursor = response.Result.TryGetProperty("nextCursor", out var nextCursor)
                        ? nextCursor.GetString()
                        : null;
                }
                while (!string.IsNullOrEmpty(cursor));

                var hasRequiredTools =
                    requiredToolNames.All(discoveredToolNames.Contains) &&
                    requiredMcpServerLabels.All(label =>
                        discoveredToolNames.Any(name =>
                            name.StartsWith($"{label}.", StringComparison.Ordinal)));
                var hasConfiguredExpectations =
                    requiredToolNames.Count > 0 || requiredMcpServerLabels.Count > 0;
                if (!retryDiscovery &&

View on GitHub (pinned to 25830f84bd)