Scaling AI-Assisted Development: The Intelligent Scaffolding Approach
From MVP to Mature: Where AI Assistance Breaks Down
When you're building an MVP, AI coding assistants feel like magic. "Build me a login page"—boom, it's done. "Add a database connection"—perfect. But as your project evolves from 100 lines to 100,000 lines, something changes:
Phase 1: The Honeymoon (0-10K lines)
- AI writes everything from scratch ✓
- No conventions needed ✓
- Pure productivity ✓
Phase 2: Pattern Emergence (10K-50K lines)
- You notice inconsistencies ⚠️
- "Why does this component use hooks differently?"
- "Didn't we already build this pattern?"
Phase 3: The Documentation Spiral (50K+ lines)
- Custom instructions get longer and longer
.md
files multiply like rabbits- Context window maxes out ✗
- AI "forgets" your conventions ✗
Sound familiar? This is where traditional AI assistance hits a wall.
My Personal Journey (The Failed Experiments)
I maintain a large monorepo with frontend apps (Next.js, TanStack Start), backend APIs (Hono.js, FastAPI, Lambda, etc...), shared packages, and infrastructure—all in one place. Over the years, I've built reusable design systems, theming, deployment patterns, and coding standards. Getting AI to respect these patterns? That was the challenge.
Attempt 1: The CLAUDE.md Approach
Like many developers, I started with comprehensive CLAUDE.md files referencing documentation via @
:
- Project Structure
- Coding Standards
- Technology Stack
- Conventions
- Style System
- Development Process
Result? Even with token-efficient documentation, I couldn't cover all design patterns and coding standards in a multi-language monorepo. AI still made mistakes.
Attempt 2: Per-Directory CLAUDE.md Files
"Maybe collocated instructions work better?" I created 50+ CLAUDE.md files for different apps, APIs, and packages.
Result? Slightly better when the collocated CLAUDE.md loaded in context (which didn't always happen). But the real issue: I only had ~10 distinct patterns across the entire monorepo. Maintaining 50+ instruction files for 10 patterns? Nightmare.
Attempt 3: Autonomous Workflows
I set up an autonomous workflow (PRD → code → lint + test → fix loop) to build internal libraries.
Result? Oh man. I spent way more time removing code and fixing bugs than if I'd just coded it myself. No matter how many times I updated CLAUDE.md, the issues persisted.
The Three Pain Points
1. Inconsistency Across Codebase
// File 1: AI generated last week
export function UserProfile({ user }: Props) {
return <div className="user-profile">...</div>
}
// File 2: AI generated yesterday
export const ProfileCard = (props: ProfileProps) => {
return <div className={styles.profile}>...</div>
}
// File 3: AI generated today
function UserCard({ userData }: UserCardProps) {
return <div className="profile-card">...</div>
}
Three different patterns for the same concept. All technically correct. All maintenance nightmares.
2. Context Window Overload
Your .md
documentation grows from this:
# Conventions
- Use functional components
- Use TypeScript
To this monstrosity:
# Conventions (120 pages)
## Components
- Use functional components
- Props interface must be exported
- Use PascalCase for component names
- Export from index.ts barrel files
- Tests in same directory with .test.tsx
- Storybook stories with .stories.tsx
...
(118 more pages)
Eventually, even AI can't keep up.
3. Pattern Recreation Waste
How many times have you watched AI recreate the same pattern?
- Authenticated API routes with similar structure
- Form components with validation logic
- Database models with timestamp fields
- Service classes that all need the same base configuration
Each time slightly different. Each time you review, fix, standardize. Hours wasted on work already done.
What is Intelligent Scaffolding?
Instead of fighting these problems with longer instructions, we need a fundamental shift: teach AI to use templates, not just write code.
This is where the AI Code Toolkit and @agiflowai/scaffold-mcp
come in.
How It Works: The scaffolding approach leverages MCP (Model Context Protocol) to expose template generation as a tool that AI agents can call. It uses structured output (JSON Schema validation) for the initial code generation, ensuring variables are properly typed and validated. This generated code then serves as guided generation for the LLM—providing a solid foundation that follows your patterns, which the AI can then enhance with context-specific logic. Think of it as "fill-in-the-blanks" coding: the structure is guaranteed consistent, while the AI adds intelligence where it matters.
The Three Core Pillars
As projects evolve from MVP to mature state, they develop patterns, conventions, and opinionated approaches. Unfortunately, custom instructions (prompts) alone don't always ensure coding agents follow your requirements. This toolkit provides three essential building blocks:
Pillar 1: Scaffolding Templates
Scaffolding ensures standardization. Combining templating with LLMs generates code that follows your internal conventions while reducing template maintenance effort. Think of it as giving AI a proven blueprint instead of asking it to improvise every time.
Pillar 2: Architecture + Design Patterns
As projects grow, convention becomes more important than configuration. Frameworks like Ruby on Rails and Angular demonstrate how opinionated approaches make code easier to find and understand—the same principle applies to coding agents. These are the pre-steps that guide the AI before writing code.
Pillar 3: Rules
Think of architecture and design patterns as pre-steps to guide the coding agent, and rules as post-checks to enforce your processes. Rules can be quantitative or qualitative, providing programmatic validation of agent outputs.
Why MCP (Model Context Protocol)?
The @agiflowai/scaffold-mcp
package is an MCP server, which means:
- ✅ Agent-agnostic: Works with Claude Code, Cursor, or any MCP-compatible tool
- ✅ Tech stack agnostic: Works with any framework - Next.js, Vite React, or your custom setup
- ✅ Multiple modes: MCP server mode (stdio/HTTP/SSE) and standalone CLI mode
- ✅ Always available: AI can use it just like any other tool, or you can script deterministic workflows with CLI commands
How to Use Scaffolding in Your Daily Workflow
Let's get practical. Here's how scaffolding transforms your daily development.
Scenario 1: Starting a New Project
Without scaffolding:
You: "Create a new Next.js 15 project with TypeScript"
AI: *generates 50 files*
You: "Wait, why is the config different from our other projects?"
You: "Can you add our standard ESLint config?"
You: "Actually, use our Tailwind setup..."
*30 minutes of back-and-forth*
With scaffolding:
# See what templates are available
scaffold-mcp boilerplate list
# Output:
# - nextjs-15-boilerplate: Next.js 15 with App Router, TypeScript, Tailwind
# - react-vite-boilerplate: React + Vite + TypeScript
# - nodejs-api-boilerplate: Node.js API with Express
# Create project with your exact conventions
scaffold-mcp boilerplate create nextjs-15-boilerplate \
--vars '{"projectName":"user-dashboard","packageName":"@myorg/user-dashboard"}'
# ✓ Complete project structure created
# ✓ All following your team's conventions
# ✓ Minutes instead of hours
Using Claude Code with MCP: Simply say: "Create a new Next.js project called user-dashboard"
Claude sees the scaffold-mcp MCP server, calls the use-boilerplate
tool, and creates your project—perfectly consistent with your other projects.
Scenario 2: Adding Features to Existing Projects
Without scaffolding:
You: "Add a new dashboard page"
AI: *creates page with different structure than existing pages*
You: "No, use the same layout as our other pages"
AI: *creates new layout that doesn't match*
You: "Just copy the pattern from /app/users/page.tsx"
*More back-and-forth*
With scaffolding:
# What features can I add?
scaffold-mcp scaffold list ./apps/my-app
# Output:
# - scaffold-nextjs-page: Add a new App Router page
# - scaffold-react-component: Add a React component with tests
# - scaffold-api-route: Add an API endpoint
# Add a page that matches your exact pattern
scaffold-mcp scaffold add scaffold-nextjs-page \
--project ./apps/my-app \
--vars '{"pageTitle":"User Dashboard","nextjsPagePath":"/dashboard"}'
# ✓ src/app/dashboard/page.tsx created
# ✓ Matches existing page structure
# ✓ Includes your standard metadata
# ✓ Ready to use
Using Claude Code: "Add a dashboard page to my app"
Claude uses the scaffold-mcp to ensure the new page matches your existing pages perfectly.
Scenario 3: Team Consistency
The Problem:
- Developer A creates components one way
- Developer B creates them differently
- AI learns from both and creates a third way
- Code reviews become style debates
The Solution: Everyone (including AI) uses the same templates:
# New team member day 1:
scaffold-mcp scaffold add scaffold-react-component \
--project ./src \
--vars '{"componentName":"UserCard","withTests":true}'
# Output matches team conventions perfectly
# No debate, no review comments about style
# Just works
Creating Your Own Templates
Now here's where it gets powerful: you can create custom templates that encode your team's exact conventions.
Step 1: Installation and Setup
# Install the package
npm install -g @agiflowai/scaffold-mcp
# or
pnpm install @agiflowai/scaffold-mcp
# Initialize your templates folder
scaffold-mcp init
# Or specify a custom path
scaffold-mcp init --path ./my-templates
# This creates:
# templates/
# └── (ready for your templates)
Step 2: Enable Admin Tools
To create and manage templates, enable admin mode in your Claude Code config:
{
"mcpServers": {
"scaffold-mcp": {
"command": "npx",
"args": ["-y", "@agiflowai/scaffold-mcp", "mcp-serve", "--admin-enable"]
}
}
}
Now restart Claude Code. You now have access to template creation tools.
Step 3: Create Your First Template
Let's create a template for your team's standard React component pattern.
Tell Claude:
"I want to create a new boilerplate template called 'react-component-library'
in a template folder called 'my-react-template'. It should create React
components in the packages/ folder with this structure:
- Component in PascalCase
- Storybook story file
- Test file
- Index barrel export"
Claude will use the admin tool generate-boilerplate
:
This creates a scaffold.yaml
configuration in your template folder with the proper structure:
# templates/my-react-template/scaffold.yaml
boilerplate:
name: react-component-library
description: React component library with Storybook and tests
targetFolder: packages
variables_schema:
type: object
properties:
componentName:
type: string
pattern: "^[A-Z][a-zA-Z0-9]*$"
description: Component name in PascalCase
example: Button
withStorybook:
type: boolean
description: Include Storybook story
default: true
required:
- componentName
additionalProperties: false
includes:
- src/{{ componentName }}/{{ componentName }}.tsx
- src/{{ componentName }}/{{ componentName }}.stories.tsx?withStorybook=true
- src/{{ componentName }}/{{ componentName }}.test.tsx
- src/{{ componentName }}/index.ts
instruction: |
Component created in packages/ following team conventions
Step 4: Create Template Files
Now create the actual template content:
Tell Claude:
"Create the component template file that uses Tailwind CSS and exports
a typed Props interface"
Claude uses generate-boilerplate-file
:
This creates actual template files with the .liquid
extension. The .liquid
extension tells scaffold-mcp to process variable replacement, and it's automatically stripped in the output.
File: templates/my-react-template/src/{{ componentName }}/{{ componentName }}.tsx.liquid
{% comment %}
This component follows our team's component pattern with Tailwind CSS
{% endcomment %}
import React from 'react';
export interface {{ componentName }}Props {
/**
* The content of the component
*/
children?: React.ReactNode;
/**
* Additional CSS classes
*/
className?: string;
}
/**
* {{ componentName }} component
*
* @example
* <{{ componentName }}>Hello World</{{ componentName }}>
*/
export const {{ componentName }}: React.FC<{{ componentName }}Props> = ({
children,
className = ''
}) => {
return (
<div className={`{{ componentName | kebabCase }} ${className}`}>
{children}
</div>
);
};
Understanding Liquid Template Syntax:
{{ componentName }}
- Variable interpolation (gets replaced with actual value){{ componentName | kebabCase }}
- Variable with filter transformation (MyButton → my-button).liquid
extension - Tells scaffold-mcp to process this file and strip the extension{% comment %}...{% endcomment %}
- Comments (won't appear in output)?withStorybook=true
in includes - Conditional include (only if withStorybook is true)
Step 5: Create Additional Template Files
Storybook story:
// File: templates/my-react-template/src/{{ componentName }}/{{ componentName }}.stories.tsx.liquid
import type { Meta, StoryObj } from '@storybook/react';
import { {{ componentName }} } from './{{ componentName }}';
const meta: Meta<typeof {{ componentName }}> = {
title: 'Components/{{ componentName }}',
component: {{ componentName }},
tags: ['autodocs'],
};
export default meta;
type Story = StoryObj<typeof {{ componentName }}>;
export const Default: Story = {
args: {
children: '{{ componentName }} content',
},
};
Test file:
// File: templates/my-react-template/src/{{ componentName }}/{{ componentName }}.test.tsx.liquid
import { render, screen } from '@testing-library/react';
import { {{ componentName }} } from './{{ componentName }}';
describe('{{ componentName }}', () => {
it('renders children correctly', () => {
render(<{{ componentName }}>Test content</{{ componentName }}>);
expect(screen.getByText('Test content')).toBeInTheDocument();
});
it('applies custom className', () => {
const { container } = render(
<{{ componentName }} className="custom-class">Content</{{ componentName }}>
);
expect(container.firstChild).toHaveClass('custom-class');
});
});
Index barrel export:
// File: templates/my-react-template/src/{{ componentName }}/index.ts.liquid
export { {{ componentName }} } from './{{ componentName }}';
export type { {{ componentName }}Props } from './{{ componentName }}';
Step 6: Use Your New Template
# Now anyone can use your template:
scaffold-mcp boilerplate create react-component-library \
--vars '{"componentName":"Button","withStorybook":true}'
# Output:
# ✓ Created packages/Button/Button.tsx
# ✓ Created packages/Button/Button.stories.tsx
# ✓ Created packages/Button/Button.test.tsx
# ✓ Created packages/Button/index.ts
#
# All files follow your exact conventions!
Or with Claude Code:
"Create a new Button component using our component library template"
Step 7: Add Feature Scaffolds
Templates can also add features to existing projects:
Tell Claude:
"Add a feature scaffold to my-react-template that adds a test file
to an existing component"
Claude uses generate-feature-scaffold
:
This adds a new feature configuration to your scaffold.yaml
:
# Added to templates/my-react-template/scaffold.yaml
features:
- name: add-component-tests
description: Add test file to existing component
variables_schema:
type: object
properties:
componentName:
type: string
description: Existing component name
example: Button
required:
- componentName
additionalProperties: false
includes:
- src/{{ componentName }}/{{ componentName }}.test.tsx
# File patterns this feature works with (for documentation)
patterns:
- src/**/Component.tsx
Now you can retroactively add tests:
scaffold-mcp scaffold add add-component-tests \
--project ./packages/Card \
--vars '{"componentName":"Card"}'
Advanced: Custom Generators
For complex scenarios, you can write TypeScript generators with full programmatic control.
When to Use Custom Generators
Use a custom generator when you need:
- Complex path transformations: Converting
/dashboard/users/[id]
into nested folders - Project analysis: Reading existing files to make decisions
- Dynamic file generation: Creating variable numbers of files
- Custom validation: Logic beyond JSON Schema
Basic Generator Example
// templates/my-template/generators/pageGenerator.ts
import {
GeneratorContext,
ScaffoldResult,
ScaffoldProcessingService
} from '@agiflowai/scaffold-generator';
import path from 'node:path';
interface PageVariables {
pagePath: string;
pageTitle: string;
withLayout?: boolean;
}
const generate = async (context: GeneratorContext): Promise<ScaffoldResult> => {
const {
variables,
fileSystem,
variableReplacer,
templatePath,
targetPath
} = context;
const vars = variables as PageVariables;
// Custom logic: Convert /dashboard/users to nested folders
const segments = vars.pagePath.split('/').filter(Boolean);
const pageFolderPath = path.join(targetPath, 'src/app', ...segments);
// Check if route already exists
const exists = await fileSystem.exists(path.join(pageFolderPath, 'page.tsx'));
if (exists) {
return {
success: false,
message: `Page already exists at ${vars.pagePath}`
};
}
// Process template files
const processingService = new ScaffoldProcessingService(
fileSystem,
variableReplacer
);
const createdFiles: string[] = [];
// Copy page.tsx template
await processingService.copyAndProcess(
path.join(templatePath, 'src/page/page.tsx.liquid'),
path.join(pageFolderPath, 'page.tsx'),
variables,
createdFiles
);
// Conditionally copy layout.tsx
if (vars.withLayout) {
await processingService.copyAndProcess(
path.join(templatePath, 'src/page/layout.tsx.liquid'),
path.join(pageFolderPath, 'layout.tsx'),
variables,
createdFiles
);
}
return {
success: true,
message: `Successfully created page at /${vars.pagePath}`,
createdFiles
};
};
export default generate;
Reference your generator in scaffold.yaml
:
features:
- name: scaffold-nextjs-page
description: Add a new Next.js App Router page
generator: pageGenerator.ts # ← Use custom generator
variables_schema:
type: object
properties:
pagePath:
type: string
description: "Page route path (e.g., 'dashboard/users')"
example: "dashboard/users"
pageTitle:
type: string
description: "Page title for metadata"
withLayout:
type: boolean
description: "Include layout.tsx file"
default: false
required:
- pagePath
- pageTitle
Real-World Results
Organizations using scaffold-mcp report dramatic improvements:
Before Scaffolding
- Setup time: 2-3 hours per new project
- Code consistency: 60-70% (many variations)
- Pattern reuse: Manual copy-paste with modifications
- Review time: 30-45 minutes per component (style debates)
- Onboarding: 2 weeks to learn team conventions
After Scaffolding
- Setup time: 2-3 minutes per new project
- Code consistency: 100% (enforced by templates)
- Pattern reuse: Automated with templates
- Review time: 5-10 minutes per component (logic only)
- Onboarding: 2 days with template documentation
Net result: 10x faster project initialization, zero convention debates, consistent quality.
Best Practices
1. Start with Provided Templates, Customize Gradually
Don't try to create perfect templates on day one:
# Week 1: Use provided templates
scaffold-mcp add --name nextjs-15 --url https://github.com/AgiFlow/aicode-toolkit
# Week 2-4: Observe what you change repeatedly
# Week 5+: Create custom templates for your modifications
2. Use Liquid Filters for Consistency
Scaffold-mcp provides powerful filters for case transformations:
{% comment %}
✅ Good: Ensure consistent casing with filters
Available filters: pascalCase, camelCase, kebabCase, snakeCase, upperCase, pluralize, singularize
{% endcomment %}
export const {{ componentName | pascalCase }} = () => {
const className = "{{ componentName | kebabCase }}";
const constant = {{ componentName | upperCase }}_VALUE;
};
{% comment %}
❌ Bad: Rely on user input casing
{% endcomment %}
export const {{ componentName }} = () => {
const className = "{{ componentName }}";
};
3. Validate with JSON Schema
# ✅ Good: Enforce format
properties:
componentName:
type: string
pattern: "^[A-Z][a-zA-Z0-9]*$" # Must be PascalCase
example: "UserCard"
# ❌ Bad: Accept anything
properties:
componentName:
type: string
4. Document with Instruction Fields
instruction: |
Component created successfully!
Files created:
- {{ componentName }}.tsx: Main component
- {{ componentName }}.test.tsx: Test file
- {{ componentName }}.stories.tsx: Storybook story
Next steps:
1. Run `pnpm test` to verify tests pass
2. Run `pnpm storybook` to view component
3. Import in your app: import { {{ componentName }} } from '@/components/{{ componentName }}'
Design patterns:
- Component uses Tailwind CSS utility classes
- Props interface is exported for reuse
- Component is fully typed with TypeScript
5. Test with Different Variable Combinations
Create test cases in your template README:
# Test Case 1: Minimal
variables:
componentName: Button
# Test Case 2: All features
variables:
componentName: UserProfile
withTests: true
withStorybook: true
withStyles: true
# Test Case 3: Edge cases
variables:
componentName: My-Component-Name # Tests kebabCase filter
description: "" # Tests empty string handling
Getting Started Today
Ready to transform your AI-assisted development?
Quick Start (5 minutes)
# 1. Install globally
npm install -g @agiflowai/scaffold-mcp
# or locally in your project
pnpm install @agiflowai/scaffold-mcp
# 2. Initialize templates folder
scaffold-mcp init
# 3. Add a template from repository (optional)
scaffold-mcp add --name nextjs-15 --url https://github.com/AgiFlow/aicode-toolkit
# 4. List available boilerplates
scaffold-mcp boilerplate list
# 5. Create a project
scaffold-mcp boilerplate create <name> --vars '{"projectName":"my-app"}'
Claude Code Setup (2 minutes)
Add to your Claude Code config:
{
"mcpServers": {
"scaffold-mcp": {
"command": "npx",
"args": ["-y", "@agiflowai/scaffold-mcp", "mcp-serve"]
}
}
}
Restart Claude Code and say: "What scaffolding templates are available?"
For Template Creators
Enable admin tools:
{
"mcpServers": {
"scaffold-mcp": {
"command": "npx",
"args": ["-y", "@agiflowai/scaffold-mcp", "mcp-serve", "--admin-enable"]
}
}
}
Then tell Claude: "Help me create a new boilerplate template for my team's component pattern"
The Path Forward
The future of AI-assisted development isn't about AI writing more code—it's about AI writing the right code, consistently, following your conventions.
Intelligent scaffolding transforms AI from a tool that generates code into a teammate that understands and respects your project's architecture, patterns, and conventions.
Three Levels of Adoption
Level 1: User (Start here)
- Use existing templates from the community
- 10x faster project setup
- Guaranteed consistency
Level 2: Customizer (Next step)
- Adapt templates to your team's conventions
- Encode your patterns once, reuse forever
- Zero convention debates
Level 3: Creator (Advanced)
- Build custom templates for your tech stack
- Advanced generators for complex workflows
- Share templates across your organization
The Bottom Line
Stop fighting with AI over conventions. Stop reviewing the same style issues. Stop recreating the same patterns.
Start with templates. Scale with scaffolding.
Resources
- GitHub Repository: github.com/AgiFlow/aicode-toolkit
- NPM Package: @agiflowai/scaffold-mcp
- Documentation:
---
"The best code is the code you don't have to write. But when you do write it, scaffolding ensures you write it right the first time—every time."
Ready to scale your AI-assisted development? Install @agiflowai/scaffold-mcp
and start building with consistency today.