@agiflowai/scaffold-mcp 0.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +661 -0
- package/README.md +264 -0
- package/dist/ScaffoldConfigLoader-CI0T6zdG.js +142 -0
- package/dist/ScaffoldConfigLoader-DhthV6xq.js +3 -0
- package/dist/ScaffoldService-CDhYAUrp.js +3 -0
- package/dist/ScaffoldService-CnJ1nj1v.js +408 -0
- package/dist/TemplateService-CnxvhRVW.js +64 -0
- package/dist/TemplateService-PmTU3_On.js +3 -0
- package/dist/VariableReplacementService-Bq0GDhTo.js +65 -0
- package/dist/VariableReplacementService-CrxFJrqU.js +3 -0
- package/dist/index.js +2812 -0
- package/package.json +65 -0
package/README.md
ADDED
|
@@ -0,0 +1,264 @@
|
|
|
1
|
+
# @agiflowai/scaffold-mcp
|
|
2
|
+
|
|
3
|
+
A Model Context Protocol (MCP) server for scaffolding applications with boilerplate templates and feature generators. Supports multiple transport modes: **stdio**, **HTTP**, **SSE**, and **CLI**.
|
|
4
|
+
|
|
5
|
+
## Features
|
|
6
|
+
|
|
7
|
+
- **Boilerplate scaffolding**: Generate complete application structures from templates
|
|
8
|
+
- **Feature scaffolding**: Add features to existing projects with custom generators
|
|
9
|
+
- **Custom generators**: Template-specific TypeScript generators for advanced scaffolding logic
|
|
10
|
+
- **Liquid templating**: Use powerful templating engine for dynamic file generation
|
|
11
|
+
- **Variable replacement**: Customize generated code with context-aware variable substitution
|
|
12
|
+
- **Dynamic template discovery**: Automatically finds templates in your workspace
|
|
13
|
+
- **Template management**: Initialize templates folder and add templates from remote repositories
|
|
14
|
+
- **Multiple frameworks**: Support for Next.js, Vite React, and custom boilerplates
|
|
15
|
+
- **Multiple modes**: MCP server mode (stdio/HTTP/SSE) and standalone CLI mode
|
|
16
|
+
- **MCP integration**: Seamlessly works with Claude Desktop and other MCP-compatible clients
|
|
17
|
+
|
|
18
|
+
## Installation
|
|
19
|
+
|
|
20
|
+
```bash
|
|
21
|
+
pnpm install @agiflowai/scaffold-mcp
|
|
22
|
+
```
|
|
23
|
+
|
|
24
|
+
## Usage
|
|
25
|
+
|
|
26
|
+
### 1. MCP Server
|
|
27
|
+
|
|
28
|
+
Run scaffold-mcp as an MCP server to integrate with Claude Desktop or other MCP clients.
|
|
29
|
+
|
|
30
|
+
#### Starting the Server
|
|
31
|
+
|
|
32
|
+
```bash
|
|
33
|
+
# stdio transport (default) - for Claude Desktop
|
|
34
|
+
npx @agiflowai/scaffold-mcp mcp-serve
|
|
35
|
+
|
|
36
|
+
# HTTP transport - for web applications
|
|
37
|
+
npx @agiflowai/scaffold-mcp mcp-serve --type http --port 3000
|
|
38
|
+
|
|
39
|
+
# SSE transport - for legacy clients
|
|
40
|
+
npx @agiflowai/scaffold-mcp mcp-serve --type sse --port 3000
|
|
41
|
+
|
|
42
|
+
# Enable admin mode (template generation tools)
|
|
43
|
+
npx @agiflowai/scaffold-mcp mcp-serve --admin-enable
|
|
44
|
+
```
|
|
45
|
+
|
|
46
|
+
**Server Options:**
|
|
47
|
+
- `-t, --type <type>`: Transport type: `stdio`, `http`, or `sse` (default: `stdio`)
|
|
48
|
+
- `-p, --port <port>`: Port for HTTP/SSE servers (default: `3000`)
|
|
49
|
+
- `--host <host>`: Host to bind to for HTTP/SSE (default: `localhost`)
|
|
50
|
+
- `--admin-enable`: Enable admin tools for template generation
|
|
51
|
+
|
|
52
|
+
#### Claude Desktop Configuration
|
|
53
|
+
|
|
54
|
+
Add to your Claude Desktop config:
|
|
55
|
+
|
|
56
|
+
```json
|
|
57
|
+
{
|
|
58
|
+
"mcpServers": {
|
|
59
|
+
"scaffold-mcp": {
|
|
60
|
+
"command": "npx",
|
|
61
|
+
"args": ["-y", "@agiflowai/scaffold-mcp", "mcp-serve"]
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
```
|
|
66
|
+
|
|
67
|
+
Or if installed globally:
|
|
68
|
+
|
|
69
|
+
```json
|
|
70
|
+
{
|
|
71
|
+
"mcpServers": {
|
|
72
|
+
"scaffold-mcp": {
|
|
73
|
+
"command": "scaffold-mcp",
|
|
74
|
+
"args": ["mcp-serve"]
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
```
|
|
79
|
+
|
|
80
|
+
**To enable admin tools** (for template creation), add the `--admin-enable` flag:
|
|
81
|
+
|
|
82
|
+
```json
|
|
83
|
+
{
|
|
84
|
+
"mcpServers": {
|
|
85
|
+
"scaffold-mcp": {
|
|
86
|
+
"command": "npx",
|
|
87
|
+
"args": ["-y", "@agiflowai/scaffold-mcp", "mcp-serve", "--admin-enable"]
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
```
|
|
92
|
+
|
|
93
|
+
#### Available MCP Tools
|
|
94
|
+
|
|
95
|
+
**Standard Tools** (always available):
|
|
96
|
+
|
|
97
|
+
1. **list-boilerplates**: List all available project boilerplates
|
|
98
|
+
- Returns: Array of boilerplate configurations with schemas
|
|
99
|
+
|
|
100
|
+
2. **use-boilerplate**: Create a new project from a boilerplate template
|
|
101
|
+
- Arguments:
|
|
102
|
+
- `boilerplateName` (string): Name of the boilerplate
|
|
103
|
+
- `variables` (object): Variables matching the boilerplate's schema
|
|
104
|
+
|
|
105
|
+
3. **list-scaffolding-methods**: List available features for a project
|
|
106
|
+
- Arguments:
|
|
107
|
+
- `projectPath` (string): Absolute path to project directory
|
|
108
|
+
|
|
109
|
+
4. **use-scaffold-method**: Add a feature to an existing project
|
|
110
|
+
- Arguments:
|
|
111
|
+
- `projectPath` (string): Absolute path to project directory
|
|
112
|
+
- `scaffold_feature_name` (string): Name of the feature to add
|
|
113
|
+
- `variables` (object): Variables for the feature
|
|
114
|
+
|
|
115
|
+
5. **write-to-file**: Write content to a file
|
|
116
|
+
- Arguments:
|
|
117
|
+
- `file_path` (string): Path to the file
|
|
118
|
+
- `content` (string): Content to write
|
|
119
|
+
|
|
120
|
+
**Admin Tools** (enabled with `--admin-enable` flag):
|
|
121
|
+
|
|
122
|
+
6. **generate-boilerplate**: Create a new boilerplate configuration in a template's scaffold.yaml
|
|
123
|
+
- Arguments:
|
|
124
|
+
- `templateName` (string): Name of the template folder
|
|
125
|
+
- `boilerplateName` (string): Name of the boilerplate (kebab-case)
|
|
126
|
+
- `description` (string): Detailed description
|
|
127
|
+
- `targetFolder` (string): Target folder for projects
|
|
128
|
+
- `variables` (array): Variable definitions with schema
|
|
129
|
+
- `includes` (array): Template files to include
|
|
130
|
+
- `instruction` (string, optional): Usage instructions
|
|
131
|
+
|
|
132
|
+
7. **generate-feature-scaffold**: Create a new feature configuration in a template's scaffold.yaml
|
|
133
|
+
- Arguments:
|
|
134
|
+
- `templateName` (string): Name of the template folder
|
|
135
|
+
- `featureName` (string): Name of the feature (kebab-case)
|
|
136
|
+
- `description` (string): Feature description
|
|
137
|
+
- `variables` (array): Variable definitions with schema
|
|
138
|
+
- `includes` (array, optional): Template files to include
|
|
139
|
+
- `patterns` (array, optional): File patterns this feature works with
|
|
140
|
+
- `instruction` (string, optional): Usage instructions
|
|
141
|
+
|
|
142
|
+
8. **generate-boilerplate-file**: Create template files for boilerplates or features
|
|
143
|
+
- Arguments:
|
|
144
|
+
- `templateName` (string): Name of the template folder
|
|
145
|
+
- `filePath` (string): Path of the file within the template
|
|
146
|
+
- `content` (string): File content with Liquid variables
|
|
147
|
+
- `header` (string, optional): Header comment for AI hints
|
|
148
|
+
- `sourceFile` (string, optional): Copy from existing source file
|
|
149
|
+
|
|
150
|
+
### 2. CLI Commands
|
|
151
|
+
|
|
152
|
+
Use scaffold-mcp as a standalone CLI tool for template management and scaffolding.
|
|
153
|
+
|
|
154
|
+
#### Template Management
|
|
155
|
+
|
|
156
|
+
```bash
|
|
157
|
+
# Initialize templates folder
|
|
158
|
+
scaffold-mcp init
|
|
159
|
+
scaffold-mcp init --path /path/to/templates
|
|
160
|
+
|
|
161
|
+
# Add templates from repositories
|
|
162
|
+
scaffold-mcp add --name my-template --url https://github.com/user/template
|
|
163
|
+
scaffold-mcp add --name my-scaffold --url https://github.com/user/scaffold --type scaffold
|
|
164
|
+
```
|
|
165
|
+
|
|
166
|
+
#### Boilerplate Commands
|
|
167
|
+
|
|
168
|
+
```bash
|
|
169
|
+
# List available boilerplates
|
|
170
|
+
scaffold-mcp boilerplate list
|
|
171
|
+
|
|
172
|
+
# Show boilerplate details
|
|
173
|
+
scaffold-mcp boilerplate info <boilerplate-name>
|
|
174
|
+
|
|
175
|
+
# Create project from boilerplate
|
|
176
|
+
scaffold-mcp boilerplate create <name> --vars '{"appName":"my-app"}'
|
|
177
|
+
scaffold-mcp boilerplate create nextjs-15-boilerplate \
|
|
178
|
+
--vars '{"projectName":"my-app","packageName":"@myorg/my-app"}' \
|
|
179
|
+
--verbose
|
|
180
|
+
```
|
|
181
|
+
|
|
182
|
+
#### Scaffold Commands
|
|
183
|
+
|
|
184
|
+
```bash
|
|
185
|
+
# List scaffolding methods for a project
|
|
186
|
+
scaffold-mcp scaffold list ./apps/my-app
|
|
187
|
+
|
|
188
|
+
# Show scaffold method details
|
|
189
|
+
scaffold-mcp scaffold info <feature-name> --project ./apps/my-app
|
|
190
|
+
|
|
191
|
+
# Add feature to project
|
|
192
|
+
scaffold-mcp scaffold add <feature> --project ./apps/my-app --vars '{"name":"MyFeature"}'
|
|
193
|
+
scaffold-mcp scaffold add scaffold-nextjs-page \
|
|
194
|
+
--project ./apps/my-app \
|
|
195
|
+
--vars '{"pageTitle":"About Us","nextjsPagePath":"/about"}' \
|
|
196
|
+
--verbose
|
|
197
|
+
```
|
|
198
|
+
|
|
199
|
+
#### Environment Variables
|
|
200
|
+
|
|
201
|
+
- `MCP_PORT`: Port number for HTTP/SSE servers (default: 3000)
|
|
202
|
+
- `MCP_HOST`: Host for HTTP/SSE servers (default: localhost)
|
|
203
|
+
|
|
204
|
+
## Quick Start
|
|
205
|
+
|
|
206
|
+
### 1. Initialize Templates
|
|
207
|
+
|
|
208
|
+
```bash
|
|
209
|
+
# Initialize templates folder in your project
|
|
210
|
+
scaffold-mcp init
|
|
211
|
+
|
|
212
|
+
# Or specify a custom path
|
|
213
|
+
scaffold-mcp init --path ./my-templates
|
|
214
|
+
```
|
|
215
|
+
|
|
216
|
+
### 2. Add Templates
|
|
217
|
+
|
|
218
|
+
```bash
|
|
219
|
+
# Add a boilerplate template from a repository
|
|
220
|
+
scaffold-mcp add --name nextjs-15 --url https://github.com/yourorg/nextjs-15-template
|
|
221
|
+
|
|
222
|
+
# Add a scaffold template
|
|
223
|
+
scaffold-mcp add --name react-component --url https://github.com/yourorg/react-component-scaffold --type scaffold
|
|
224
|
+
```
|
|
225
|
+
|
|
226
|
+
### 3. Create a New Project
|
|
227
|
+
|
|
228
|
+
```bash
|
|
229
|
+
# List available boilerplates
|
|
230
|
+
scaffold-mcp boilerplate list
|
|
231
|
+
|
|
232
|
+
# Get info about a specific boilerplate
|
|
233
|
+
scaffold-mcp boilerplate info nextjs-15-boilerplate
|
|
234
|
+
|
|
235
|
+
# Create a new Next.js 15 project
|
|
236
|
+
scaffold-mcp boilerplate create nextjs-15-boilerplate \
|
|
237
|
+
--vars '{"projectName":"my-app","packageName":"@myorg/my-app","appName":"My App"}'
|
|
238
|
+
```
|
|
239
|
+
|
|
240
|
+
### 4. Add Features to Existing Projects
|
|
241
|
+
|
|
242
|
+
```bash
|
|
243
|
+
# List available features for your project
|
|
244
|
+
scaffold-mcp scaffold list /path/to/your/project
|
|
245
|
+
|
|
246
|
+
# Get info about a specific scaffold method
|
|
247
|
+
scaffold-mcp scaffold info scaffold-nextjs-page --project /path/to/your/project
|
|
248
|
+
|
|
249
|
+
# Add a new Next.js page
|
|
250
|
+
scaffold-mcp scaffold add scaffold-nextjs-page \
|
|
251
|
+
--project /path/to/your/project \
|
|
252
|
+
--vars '{"pageTitle":"About Us","pageDescription":"Learn more","nextjsPagePath":"/about"}'
|
|
253
|
+
```
|
|
254
|
+
|
|
255
|
+
## Documentation
|
|
256
|
+
|
|
257
|
+
- [CLI Commands](./docs/cli-commands.md) - Complete CLI reference
|
|
258
|
+
- [MCP Tools](./docs/mcp-tools.md) - MCP server tools reference
|
|
259
|
+
- [Template Conventions](./docs/template-conventions.md) - Guide for creating templates with Liquid syntax
|
|
260
|
+
- [Advanced Generators](./docs/advanced-generators.md) - Guide for creating custom TypeScript generators
|
|
261
|
+
|
|
262
|
+
## License
|
|
263
|
+
|
|
264
|
+
AGPL-3.0
|
|
@@ -0,0 +1,142 @@
|
|
|
1
|
+
import path from "node:path";
|
|
2
|
+
import yaml from "js-yaml";
|
|
3
|
+
import { z } from "zod";
|
|
4
|
+
|
|
5
|
+
//#region src/services/ScaffoldConfigLoader.ts
|
|
6
|
+
const VariablesSchemaSchema = z.object({
|
|
7
|
+
type: z.literal("object"),
|
|
8
|
+
properties: z.record(z.any()),
|
|
9
|
+
required: z.array(z.string()),
|
|
10
|
+
additionalProperties: z.boolean()
|
|
11
|
+
});
|
|
12
|
+
const ScaffoldConfigEntrySchema = z.object({
|
|
13
|
+
name: z.string(),
|
|
14
|
+
description: z.string().optional(),
|
|
15
|
+
instruction: z.string().optional(),
|
|
16
|
+
targetFolder: z.string().optional(),
|
|
17
|
+
variables_schema: VariablesSchemaSchema,
|
|
18
|
+
includes: z.array(z.string()),
|
|
19
|
+
generator: z.string().optional(),
|
|
20
|
+
patterns: z.array(z.string()).optional()
|
|
21
|
+
});
|
|
22
|
+
const ScaffoldYamlSchema = z.object({
|
|
23
|
+
boilerplate: z.union([ScaffoldConfigEntrySchema, z.array(ScaffoldConfigEntrySchema)]).optional(),
|
|
24
|
+
features: z.union([ScaffoldConfigEntrySchema, z.array(ScaffoldConfigEntrySchema)]).optional()
|
|
25
|
+
}).catchall(z.union([ScaffoldConfigEntrySchema, z.array(ScaffoldConfigEntrySchema)]));
|
|
26
|
+
var ScaffoldConfigLoader = class {
|
|
27
|
+
constructor(fileSystem, templateService) {
|
|
28
|
+
this.fileSystem = fileSystem;
|
|
29
|
+
this.templateService = templateService;
|
|
30
|
+
}
|
|
31
|
+
async parseArchitectConfig(templatePath) {
|
|
32
|
+
const architectPath = path.join(templatePath, "scaffold.yaml");
|
|
33
|
+
if (!await this.fileSystem.pathExists(architectPath)) return null;
|
|
34
|
+
try {
|
|
35
|
+
const content = await this.fileSystem.readFile(architectPath, "utf8");
|
|
36
|
+
const rawConfig = yaml.load(content);
|
|
37
|
+
return ScaffoldYamlSchema.parse(rawConfig);
|
|
38
|
+
} catch (error) {
|
|
39
|
+
if (error instanceof z.ZodError) {
|
|
40
|
+
const errorMessages = error.errors.map((err) => `${err.path.join(".")}: ${err.message}`).join("; ");
|
|
41
|
+
throw new Error(`scaffold.yaml validation failed: ${errorMessages}`);
|
|
42
|
+
}
|
|
43
|
+
throw new Error(`Failed to parse scaffold.yaml: ${error instanceof Error ? error.message : String(error)}`);
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
parseIncludeEntry(includeEntry, variables) {
|
|
47
|
+
const [pathPart, conditionsPart] = includeEntry.split("?");
|
|
48
|
+
const conditions = {};
|
|
49
|
+
if (conditionsPart) {
|
|
50
|
+
const conditionPairs = conditionsPart.split("&");
|
|
51
|
+
for (const pair of conditionPairs) {
|
|
52
|
+
const [key, value] = pair.split("=");
|
|
53
|
+
if (key && value) conditions[key.trim()] = value.trim();
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
if (pathPart.includes("->")) {
|
|
57
|
+
const [sourcePath, targetPath] = pathPart.split("->").map((p) => p.trim());
|
|
58
|
+
return {
|
|
59
|
+
sourcePath,
|
|
60
|
+
targetPath: this.replaceVariablesInPath(targetPath, variables),
|
|
61
|
+
conditions
|
|
62
|
+
};
|
|
63
|
+
}
|
|
64
|
+
const processedPath = this.replaceVariablesInPath(pathPart.trim(), variables);
|
|
65
|
+
return {
|
|
66
|
+
sourcePath: pathPart.trim(),
|
|
67
|
+
targetPath: processedPath,
|
|
68
|
+
conditions
|
|
69
|
+
};
|
|
70
|
+
}
|
|
71
|
+
replaceVariablesInPath(pathStr, variables) {
|
|
72
|
+
return this.templateService.renderString(pathStr, variables);
|
|
73
|
+
}
|
|
74
|
+
shouldIncludeFile(conditions, variables) {
|
|
75
|
+
if (!conditions || Object.keys(conditions).length === 0) return true;
|
|
76
|
+
for (const [conditionKey, conditionValue] of Object.entries(conditions)) {
|
|
77
|
+
const variableValue = variables[conditionKey];
|
|
78
|
+
if (conditionValue === "true" || conditionValue === "false") {
|
|
79
|
+
const expectedBoolean = conditionValue === "true";
|
|
80
|
+
if (Boolean(variableValue) !== expectedBoolean) return false;
|
|
81
|
+
} else if (String(variableValue) !== conditionValue) return false;
|
|
82
|
+
}
|
|
83
|
+
return true;
|
|
84
|
+
}
|
|
85
|
+
async validateTemplate(templatePath, scaffoldType) {
|
|
86
|
+
const errors = [];
|
|
87
|
+
const missingFiles = [];
|
|
88
|
+
if (!await this.fileSystem.pathExists(templatePath)) {
|
|
89
|
+
errors.push(`Template directory ${templatePath} does not exist`);
|
|
90
|
+
return {
|
|
91
|
+
isValid: false,
|
|
92
|
+
errors,
|
|
93
|
+
missingFiles
|
|
94
|
+
};
|
|
95
|
+
}
|
|
96
|
+
let architectConfig;
|
|
97
|
+
try {
|
|
98
|
+
architectConfig = await this.parseArchitectConfig(templatePath);
|
|
99
|
+
} catch (error) {
|
|
100
|
+
errors.push(`Failed to parse scaffold.yaml: ${error instanceof Error ? error.message : String(error)}`);
|
|
101
|
+
return {
|
|
102
|
+
isValid: false,
|
|
103
|
+
errors,
|
|
104
|
+
missingFiles
|
|
105
|
+
};
|
|
106
|
+
}
|
|
107
|
+
if (!architectConfig) {
|
|
108
|
+
errors.push("scaffold.yaml not found in template directory");
|
|
109
|
+
return {
|
|
110
|
+
isValid: false,
|
|
111
|
+
errors,
|
|
112
|
+
missingFiles
|
|
113
|
+
};
|
|
114
|
+
}
|
|
115
|
+
if (!architectConfig[scaffoldType]) {
|
|
116
|
+
const availableTypes = Object.keys(architectConfig).join(", ");
|
|
117
|
+
errors.push(`Scaffold type '${scaffoldType}' not found in scaffold.yaml. Available types: ${availableTypes}`);
|
|
118
|
+
return {
|
|
119
|
+
isValid: false,
|
|
120
|
+
errors,
|
|
121
|
+
missingFiles
|
|
122
|
+
};
|
|
123
|
+
}
|
|
124
|
+
const config = architectConfig[scaffoldType];
|
|
125
|
+
if (config.includes && Array.isArray(config.includes)) for (const includeFile of config.includes) {
|
|
126
|
+
const parsed = this.parseIncludeEntry(includeFile, {});
|
|
127
|
+
const sourcePath = path.join(templatePath, parsed.sourcePath);
|
|
128
|
+
const liquidSourcePath = `${sourcePath}.liquid`;
|
|
129
|
+
const sourceExists = await this.fileSystem.pathExists(sourcePath);
|
|
130
|
+
const liquidExists = await this.fileSystem.pathExists(liquidSourcePath);
|
|
131
|
+
if (!sourceExists && !liquidExists) missingFiles.push(includeFile);
|
|
132
|
+
}
|
|
133
|
+
return {
|
|
134
|
+
isValid: errors.length === 0 && missingFiles.length === 0,
|
|
135
|
+
errors,
|
|
136
|
+
missingFiles
|
|
137
|
+
};
|
|
138
|
+
}
|
|
139
|
+
};
|
|
140
|
+
|
|
141
|
+
//#endregion
|
|
142
|
+
export { ScaffoldConfigLoader };
|