The decision to implement were:
- https://github.com/OpenAPITools/openapi-generator – because
- has multiple language implementations
- has a WebAPI docker
- Implement one SDK generator per WebAPI –
- it is simpler to generate commands for each one , rather to have multiple on the same ASPIRE resource
- Can wait for 1 WebAPI to become available, rather for all
This said, the usage is pretty simple:
var apiService = builder.AddProject<Projects.WebAPIDocsExtensions_ApiService>("apiservice")
apiService.AddSDKGeneration_openapitools();
And the result is

genClients – saves all the clients in the specified folder ( wwwroot/docs )
html2 – generates code for HTML 2
clients – shows all the programming languages on the ASPIRE log
The code has had the following problems to be solved:
1. Find the clients to send the command
The WebAPI has a HTPP endpoint call to retrieve the languages ( api/gen/clients ) . The result is a JSON array – and the software needs this JSON array to have download the generated SDK in the language. How to retrieve and send this to the command ? Retrieve – easy – use OnResourceReady .
internal class AnnotationClients : IResourceAnnotation
{
    public string Text { get; set; } = string.Empty;
    public string[] Data { get; set; } = [];
    public string Url { get; set; } = string.Empty;
    public AnnotationClients(string text)
    {
        Text = text;
    }
}
resource.OnResourceReady(async (res, evt, ct) =>
{
        // code omitted to retrieve endpoints of aspire resourceHttpClient client = new();
client.BaseAddress = new Uri(first);
try
{
    var response = await client.GetAsync("api/gen/clients");
    response.EnsureSuccessStatusCode();
    var text = await response.Content.ReadAsStringAsync();
    logger?.LogInformation($"!!!Clients: {text}");
    var ann = res.Annotations.FirstOrDefault(it => it is AnnotationClients) as AnnotationClients;
    if (ann == null)
    {
        logger?.LogError($"!!!!!!!!Container {name} has no annotation.");
        return;
    }
    ann.Url = first;
    ann.Data = text
        .Replace("[", "")
        .Replace("]", "")
        .Replace("\"", "")
        .Split(',', StringSplitOptions.RemoveEmptyEntries);
To send to the command , I use an IResourceAnnotation
.WithCommand("genClients", "genClients", async (ctx) =>
{
    //code omited  for clarity
    var ann = resource.Resource.Annotations.FirstOrDefault(it => it is AnnotationClients) as AnnotationClients;
    if (ann == null)
    {
        return new ExecuteCommandResult() { Success = false, ErrorMessage = "No annotation found" };
    }
    if (ann.Data.Length == 0)
    {
        return new ExecuteCommandResult() { Success = false, ErrorMessage = "No clients available" };
    }
    logger?.LogInformation("Available clients:");
    HttpClient httpClient = new();
    httpClient.BaseAddress = new Uri(ann.Url);
    foreach (var client in ann.Data)
            
2. The docker with OpenAPITools has no acces to localhost , where the WebAPI works. However, because I added ASPIRE reference of OpenAPITools of the WebAPI, ASPIRE has been nice to add a network – hence this code to retrieve the WebAPI base address
   var endPoints = project.Resource.GetEndpoints()?.ToArray() ?? [];
   var first = endPoints.First(it => it.Url.Contains("http://")).Url.Replace("localhost", "host.docker.internal");
   var http = first.EndsWith("/") ? first : first + "/";
2