Skip to main content
skill CX Codex

aspireify — Claude Code skill: wire an Aspire AppHost

The skill scans your repo, proposes a resource graph and turns a bare AppHost into a working one. Useful right after aspire init.

Install into Claude Code · Skills CLI (Vercel) All methods and platforms ↓
npx skills add microsoft/aspire-skills --skill aspireify -a claude-code
Open on GitHub

What aspireify does

aspire init drops a skeleton. aspireify makes it work: it scans the repo, proposes a resource graph, edits C#, file-based C# or TypeScript AppHosts, wires Aspire.ServiceDefaults and OTel, then validates with aspire start. After a clean start it deactivates itself.

You need .NET 10.0 SDK for a C# AppHost, or Node.js ^20.19.0, ^22.13.0 or >=24 for a TypeScript AppHost, plus the Aspire CLI. The skeleton must already exist — aspire.config.json and an AppHost stub. Before writing code the skill checks API shape with aspire docs api search and aspire docs get, because C# and TypeScript AppHost APIs differ.

One hard rule: it refuses to edit anything under .aspire/modules/ in a TypeScript AppHost. That directory is generated from the installed integration package graph and gets regenerated by aspire add, aspire restore and startup. Edits there are lost, so changes belong in apphost.mts or the legacy apphost.ts. If .agents/skills/aspireify/SKILL.md exists in the project, the skill warns you and defers to that local copy.

What it helps with

  • build a resource graph after aspire init
  • add Postgres, Redis, RabbitMQ or Mongo to the AppHost
  • connect a frontend to the API with WithReference
  • repair a TypeScript AppHost toolchain
  • move connection strings and keys out of .env into AppHost parameters

Example prompts for the agent

  • scan the repo and propose a resource graph for the AppHost
  • add Postgres and Redis to the AppHost and wire them to the API
  • connect the frontend to the API and validate with aspire start

How to install aspireify

1Skills CLI (Vercel)

npx skills add microsoft/aspire-skills --skill aspireify -a claude-code

2Manually: put the files in place

~/.claude/skills/aspireify/SKILL.md

Copy the whole skill folder; for one project use .claude/skills/aspireify/

Step by step, with commands for each platform: How to install skills in Claude Code: five ways

Questions about aspireify

How do I install aspireify in Claude?

Run npx skills add microsoft/aspire-skills --skill aspireify -a claude-code. The skill lands at ~/.claude/skills/aspireify/SKILL.md.

What does aspireify do?

It scans the repo, proposes a resource graph, edits the AppHost, wires Aspire.ServiceDefaults and OTel, and validates with aspire start. It deactivates after a clean start.

Can I edit files under .aspire/modules/?

No. That directory is generated from the package graph and regenerated by aspire add, aspire restore and startup. Put changes in apphost.mts or apphost.ts.

Source text

SKILL.md · MIT
---
name: aspireify
description: >-
  **WORKFLOW SKILL** - Wire Aspire AppHosts or repair TypeScript AppHost toolchains.
  Scans the repo, proposes a resource graph, edits C#, file-based C#, or TypeScript
  AppHosts, wires ServiceDefaults + OTel, validates with `aspire start`, then stops.
  USE FOR: wire/scaffold AppHost, add Postgres/Redis/Rabbit/Mongo, connect frontend
  to API, after `aspire init`, AddNextJsApp, AddViteApp, WithBrowserLogs, WithTerminal,
  Interaction Service, command arguments, apphost.cs, apphost.mts, unified
  withEnvironment, .aspire/modules refusal, config/secrets,
  TS dependency restore, pnpm/yarn/bun, or Yarn Classic.
  DO NOT USE FOR: skeleton drop (aspire-init), lifecycle-only start/stop/wait/restart
  or `aspire update --migrate` (aspire-orchestration), publish/deploy/destroy
  (aspire-deployment), logs/traces (aspire-monitoring).
  INVOKES: aspire CLI, AppHost source edits, ServiceDefaults wiring.
  FOR SINGLE OPERATIONS: Run `aspire add PACKAGE` directly for a one-off integration.
license: MIT
metadata:
  author: Microsoft
  version: "0.0.4"
---

# Aspireify

> **One-time wiring skill.** `aspire init` drops a skeleton; `aspireify` turns
> that skeleton into a working AppHost by scanning the repo, proposing a resource
> graph, editing the AppHost, wiring `Aspire.ServiceDefaults`, and validating end
> to end. Self-deactivates after a clean `aspire start`.

## 🚫 Hard Refusal: Never Edit `.aspire/modules/`

> ⛔ **REFUSE** any request to edit, modify, change, open-for-edit, or "tweak" files
> inside `.aspire/modules/` of a TypeScript AppHost. This directory is **generated** from
> the installed Aspire integration package graph. It is regenerated by `aspire add`,
> `aspire restore`, and startup when package changes require it.
>
> If a user asks to edit something in `.aspire/modules/` (e.g., `.aspire/modules/postgres.module.ts`),
> the correct response is:
>
> 1. **Refuse the edit** with a clear "I won't edit `.aspire/modules/`" statement.
> 2. **Explain** that `.aspire/modules/` is generated and any changes are clobbered.
> 3. **Redirect** the requested change to the configured AppHost entry point:
>    current `apphost.mts`, or legacy `apphost.ts`.
> 4. If the user wants a new integration, suggest `aspire add <package>`; if they want
>    to change configuration, show the equivalent edit in the AppHost entry point.

| ❌ Wrong | ✅ Right |
|----------|---------|
| Open `.aspire/modules/postgres.module.ts` and tweak the connection options | Edit `apphost.mts` and change `addPostgres('pg', { ... })` options there |
| Modify a generated `.aspire/modules/*` file directly | Re-run `aspire add <package>` after updating `apphost.mts` |
| Comment out a line in `.aspire/modules/` to disable a resource | Remove or guard the resource declaration in `apphost.mts` |

This rule applies even if the user insists, even for "one-line" changes, even for
"just to test something." The TS AppHost regenerates `.aspire/modules/` deterministically;
edits are unrecoverable noise.

## Guiding Principles

### Minimize changes to the user's code

Adapt the AppHost to fit the app, not the other way around. Prefer `WithEnvironment()`
to match existing environment variable names, Aspire-managed ports over fixed ports,
and 1:1 Docker Compose mapping before optimizing. Do not restructure directories,
rename files, or change build scripts unless the user explicitly chooses that tradeoff.

### Surface tradeoffs; do not decide silently

When a small code change unlocks better Aspire integration, present both options:
the zero-code-change mapping and the small-change version that enables `WithReference`,
health checks, service discovery, dynamic ports, or dashboard telemetry. Ask which
approach the user wants, then implement that choice without complaint.

### Verify APIs before writing AppHost code

Use `aspire docs search <topic>` and `aspire docs get <slug>` for workflow guidance.
Use `aspire docs api search <query> --language csharp|typescript` and
`aspire docs api get <id>` for API shape. Use `aspire integration list/search` to
find integrations before `aspire add`. Do not invent packages, methods, overloads,
or command shapes; C# and TypeScript AppHost APIs differ.

### Keep configuration visible in the AppHost

Scan `.env`, `.env.local`, `.env.development`, `secrets.json.example`,
`<UserSecretsId>`, and setup scripts. Propose migrating values into AppHost parameters:
connection strings become Aspire resources, API keys/tokens become secret parameters,
and non-secret config becomes plain parameters or `WithEnvironment()` values. Never
delete `.env` files or remove existing `UserSecretsId` entries without explicit user
approval because non-Aspire workflows may still depend on them.

### Local development first

This skill optimizes local development, not production deployment. Prefer persistent
container lifetimes and data volumes for databases/caches, use HTTPS endpoints by
default, pass endpoint references instead of hardcoded URLs, and model external SaaS
URLs/API keys as parameters so they are visible in the dashboard.

### Redis TLS edge case

Aspire can automatically provision TLS certificates for container resources. If Redis
health checks fail with SSL/TLS handshake errors, do not fall back to `AddContainer()`.
Use `WithoutHttpsCertificate()` on the Redis resource when the consuming app expects
plain Redis.

## Project-Local Override

If `.agents/skills/aspireify/SKILL.md` exists (installed by `aspire init` or
`aspire agent init --skills aspireify`), **warn the user** that a project-local
copy is present and **defer to it**. The plugin version is the fallback.

```
⚠️ Project-local .agents/skills/aspireify/SKILL.md detected — deferring to it.
```

## Prerequisites

| Requirement | Install |
|-------------|---------|
| .NET 10.0 SDK (C# AppHost) | https://dotnet.microsoft.com/download |
| Node.js `^20.19.0`, `^22.13.0`, or `>=24` (TS AppHost) | https://nodejs.org |
| Aspire CLI | `npm install -g @microsoft/aspire-cli`, install script, or `dotnet tool install -g Aspire.Cli` |
| Skeleton already dropped | `aspire init` produced `aspire.config.json` + AppHost stub |

## Detection — When to Activate

Activate when ANY signal is present **AND** the AppHost is unwired (no resources
declared beyond the stub):

| Signal | How to Detect | Confidence |
|--------|---------------|------------|
| Skeleton just dropped | `aspire init` just ran in this session | ✅ Definitive |
| Empty AppHost stub | `apphost.cs` / `Program.cs` / current `apphost.mts` (or legacy `apphost.ts`) only contains `Build().Run()` | ✅ Definitive |
| `aspire.config.json` without resources | Config present, AppHost has no `AddProject`/`addProject` | High |
| User asks to "wire" / "scaffold resource graph" | Verb match: wire, scaffold, integrate, hook up, add Postgres/Redis/etc. | High |
| User asks "what next after aspire init" | Direct handoff request | ✅ Definitive |
| Existing repo with services + new AppHost | Repo has `.csproj`/`package.json` projects but AppHost references none | High |

If the AppHost already has wired resources and the user wants to **start/stop**
the app → `aspire-orchestration`. If the user wants to **deploy** → `aspire-deployment`.

## Language Support

| AppHost Style | Detection | Edit Target |
|---------------|-----------|-------------|
| **C# SDK-style** | `.csproj` containing `<Sdk Name="Aspire.AppHost.Sdk" />` | `Program.cs` (top-level statements) |
| **File-based C#** | `apphost.cs` with `#:sdk Aspire.AppHost.Sdk` and `#:package` directives | `apphost.cs` itself |
| **TypeScript** | Current `apphost.mts` or legacy `apphost.ts` with generated `.aspire/modules/` | Configured AppHost entry point only — **never edit `.aspire/modules/`** |

See [references/csharp-authoring.md](references/csharp-authoring.md) and
[references/typescript-authoring.md](references/typescript-authoring.md).

`aspire-orchestration` owns the CLI-driven migration from legacy `apphost.ts`. After it
updates packages and migration artifacts, return to aspireify only when the user still
needs AppHost source wiring or authoring.

### TypeScript AppHost package managers

Dependency and toolchain failures belong to this skill. Before recommending a command,
inspect the AppHost directory and then its immediate eligible parent. Within each directory,
the first recognized marker wins in this order:

1. `packageManager` in `package.json` (`npm`, `pnpm`, `yarn`, or `bun`, optionally versioned)
2. `bun.lock`
3. `bun.lockb`
4. `pnpm-lock.yaml`
5. `yarn.lock`
6. `.yarnrc.yml`
7. `package-lock.json`

An AppHost-local marker takes precedence over every parent marker. If neither directory has
a recognized marker, Aspire defaults to npm. Report the selected manager and marker.

Use the selected manager only to repair dependencies or diagnose the toolchain. The resolver
commands are `npm install`, `bun install`, and `yarn install`; a generated brownfield pnpm
AppHost uses `pnpm install --ignore-workspace`, while other pnpm dependency installs use
`pnpm install`. Yarn Classic (`yarn@1...` or a v1 lockfile) is unsupported: stop and ask the
user to upgrade to Yarn 4+ or explicitly migrate to npm, pnpm, or Bun.

Do not change `packageManager` or create, replace, or regenerate a lockfile merely to
influence detection or switch managers. Preserve existing files until the user explicitly
chooses an upgrade or migration. Start the AppHost with `aspire start --non-interactive`,
not a raw package-manager launcher. See
[references/typescript-authoring.md](references/typescript-authoring.md) for the full
command matrix and resolver details.

## Workflow Phases

```
1. SCAN     → discover projects, services, dependencies, integration candidates
2. PROPOSE  → resource graph + integration list, confirm with user
3. EDIT     → wire AppHost, add ServiceDefaults + OTel + health checks
4. VALIDATE → aspire start --non-interactive → aspire wait <each resource>
5. DEACTIVATE → confirm clean start, hand off to aspire-orchestration
```

For the detailed, upstream-parity workflow, load these references before editing:

- [apphost-wiring.md](references/apphost-wiring.md) — full AppHost wiring workflow, API lookup, endpoint/parameter patterns, validation, solution updates, and cleanup.
- [docker-compose.md](references/docker-compose.md) — docker-compose migration, profiles, image mapping, ports, volumes, and `depends_on`.
- [full-solution-apphosts.md](references/full-solution-apphosts.md) — large solution triage, mixed SDK boundaries, solution membership, ServiceDefaults placement, and legacy host migration.
- [javascript-apps.md](references/javascript-apps.md) — JavaScript resource selection, workspace/monorepo package-manager handling, ports, scripts, and TS AppHost package config.
- [opentelemetry.md](references/opentelemetry.md) — optional Node.js, Python, and Go OpenTelemetry wiring for non-.NET services.

### 1. Scan

Walk the repo and inventory:

| What | How |
|------|-----|
| .NET projects | `find . -name '*.csproj' -not -path '*/bin/*' -not -path '*/obj/*'` |
| Node services | `find . -name 'package.json' -not -path '*/node_modules/*'` |
| Python services | `find . -name 'pyproject.toml' -o -name 'requirements.txt'` |
| Container deps in compose | `docker-compose.yml`, `compose.yaml` (Postgres? Redis? Rabbit?) |
| Connection strings | grep `appsettings*.json`, `.env*`, `config/*` for `Postgres`, `Redis`, `Mongo`, `RabbitMQ`, `Cosmos`, `ServiceBus` |
| Integration packages | `dotnet list package` per project; package.json `dependencies` |
| Existing endpoints | hardcoded ports in `launchSettings.json`, `next.config.js`, `vite.config.ts` |

Full heuristics in [references/scan-and-propose.md](references/scan-and-propose.md).

### 2. Propose

Present a resource graph **before editing**. Ask clarifying questions:

- "I see Postgres in `docker-compose.yml` — should I model it as `AddPostgres('db')` or use Azure Database for PostgreSQL?"
- "Your React app hardcodes `http://localhost:5000` — replace with Aspire service discovery (`endpoint.url`)?"
- "Your API has an `/admin` endpoint — exclude it from `WithReference()` so consumers don't see it?"

### 3. Edit

Apply the proposed graph. Use the right authoring style for the AppHost language.

### 4. Validate

```bash
aspire start --non-interactive --format Json
aspire wait <resource>          # repeat for each declared resource
aspire describe --format Json   # sanity check graph
```

Full validation flow + recovery in [references/validation.md](references/validation.md).

### 5. Self-Deactivate

After a clean `aspire start`, announce:

```
✅ AppHost wired and validated. Handing off to aspire-orchestration for
   day-to-day start/stop/wait. Aspireify is done.
```

## Integration Discovery Catalog

Map detected services → Aspire integrations. See
[references/scan-and-propose.md](references/scan-and-propose.md) for the full
catalog.

| Detected | C# | TS |
|----------|----|----|
| Postgres in compose / `Npgsql` package | `AddPostgres("pg").AddDatabase("db")` | `addPostgres('pg').addDatabase('db')` |
| Redis in compose / `StackExchange.Redis` | `AddRedis("cache")` | `addRedis('cache')` |
| RabbitMQ | `AddRabbitMQ("mq")` (v7 client w/ pub-sub tracing) | `addRabbitMQ('mq')` |
| MongoDB | `AddMongoDB("mongo")` | `addMongoDB('mongo')` |
| Cosmos DB | `AddAzureCosmosDB("cosmos")` | `addAzureCosmosDB('cosmos')` |
| Azure Service Bus | `AddAzureServiceBus("sb")` | `addAzureServiceBus('sb')` |
| Azure Cache for Redis (Entra) | `AddAzureRedis("cache")` (now GA) | `addAzureRedis('cache')` |
| Next.js frontend | `AddNextJsApp("web", "./web")` | `addNextJsApp('web', '../web')` |
| Vite SPA | `AddViteApp("web", "./web")` | `addViteApp('web', '../web')` |
| Plain Node app | `AddNodeApp("api", "server.js")` | `addNodeApp('api', 'server.js')` |

## Current Authoring Rules

| Rule | Why |
|------|-----|
| Use **unified `withEnvironment(name, value)`** in TS — never the deprecated per-kind helpers (`withEnvironmentEndpoint`, `withEnvironmentParameter`, etc.) | Single API handles all value kinds; per-kind helpers are deprecated |
| Use `AddNextJsApp` / `AddViteApp` over hand-rolled Dockerfiles for JS frontends | First-class lifecycle + `PublishAs*` integration |
| Use `PublishAsStaticWebsite` / `PublishAsNodeServer` / `PublishAsPackageScript` for JS publish | Replaces hand-rolled Dockerfiles; SPA → static, SSR Node → NodeServer, package-script SSR → PackageScript |
| Add `WithBrowserLogs()` to frontend resources for browser console + screenshots in dashboard | `Aspire.Hosting.Browsers` surfaces browser telemetry in the dashboard |
| Bind every resource to a compute environment with `WithComputeEnvironment(env)` when multiple environments exist | Multi-environment deploys require explicit binding |
| **Never edit `.aspire/modules/`** in TS AppHosts | Generated; edits get clobbered. Edit the configured `apphost.mts` (or legacy `apphost.ts`) only |
| Use `WithEndpoint("name", e => ...)` to update endpoints | Endpoint callbacks update existing endpoints rather than throwing on duplicates |
| Mark admin endpoints with `ExcludeReferenceEndpoint = true` | Prevents consumers from receiving admin URLs via `WithReference()` |
| Look up unfamiliar API: `aspire docs api search <query> --language csharp\|typescript` | Don't guess overloads or builder chains |
| Use context `.Services` / `await ctx.services().getInteractionService()` | `.ServiceProvider` is obsolete, and `ctx.services()` alone returns a services accessor |
| Use `AddConnectionString` for external connection strings | `PublishAsConnectionString` is obsolete |
| Check `IInteractionService.IsAvailable` before prompting | CLI-invoked commands may be noninteractive; prefer command arguments for dashboard + CLI input |
| Treat `WithTerminal()` as experimental | Suppress `ASPIRETERMINAL001`; do not generate removed `TerminalOptions.Shell` or TypeScript dimension options |
| Keep all Aspire SDK and `Aspire.Hosting.*` packages on the same release family | Mixed release families can fail at startup |
| Migrate GitHub Models integrations to Azure AI Foundry | `Aspire.Hosting.GitHub.Models` is deprecated and absent from integration discovery |
| Use `WithModule(RedisModules.*)` for Redis 8 modules | Prefer typed JSON, Search, Bloom Filter, and TimeSeries constants over raw module paths |
| Use Foundry `AsHostedAgent(...)` for hosted executable/container agents | Current Azure AI Foundry path replaces deprecated GitHub Models |

## C# vs TS Quick Reference

| Concept | C# | TypeScript |
|---------|----|------------|
| Builder | `var builder = DistributedApplication.CreateBuilder(args);` | `const builder = await createBuilder();` |
| Add project | `builder.AddProject<Projects.Api>("api")` (SDK) or `AddProject("api", "../Api/Api.csproj")` | `await builder.addProject('api', '../Api/Api.csproj')` |
| Wire env var (any value type) | `.WithEnvironment("KEY", value)` | `.withEnvironment('KEY', value)` ← unified API |
| Wait for dependency | `.WaitFor(db)` | `.waitFor(db)` |
| Pass connection | `.WithReference(db)` | `.withReference(db)` |
| External HTTP | `.WithExternalHttpEndpoints()` | `.withExternalHttpEndpoints()` |
| Endpoint expression | `api.GetEndpoint("http")` | `api.getEndpoint('http').url` / `.host` / `.port` |
| Build + run | `builder.Build().Run();` | `await builder.build().run();` |

## ServiceDefaults Wiring

Each project should call `builder.AddServiceDefaults();` to opt into OpenTelemetry,
health checks, and service discovery. Add the `Aspire.ServiceDefaults` project
reference (or NuGet for non-monorepo). See
[references/service-defaults.md](references/service-defaults.md).

## Endpoint & Reference Conventions

```csharp
// Public-facing API. Mark "admin" endpoint as not-for-consumers.
var api = builder.AddProject<Projects.Api>("api")
    .WithExternalHttpEndpoints()
    .WithEndpoint("admin", e => e.ExcludeReferenceEndpoint = true);

// Frontend wires the API via service discovery.
builder.AddNextJsApp("web", "./web")
    .WithReference(api)        // injects services__api__http and __https
    .WaitFor(api)
    .WithBrowserLogs();        // browser console + screenshots
```

## Validation & Recovery

| Symptom | Action |
|---------|--------|
| `aspire start` fails with build error | Fix code, re-run `aspire start` |
| File-lock errors during edit | Hand off to `aspire-orchestration` → `aspire stop` → retry |
| Resource missing from `aspire describe` | Re-run `aspire describe --include-hidden`; `aspire ps` is AppHost-level |
| TS AppHost change ignored | Confirm you edited the configured `apphost.mts` (or legacy `apphost.ts`), not `.aspire/modules/` |
| Mixed JSON output from `aspire start` | Strip non-JSON lines before parsing ([#15843](https://github.com/microsoft/aspire/issues/15843)) |

Full flow in [references/validation.md](references/validation.md).

## Handoff Rules

| Scenario | Route To |
|----------|----------|
| AppHost skeleton not yet dropped | → `aspire-init` skill |
| Day-to-day start/stop/wait/restart | → `aspire-orchestration` skill |
| Publish, deploy, destroy, pipeline steps | → `aspire-deployment` skill |
| Logs, traces, metrics, dashboard, browser log inspection | → `aspire-monitoring` skill |
| Deployed (Azure/AKS) app diagnostics | → `azure-diagnostics` skill (azure-skills) |

## Key Rules

- **Never overwrite existing files** — always augment or merge.
- **Ask before modifying service code**, especially OpenTelemetry and ServiceDefaults injection.
- **Respect existing project structure** — do not reorganize the repo.
- **If stuck, use `aspire doctor`** to diagnose environment issues.
- **Never hardcode URLs in `WithEnvironment` / `withEnvironment`** — pass endpoint references such as `api.GetEndpoint("http")` or `api.getEndpoint('http')` instead of string literals.
- **Never use `WithUrlForEndpoint` / `withUrlForEndpoint` to set `dev.localhost` URLs** — that API is only for dashboard display labels; `dev.localhost` belongs in AppHost launch/profile configuration.

## References

- [apphost-wiring.md](references/apphost-wiring.md) — Detailed AppHost wiring workflow and API lookup patterns
- [docker-compose.md](references/docker-compose.md) — Docker Compose migration patterns
- [full-solution-apphosts.md](references/full-solution-apphosts.md) — Large/full-solution AppHost guidance
- [javascript-apps.md](references/javascript-apps.md) — JavaScript/TypeScript app and workspace handling
- [opentelemetry.md](references/opentelemetry.md) — Non-.NET OpenTelemetry setup
- [scan-and-propose.md](references/scan-and-propose.md) — Repo scan heuristics + integration catalog
- [csharp-authoring.md](references/csharp-authoring.md) — C# AppHost patterns
- [typescript-authoring.md](references/typescript-authoring.md) — TS AppHost patterns + parity APIs
- [service-defaults.md](references/service-defaults.md) — Wire OTel, health checks, service discovery
- [validation.md](references/validation.md) — End-to-end validation + recovery
- [aspire-13-5-breaking-changes.md](https://github.com/microsoft/aspire-skills/blob/main/skills/aspire/references/aspire-13-5-breaking-changes.md) — Breaking changes introduced in 13.5 and fixes for affected releases

© Microsoft · MIT · microsoft/aspire-skills

Similar skills

Everything for DevOps and infrastructure →

From the same set3

Project sponsors

Companies that keep this analytics open
Gold sponsor

Impressions, clicks and code copies — open statistics. We keep the numbers public for the same reason NeBlask itself is public. Become a sponsor