De vorige post ging over het lezen van de AppHost. Architectuur als code. De dependency graph in één bestand. Claude Code als gids door statische configuratie.

Nuttig — maar slechts de helft van het verhaal.

De andere helft is wat er gebeurt als de applicatie daadwerkelijk draait. Logs verschijnen, traces vertakken zich over services, health checks springen op groen en rood. Het Aspire dashboard laat dat allemaal mooi zien.

Het probleem is dat het dashboard in een browsertab zit en jouw werk in je editor. Elke diagnose verandert al snel in tab-switchen, stacktraces in chat plakken en context reconstrueren die het draaiende systeem allang heeft.

Sinds Aspire 13 ontsluit het dashboard runtime data via een MCP-server. Claude Code kan de draaiende Aspire-applicatie direct bevragen — dezelfde resources, logs, traces en resource commands die je anders met de hand zou inspecteren.

Dat verandert de workflow van:

“Leg deze AppHost uit.”

naar:

“Wat gebeurt er nu in deze AppHost?”

Dit vervangt het dashboard niet. Het maakt van het dashboard een queryable bron van runtime context voor de agent.


Vereisten

Je hebt een paar dingen nodig:

  • .NET Aspire 13 of nieuwer
  • de Aspire CLI geïnstalleerd
  • je AppHost lokaal draaiend
  • Claude Code of een andere MCP-capable assistant

Het exacte setup-commando hangt af van je Aspire CLI-versie, en dat is relevant omdat de naamgeving rond dit onderwerp is veranderd.


Setup: aspire agent init

Er zijn twee routes: de CLI-route en de handmatige dashboard-route.

In recente Aspire CLI-versies is de makkelijkste route:

aspire agent init

Als dat commando niet beschikbaar is in je geïnstalleerde CLI, probeer dan de oudere naam:

aspire mcp init

De CLI detecteert ondersteunde AI-assistants — zoals VS Code, Visual Studio, Cursor en Claude Code — en schrijft de MCP-configuratie voor ze weg.

Voor Claude Code betekent dit dat de assistant de Aspire MCP-bridge via STDIO kan starten. Je hoeft geen dashboard-URL’s, API-keys of certificaten handmatig in je assistant-configuratie te kopiëren.

De handmatige route bestaat ook nog. Start de app, open het Aspire dashboard, klik op de MCP-knop en kopieer het HTTP-endpoint plus de waarde van de x-mcp-api-key header naar de MCP-configuratie van je assistant.

Het verschil is relevant:

  • het dashboard ontsluit een HTTP MCP-endpoint
  • de CLI-gegenereerde setup kan dat verbergen achter een lokaal STDIO-commando

Daarom is de CLI-route meestal soepeler. Het scheelt een hoop frictie rond certificaten en endpoints die je krijgt bij handmatige HTTP-configuratie.

Eén valkuil om vooraf te kennen: het HTTP-endpoint van het dashboard kan een self-signed certificaat gebruiken. Sommige AI-assistants weigeren ermee te praten. De workaround voor lokale ontwikkeling is om in plaats daarvan een HTTP-endpoint aan te bieden, door deze waarden in launchSettings.json te zetten:

{
  "profiles": {
    "https": {
      "environmentVariables": {
        "ASPIRE_DASHBOARD_MCP_ENDPOINT_URL": "http://localhost:18888",
        "ASPIRE_ALLOW_UNSECURED_TRANSPORT": "true"
      }
    }
  }
}

Gebruik dit alleen voor lokale ontwikkeling. Zet unsecured transport niet aan in gedeelde, remote of productie-achtige omgevingen.

Eerste test, ongeacht de route:

Geef alle resources in deze Aspire-app.

Als Claude Code een gestructureerde lijst van je services, containers en dependencies teruggeeft, werkt de verbinding. Krijg je niets terug, dan is de assistant mogelijk buiten je workspace gestart, draait de app niet, of blokkeert het HTTP-certificaatprobleem hierboven de verbinding.


Resources inspecteren

De Aspire MCP-server biedt een kleine set tools:

  • list_resources
  • list_console_logs
  • list_structured_logs
  • list_traces
  • list_trace_structured_logs
  • execute_resource_command

De meest nuttige workflows zijn combinaties van deze tools.

list_resources is het werkpaard. Het geeft de resources in de draaiende AppHost terug met hun huidige state, health status, endpoints, environment metadata en alle commands die het dashboard voor ze ontsluit.

In Grouply — het multi-tenant fleet platform uit de vorige post — gaat het dan om resources zoals:

  • API service
  • background workers
  • Postgres
  • Service Bus
  • Redis
  • Keycloak
  • het dashboard zelf

De use case is niet alleen:

Wat zit er in mijn AppHost?

Die code heb je zelf geschreven.

De nuttigere vraag is:

Waarom werkt dit nu niet?

Vraag:

Welke resources zijn unhealthy?

en Claude Code kan de runtime state direct opvragen.

Vraag:

Waar wacht apiservice op?

en je ontdekt misschien dat hij in Starting blijft hangen omdat Keycloak nog Unhealthy is.

Dat is dezelfde dependency graph uit de AppHost, maar dan zichtbaar op runtime.


Gevoelige resources buiten MCP houden

Voor enterprise-scenario’s is er één detail dat ik vroeg wil noemen: ExcludeFromMcp().

Sommige resources horen niet blootgesteld te worden aan AI-assistants. Misschien bevat een database productie-achtige testdata. Misschien logt een service tijdens lokale ontwikkeling gevoelige waarden. Misschien staan in de resource-metadata endpoints of environment variables die je niet in de assistant-context wilt hebben.

Je sluit een resource uit in de AppHost:

var sensitiveDb = builder
    .AddPostgres("audit-db")
    .ExcludeFromMcp();

De uitsluiting dekt de resource, de logs en de telemetry.

Dit is iets om vroeg in te regelen. Alles wat je via MCP ontsluit kan onderdeel worden van de werkende context van de assistant, en afhankelijk van de assistant en zijn configuratie kan die context je machine verlaten.

MCP maakt het draaiende systeem makkelijker te inspecteren. Dat betekent ook dat je moet beslissen wat juist niet inspecteerbaar mag zijn.


Logs debuggen zonder logs te kopiëren

De concrete case.

Nadat ik de reporting worker uit de vorige post had toegevoegd — een tweede subscriber op de vehicles Service Bus topic — startte ik Aspire en keek naar het dashboard.

De worker kwam op. Hij werd healthy. En toen bleef hij stilstaan.

Geen messages verwerkt.

Op het eerste gezicht geen duidelijke errors.

De console-log van de worker was een muur aan startup-output. Telemetry-initialisatie, dependency injection registraties, health check registraties, container-output — daar ergens tussenin zat het echte probleem.

In plaats van handmatig te scrollen, vroeg ik Claude Code:

Laat errors en warnings zien van reportingworker in de afgelopen 5 minuten.

Die scoped vraag is belangrijk. Vraag ik om elke logregel van elke service, dan wordt het antwoord snel ruis. Met de resource-naam, severity en tijdsvenster erbij rolde de bruikbare regel er meteen uit:

Azure.Messaging.ServiceBus.ServiceBusException:
The messaging entity 'sb://servicebus-xxxx.servicebus.windows.net/
  vehicles/Subscriptions/reportingconsumer' could not be found.
(MessagingEntityNotFound)

Zodra je het bericht ziet, is de diagnose duidelijk: de subscription bestaat niet.

Terug naar de AppHost extension method:

var vehiclesTopic = resourceBuilder.AddServiceBusTopic("vehicles");

vehiclesTopic.AddServiceBusSubscription(
    "apiserviceconsumer",
    "apiservice");

// Ontbreekt:
vehiclesTopic.AddServiceBusSubscription(
    "reportingconsumer",
    "reporting");

Het worker-project aan de AppHost toevoegen was stap één.

De Service Bus subscription toevoegen was stap twee.

Ik had stap één gedaan en stap twee niet.

Het nuttige was niet dat Claude Code op magische wijze Service Bus snapte. Het nuttige was dat het de runtime logs kon bevragen, kon filteren, de fout aan de AppHost-configuratie kon koppelen, en de hele diagnose binnen de editor kon houden.


Kies structured logs als het kan

Console-logs worden snel lang.

Aspire produceert veel bij startup. Dependency containers ratelen door. EF Core kan elke query narreren. Authentication middleware kan meerdere regels per gefaalde request opleveren.

De MCP-server kan grote responses afkappen. Vraag je:

Laat alle logs zien van alle services in het afgelopen uur.

dan kan de relevante regel afgekapt zijn voordat Claude Code hem ooit ziet.

Filter agressief:

Laat warnings en errors zien van reportingworker in de afgelopen 5 minuten.
Laat structured logs van apiservice zien voor requests die 401 teruggaven.
Vind recente Service Bus errors voor de reporting worker.

Structured logs zijn meestal beter dan console-output. Ze dragen velden waar Claude Code betrouwbaarder op kan filteren en correleren dan op rauwe tekst.

De discipline is dezelfde als bij elke observability tool: scope smal, vraag dicht op het event, en kies gestructureerde data boven muren tekst.


Traces en de configuratieketen

Het JWT/Keycloak-probleem uit de vorige post werd post-mortem opgelost. Ik had de error, de AppHost en de API-configuratie. Claude Code reconstrueerde de keten door meerdere bestanden heen.

Met de MCP-server kan dezelfde diagnose plaatsvinden terwijl de app draait.

Als een authenticatie-request faalt, kan de trace de hele keten laten zien:

  • inkomende request naar apiservice
  • metadata-fetch naar Keycloak
  • token-validatie
  • gefaalde authenticatie
  • 401 response

Elke span kan structured logs dragen.

De vraag wordt:

Laat de meest recente gefaalde authentication trace zien voor apiservice.

Het nuttige antwoord is niet alleen het trace-id. Het is de span tree plus de structured log die aan de falende span hangt:

IDX10205: Issuer validation failed.

Issuer:
'http://keycloak:8080/realms/grouply'

Did not match validationParameters.ValidIssuer:
'https://keycloak-abc123.azurecontainerapps.io/realms/grouply'

De asymmetrie is in één query zichtbaar.

De issuer van het token en de geconfigureerde valid issuer komen niet overeen. De één wijst naar de interne Keycloak-URL, de ander naar de externe HTTPS-URL.

Dat is precies het soort distributed configuratieprobleem dat je makkelijk mist als je bestand voor bestand inspecteert. De AppHost, environment variables, authenticatie-configuratie, container-networking en de Azure-deployment doen er allemaal toe.

Hier verdient de combinatie zich terug.

Distributed traces zijn lastig te lezen in rauwe vorm. De visuele timeline van het dashboard is uitstekend, maar niet altijd het snelste startpunt als je nog niet weet waar je naar zoekt.

Vragen aan Claude Code:

Vat de gefaalde authentication trace samen en vertel me waar het misging.

kan recht naar de kern gaan.

Het visuele dashboard wint nog steeds als je intuïtie wilt voor timing: latency-pieken, fan-out vorm, langzame dependencies, of een trace met veel spans. De MCP-route wint als je correlatie, filtering of een geschreven uitleg wilt.

Andere tools, andere momenten.


Commands uitvoeren — en waar het stopt

execute_resource_command voert de commands uit die het dashboard per resource biedt.

Dat zijn meestal commands zoals:

  • start
  • stop
  • restart

en alle custom commands die je in de AppHost hebt gedefinieerd.

Dit werkt dus:

Restart Redis.

Dit ook:

Stop de reporting worker terwijl ik de subscription-configuratie aanpas.

Dat is handig als je een configuratiewaarde hebt gecorrigeerd en één resource wilt herstarten in plaats van de hele AppHost.

Maar daar zit ongeveer het plafond.

Je kunt resources niet dynamisch herconfigureren. Je kunt geen dependencies herbedraden. Je kunt geen nieuwe services toevoegen op runtime. Je kunt een structureel AppHost-probleem niet oplossen door de draaiende app te muteren.

Die grens is bewust. De runtime van Aspire moet weerspiegelen wat er in code staat, niet ervan afdrijven.

Heb je een structurele wijziging nodig, dan pas je de AppHost aan en herstart je.


Waar het tekortschiet

De MCP-server is read-mostly.

Het meeste wat je doet is state observeren. Schrijven is beperkt tot een kleine set vooraf gedefinieerde resource commands. Dat past bij het dev-time observability-model van Aspire, maar het is goed om expliciet te benoemen.

Claude Code repareert geen verkeerde connection string in de draaiende app. De loop is:

  1. observeren via MCP
  2. de bron aanpassen
  3. de resource herstarten of bouncen
  4. verifiëren via MCP

Latency telt ook op.

Elke query gaat van Claude Code naar de MCP-server, dan naar de runtime data van het dashboard, en weer terug via de assistant. Voor interactief debuggen is dat prima. Voor heel strakke iteratie-loops merk je de vertraging.

Druk op het context window is reëel.

Truncation is geen theorie. Het bijt zodra je te veel vraagt:

Laat alle logs zien van alle services in het afgelopen uur.

Dat is een slechte vraag.

Een betere:

Laat warnings en errors zien van apiservice en reportingworker in de afgelopen 10 minuten.

Visuele traces winnen nog steeds voor sommige taken.

Een complexe trace met twintig spans lees je soms sneller visueel dan via een prosa-samenvatting. De MCP-route schittert als je correlatie wilt over resources heen of als je de assistant de faalketen wilt laten uitleggen. Het dashboard wint als je vorm en timing wilt zien.

Tot slot: de MCP-server hoort bij één draaiende app.

Stop de Aspire-app en de MCP-server is weg. Draai twee Aspire-applicaties naast elkaar en elk heeft zijn eigen dashboard, eigen MCP-endpoint en eigen runtime context.

Een globale “bevraag al mijn Aspire-apps”-view bestaat hier niet.


Wat dit verandert

De vorige post maakte de blueprint van Aspire leesbaar voor Claude Code.

Deze maakt het runtime-gedrag bevraagbaar.

Samen dekken ze beide helften van hetzelfde plaatje:

  • de architectuur zoals geschreven
  • de architectuur zoals die zich gedraagt terwijl ze draait

Voor mijn Grouply-werk werd de loop strakker.

Minder tab-switchen. Minder stacktraces kopiëren. Minder rondes van:

Even de context geven voor deze vraag.

In plaats daarvan kan ik directe vragen stellen over het draaiende systeem en het draaiende systeem zelf de context laten leveren.

Het is geen revolutie. Het Aspire dashboard ontsluit het meeste hiervan al. De belangrijke verandering is dat de frictie tussen nadenken over een probleem en de assistant de relevante runtime context geven merkbaar afneemt.

Draai je een recente Aspire-versie, probeer dan:

aspire agent init

of, als je CLI dat commando nog niet kent:

aspire mcp init

Start dan de AppHost en vraag Claude Code:

Laat errors zien van de afgelopen 10 minuten over alle services heen.

De eerste keer dat je een scoped, gestructureerd antwoord uit runtime data terugkrijgt, is de aantrekkingskracht duidelijk.

De blueprint zit in de AppHost.
Het gedrag zit in het dashboard.
Nu zijn beide één prompt verderop.