# MCP Scaffold Workflow: DTOs + Controller

> **Reference for:** step-03-generate.md
> **Purpose:** Two-call MCP scaffold sequence with NavRoute integration
> **Calls:** `mcp__smartstack__scaffold_extension` for DTOs, then for Controller

---

## Two-Call Strategy

The skill uses TWO separate MCP calls (not one):

1. **First call:** Generate DTOs (Response, Create, Update)
2. **Second call:** Generate Controller with NavRoute attribute

**Why two calls?**
- The skill assumes Entity already exists (verified in step-01-analyze)
- Type "dto" generates DTOs without regenerating Entity
- Type "controller" generates Controller with NavRoute and DTO references

---

## Call 1: Generate DTOs

```javascript
const dtoCall = await mcp__smartstack__scaffold_extension({
  type: "dto",
  name: "{entityName}",  // PascalCase: "Product", "OrderItem"
  options: {
    navRoute: "{permission_path}",  // For proper namespace hierarchy
    schema: "core",
    dryRun: false
  }
});

// Expected output paths:
// - Application/{Application}/{Module}/DTOs/{entityName}ResponseDto.cs
// - Application/{Application}/{Module}/DTOs/Create{entityName}Dto.cs
// - Application/{Application}/{Module}/DTOs/Update{entityName}Dto.cs
```

**From response, extract:**
- `dtoCall.dtoFiles.response` - Response DTO path
- `dtoCall.dtoFiles.create` - Create DTO path
- `dtoCall.dtoFiles.update` - Update DTO path

### Error Handling: DTOs

```javascript
if (dtoCall.status === 'failed' || !dtoCall.dtoFiles) {
  console.error(`MCP DTO generation failed: ${dtoCall.error}`);
  console.log(`Suggested actions:`);
  console.log(`  1. Verify entity name is PascalCase (e.g., "Product", not "product")`);
  console.log(`  2. Verify entity properties are defined in DB`);
  console.log(`  3. Run /mcp healthcheck to verify MCP server connectivity`);
  STOP;  // Do NOT proceed to controller call
}
```

---

## Call 2: Generate Controller

```javascript
const controllerCall = await mcp__smartstack__scaffold_extension({
  type: "controller",
  name: "{entityName}",  // PascalCase from step 1
  options: {
    navRoute: "{permission_path}",  // MANDATORY for NavRoute attribute
    navRouteSuffix: null,             // Optional: e.g., "details" → [NavRoute("path", Suffix = "details")]
    customSegment: null,              // Optional: e.g., "user/dashboard" → [NavRoute("path", CustomSegment = "user/dashboard")]
    dryRun: false
  }
});

// Expected output:
// Api/Controllers/{Application}/{Module}/{entityName}Controller.cs with [NavRoute] attribute
// Uses MediatR (ISender _mediator), [Microsoft.AspNetCore.Authorization.Authorize] (fully qualified),
// [Tags], [Produces("application/json")], [RequirePermission] with Permissions constants,
// [ProducesResponseType] with StatusCodes.* constants including 401/403
```

**From response, extract:**
- `controllerCall.controllerFile` - Controller path
- `controllerCall.navRouteAttribute` - NavRoute value (should match permission_path)

### Error Handling: Controller

```javascript
if (controllerCall.status === 'failed' || !controllerCall.controllerFile) {
  console.error(`MCP Controller generation failed: ${controllerCall.error}`);
  console.log(`Note: DTOs were generated successfully (${dtoCall.dtoFiles.response})`);
  console.log(`Suggested actions:`);
  console.log(`  1. Verify navRoute format (e.g., "app.module.resource")`);
  console.log(`  2. Run /mcp healthcheck to verify MCP server connectivity`);
  console.log(`  3. Check that DTOs were created (they should exist from Call 1)`);
  STOP;  // Do NOT proceed to step-04
}
```

---

## NavRoute Validation

```javascript
// Verify NavRoute is present in controller
const controllerContent = readFile(controllerCall.controllerFile);

if (!controllerContent.includes('[NavRoute(')) {
  console.warn(`NavRoute attribute NOT FOUND in controller`);
  console.warn(`Expected: [NavRoute("${permission_path}")]`);
  console.warn(`Got: missing attribute`);
  console.warn(`\nWithout NavRoute:`);
  console.warn(`  ❌ Frontend routes will be hardcoded`);
  console.warn(`  ❌ No automatic permission sync`);
  console.warn(`  ❌ Manual navigation management required`);
  STOP;
}

// Extract actual NavRoute value
const navRouteMatch = controllerContent.match(/\[NavRoute\("([^"]+)"\)/);
const actualNavRoute = navRouteMatch ? navRouteMatch[1] : null;

if (actualNavRoute !== permission_path) {
  console.warn(`NavRoute mismatch:`);
  console.warn(`  Expected: "${permission_path}"`);
  console.warn(`  Got: "${actualNavRoute}"`);
  // Warning only — proceed
}

// Validate segment count
const segments = actualNavRoute ? actualNavRoute.split('.').length : 0;
if (segments < 2) {
  console.error(`BLOCKING: NavRoute "${actualNavRoute}" has only ${segments} segment(s) — minimum 2 required`);
  console.error(`  Format: "app.module" (2 segments) or "app.module.section" (3 segments)`);
  STOP;
}

// If entity is in a section subfolder, NavRoute must have 3+ segments
// Check: controller path has 3+ levels after Controllers/ → section-level
const controllerDir = controllerCall.controllerFile.replace(/[^/]*$/, '');
const depthAfterControllers = controllerDir.split('Controllers/')[1]?.split('/').filter(Boolean).length || 0;
if (depthAfterControllers >= 2 && segments < 3) {
  console.warn(`WARNING: Controller is in a section subfolder (depth=${depthAfterControllers})`);
  console.warn(`  but NavRoute "${actualNavRoute}" has only ${segments} segments`);
  console.warn(`  Expected 3 segments: "app.module.section"`);
  console.warn(`  A 2-segment NavRoute on a section controller causes API 404s`);
  // Warning — developer should verify and fix
}
```

---

## Success Output

```markdown
Generated Files:

| File | Path | Status |
|------|------|--------|
| DTO (Response) | {dtoResponsePath} | ✅ Created |
| DTO (Create) | {dtoCreatePath} | ✅ Created |
| DTO (Update) | {dtoUpdatePath} | ✅ Created |
| Controller | {controllerPath} | ✅ Created with [NavRoute] |

NavRoute Integration: ✅ Enabled
  - [NavRoute("{permission_path}")]
  - Frontend/backend sync active
  - Permission-based navigation ready

Next: Synchronizing permissions in step-04...
```

---

## Post-Generation Checks

```javascript
// 1. Verify all files created
const requiredFiles = [
  dtoCall.dtoFiles.response,
  dtoCall.dtoFiles.create,
  dtoCall.dtoFiles.update,
  controllerCall.controllerFile
];

for (const file of requiredFiles) {
  if (!fileExists(file)) {
    console.error(`File not found: ${file}`);
    STOP;
  }
}

// 2. Verify controller references DTOs correctly
const controllerContent = readFile(controllerCall.controllerFile);
const expectedDtoReferences = [
  `${entityName}ResponseDto`,
  `Create${entityName}Dto`,
  `Update${entityName}Dto`
];

for (const ref of expectedDtoReferences) {
  if (!controllerContent.includes(ref)) {
    console.warn(`DTO reference missing in controller: ${ref}`);
  }
}

// 3. Verify NavRoute matches permission_path
if (!controllerContent.includes(`NavRoute("${permission_path}")`)) {
  console.warn(`NavRoute value doesn't match permission_path`);
}

console.log(`✅ All checks passed — controller ready for step-04`);
```

---

## Fallback: Manual NavRoute

If MCP doesn't generate [NavRoute] attribute:

```csharp
// Manually add — REPLACE [Route] with [NavRoute], do NOT keep both (causes 404s):
[ApiController]
[NavRoute("app.module.resource")]
[Microsoft.AspNetCore.Authorization.Authorize]
[Produces("application/json")]
[Tags("{EntityNamePlural}")]
public class {EntityName}Controller : ControllerBase
{
  // ...
}
```

**CRITICAL:** Remove `[Route("api/...")]` if present — combining `[Route]` and `[NavRoute]` causes 404 errors.

This indicates MCP failure and should not be needed in normal operation.
