multisite-cms-mcp 1.5.4 → 1.5.6

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/README.md CHANGED
@@ -9,8 +9,7 @@ Fast Mode is a modern CMS platform that turns static HTML websites into fully ed
9
9
  ### Key Features
10
10
 
11
11
  - **Keep Your Design** — Use any HTML/CSS. No themes, no page builders, no compromises.
12
- - **Built-in Collections** — Blog posts, team members, authors, downloads — all ready to use.
13
- - **Custom Collections** — Create any content type you need with custom fields.
12
+ - **Custom Collections** — Create any content type you need (blog posts, team members, products, etc.)
14
13
  - **Visual Editor** — Edit content directly on your live site.
15
14
  - **Instant Deploy** — Push from GitHub or upload directly. Sites go live in seconds.
16
15
  - **Forms & Submissions** — Collect form data without any backend code.
@@ -34,7 +33,6 @@ These tools work without authentication — perfect for converting and validatin
34
33
 
35
34
  | Tool | Description |
36
35
  |------|-------------|
37
- | `get_schema` | Get the complete CMS schema with all collections and fields |
38
36
  | `get_field_types` | Get available field types for creating custom fields |
39
37
  | `validate_manifest` | Validate your manifest.json file |
40
38
  | `validate_template` | Check HTML templates for correct token usage, form handling, and schema validation |
@@ -122,7 +120,7 @@ Add to your Claude config:
122
120
 
123
121
  Ask your AI assistant:
124
122
 
125
- > "Convert this website to Fast Mode format. Use the get_schema tool to understand the CMS structure."
123
+ > "Convert this website to Fast Mode format"
126
124
 
127
125
  The AI will use validation tools to create a proper package structure.
128
126
 
@@ -159,7 +157,7 @@ Your site will be available at `https://your-site.fastmode.ai` and you can manag
159
157
 
160
158
  ## Schema Sync (Critical for Custom Fields)
161
159
 
162
- When your templates use custom fields (fields beyond the built-in ones), you **must** create them in the CMS before deploying. The MCP server handles this automatically.
160
+ When your templates use custom fields, you **must** create them in the CMS before deploying. The MCP server handles this automatically.
163
161
 
164
162
  ### Recommended Workflow
165
163
 
@@ -200,15 +198,14 @@ When you validate a template with a project ID, the tool will:
200
198
  | `multiSelect` | Multiple dropdown choices (tags) |
201
199
  | `relation` | Link to another collection (author → authors) |
202
200
 
203
- ### Example: Adding Custom Fields to Built-in Collection
201
+ ### Example: Adding Fields to Existing Collection
204
202
 
205
203
  ```json
206
204
  {
207
205
  "projectId": "my-project",
208
206
  "fieldsToAdd": [
209
207
  {
210
- "collectionSlug": "blogs",
211
- "isBuiltin": true,
208
+ "collectionSlug": "posts",
212
209
  "fields": [
213
210
  { "slug": "heroImage", "name": "Hero Image", "type": "image" },
214
211
  { "slug": "category", "name": "Category", "type": "select", "options": "Tech,Business,Lifestyle" }
@@ -239,10 +236,11 @@ When you validate a template with a project ID, the tool will:
239
236
  ```
240
237
 
241
238
  **Features:**
239
+ - ✅ Two-phase creation: collections first, then fields (relation fields always work)
240
+ - ✅ Automatic retry for transient failures
242
241
  - ✅ Validates all field types before creating
243
242
  - ✅ Skips duplicates automatically (safe to re-run)
244
- - ✅ Works with both builtin and custom collections
245
- - ✅ Reports detailed results (created/skipped)
243
+ - ✅ Reports detailed summary table with status
246
244
 
247
245
  ---
248
246
 
@@ -388,6 +386,14 @@ Use the `get_conversion_guide` tool for detailed instructions.
388
386
 
389
387
  ## Changelog
390
388
 
389
+ ### v1.5.5
390
+ - **Removed `get_schema`** — Use `get_tenant_schema` for project-specific schema
391
+ - **Two-Phase Schema Sync** — Collections created first, then all fields (relation fields always work)
392
+ - **Automatic Retry** — Transient failures are retried automatically
393
+ - **Similar Project Check** — `create_site` warns about similar existing project names
394
+ - **Explicit Project Selection** — `deploy_package` now requires explicit project selection
395
+ - **Improved Output** — Clear summary tables for sync results
396
+
391
397
  ### v1.5.0
392
398
  - **Form Validation** — Validates `data-form` attribute, input names, submit buttons
393
399
  - **Static Page Validation** — Validates `{{#each}}` collection references against schema
package/dist/index.js CHANGED
@@ -4,7 +4,6 @@ Object.defineProperty(exports, "__esModule", { value: true });
4
4
  const index_js_1 = require("@modelcontextprotocol/sdk/server/index.js");
5
5
  const stdio_js_1 = require("@modelcontextprotocol/sdk/server/stdio.js");
6
6
  const types_js_1 = require("@modelcontextprotocol/sdk/types.js");
7
- const get_schema_1 = require("./tools/get-schema");
8
7
  const validate_manifest_1 = require("./tools/validate-manifest");
9
8
  const validate_template_1 = require("./tools/validate-template");
10
9
  const validate_package_1 = require("./tools/validate-package");
@@ -26,15 +25,6 @@ const server = new index_js_1.Server({
26
25
  });
27
26
  // Define available tools
28
27
  const TOOLS = [
29
- {
30
- name: 'get_schema',
31
- description: 'Get the complete CMS schema including all built-in and custom collections with their fields. Use this to understand what content types are available and what tokens to use in templates.',
32
- inputSchema: {
33
- type: 'object',
34
- properties: {},
35
- required: [],
36
- },
37
- },
38
28
  {
39
29
  name: 'validate_manifest',
40
30
  description: 'Validate a manifest.json file for the CMS package. Returns errors if the structure is incorrect or required fields are missing.',
@@ -161,7 +151,7 @@ const TOOLS = [
161
151
  },
162
152
  {
163
153
  name: 'get_tenant_schema',
164
- description: 'Fetch the complete schema for a specific project including custom collections and custom fields on built-in collections. Use this when converting a website for an existing project to see their specific CMS configuration. Requires FASTMODE_AUTH_TOKEN environment variable.',
154
+ description: 'Fetch the complete schema for a specific project including all collections and their fields. Use this when converting a website for an existing project to see their specific CMS configuration. Requires authentication.',
165
155
  inputSchema: {
166
156
  type: 'object',
167
157
  properties: {
@@ -175,7 +165,7 @@ const TOOLS = [
175
165
  },
176
166
  {
177
167
  name: 'create_site',
178
- description: 'Create a new Fast Mode site/project. Opens browser for authentication if not already logged in. After creation, use deploy_package to upload your website.',
168
+ description: 'Create a new Fast Mode site/project. Checks for similar existing project names first. Opens browser for authentication if not already logged in. After creation, use deploy_package to upload your website.',
179
169
  inputSchema: {
180
170
  type: 'object',
181
171
  properties: {
@@ -187,13 +177,17 @@ const TOOLS = [
187
177
  type: 'string',
188
178
  description: 'Optional custom subdomain (auto-generated from name if not provided)',
189
179
  },
180
+ confirmCreate: {
181
+ type: 'boolean',
182
+ description: 'Set to true to skip similar-name check (use only after user confirms they want a new project)',
183
+ },
190
184
  },
191
185
  required: ['name'],
192
186
  },
193
187
  },
194
188
  {
195
189
  name: 'deploy_package',
196
- description: 'Deploy a website package (.zip file) to Fast Mode. Will check for GitHub sync and block deployment if connected (to prevent conflicts). Use force:true to override. Opens browser for authentication if not already logged in.',
190
+ description: 'Deploy a website package (.zip file) to Fast Mode. Requires a projectId - use list_projects to find existing projects or create_site to create a new one first. Will check for GitHub sync and block deployment if connected. Use force:true to override.',
197
191
  inputSchema: {
198
192
  type: 'object',
199
193
  properties: {
@@ -203,11 +197,7 @@ const TOOLS = [
203
197
  },
204
198
  projectId: {
205
199
  type: 'string',
206
- description: 'Optional: Deploy to an existing project with this ID. Use list_projects to get project IDs.',
207
- },
208
- projectName: {
209
- type: 'string',
210
- description: 'Optional: Create a new project with this name and deploy to it. Required if projectId is not provided and no projects exist.',
200
+ description: 'Project ID to deploy to. Use list_projects to find existing projects, or create_site to create a new one.',
211
201
  },
212
202
  force: {
213
203
  type: 'boolean',
@@ -219,7 +209,7 @@ const TOOLS = [
219
209
  },
220
210
  {
221
211
  name: 'get_field_types',
222
- description: 'Get the list of available field types for creating new fields in collections. Use this to see what field types you can use with sync_schema. This is NOT the list of fields in your schema - use get_schema or get_tenant_schema for that. No authentication required.',
212
+ description: 'Get the list of available field types for creating new fields in collections. Use this to see what field types you can use with sync_schema. This is NOT the list of fields in your schema - use get_tenant_schema for that. No authentication required.',
223
213
  inputSchema: {
224
214
  type: 'object',
225
215
  properties: {},
@@ -228,7 +218,7 @@ const TOOLS = [
228
218
  },
229
219
  {
230
220
  name: 'sync_schema',
231
- description: 'IMPORTANT: Create new collections and/or add custom fields to collections. Call this BEFORE deploying when validate_template reports missing fields or collections. Requires authentication. All fields must have a type specified - call get_field_types first to see available types. Skips duplicates automatically. Without calling this tool, templates with custom fields will not render correctly.',
221
+ description: 'IMPORTANT: Create collections and fields in Fast Mode. Uses two-phase creation: all collections are created first, then all fields - so relation fields between collections always work regardless of order. Includes automatic retry for transient failures. Skips duplicates automatically. Call this BEFORE deploying. Requires authentication.',
232
222
  inputSchema: {
233
223
  type: 'object',
234
224
  properties: {
@@ -311,9 +301,6 @@ server.setRequestHandler(types_js_1.CallToolRequestSchema, async (request) => {
311
301
  try {
312
302
  let result;
313
303
  switch (name) {
314
- case 'get_schema':
315
- result = await (0, get_schema_1.getSchema)();
316
- break;
317
304
  case 'validate_manifest':
318
305
  result = await (0, validate_manifest_1.validateManifest)(params.manifest);
319
306
  break;
@@ -346,10 +333,10 @@ server.setRequestHandler(types_js_1.CallToolRequestSchema, async (request) => {
346
333
  result = await (0, get_tenant_schema_1.getTenantSchema)(params.projectId);
347
334
  break;
348
335
  case 'create_site':
349
- result = await (0, create_site_1.createSite)(params.name, params.subdomain);
336
+ result = await (0, create_site_1.createSite)(params.name, params.subdomain, params.confirmCreate);
350
337
  break;
351
338
  case 'deploy_package':
352
- result = await (0, deploy_package_1.deployPackage)(params.packagePath, params.projectId, params.projectName, params.force);
339
+ result = await (0, deploy_package_1.deployPackage)(params.packagePath, params.projectId, params.force);
353
340
  break;
354
341
  case 'get_field_types':
355
342
  result = await (0, get_field_types_1.getFieldTypes)();
@@ -3,6 +3,7 @@
3
3
  *
4
4
  * @param name - The name of the project
5
5
  * @param subdomain - Optional: Custom subdomain (auto-generated from name if not provided)
6
+ * @param confirmCreate - Optional: Skip similar-name check (set to true after user confirms)
6
7
  */
7
- export declare function createSite(name: string, subdomain?: string): Promise<string>;
8
+ export declare function createSite(name: string, subdomain?: string, confirmCreate?: boolean): Promise<string>;
8
9
  //# sourceMappingURL=create-site.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"create-site.d.ts","sourceRoot":"","sources":["../../src/tools/create-site.ts"],"names":[],"mappings":"AAgBA;;;;;GAKG;AACH,wBAAsB,UAAU,CAAC,IAAI,EAAE,MAAM,EAAE,SAAS,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,CAyFlF"}
1
+ {"version":3,"file":"create-site.d.ts","sourceRoot":"","sources":["../../src/tools/create-site.ts"],"names":[],"mappings":"AAiEA;;;;;;GAMG;AACH,wBAAsB,UAAU,CAAC,IAAI,EAAE,MAAM,EAAE,SAAS,CAAC,EAAE,MAAM,EAAE,aAAa,CAAC,EAAE,OAAO,GAAG,OAAO,CAAC,MAAM,CAAC,CAwI3G"}
@@ -3,13 +3,46 @@ Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.createSite = createSite;
4
4
  const api_client_1 = require("../lib/api-client");
5
5
  const device_flow_1 = require("../lib/device-flow");
6
+ /**
7
+ * Check for similar project names
8
+ */
9
+ function findSimilarProjects(name, projects) {
10
+ const normalizedName = name.toLowerCase().trim();
11
+ return projects.filter(project => {
12
+ const projectName = project.name.toLowerCase();
13
+ // Exact match
14
+ if (projectName === normalizedName)
15
+ return true;
16
+ // One contains the other
17
+ if (projectName.includes(normalizedName) || normalizedName.includes(projectName))
18
+ return true;
19
+ // Similar words (check if main words overlap)
20
+ const nameWords = normalizedName.split(/\s+/).filter(w => w.length > 2);
21
+ const projectWords = projectName.split(/\s+/).filter(w => w.length > 2);
22
+ const overlap = nameWords.filter(w => projectWords.some(pw => pw.includes(w) || w.includes(pw)));
23
+ if (overlap.length > 0 && overlap.length >= nameWords.length * 0.5)
24
+ return true;
25
+ return false;
26
+ });
27
+ }
28
+ /**
29
+ * List existing projects for the authenticated user
30
+ */
31
+ async function listExistingProjects() {
32
+ const response = await (0, api_client_1.apiRequest)('/api/tenants');
33
+ if ((0, api_client_1.isApiError)(response)) {
34
+ return [];
35
+ }
36
+ return response.data;
37
+ }
6
38
  /**
7
39
  * Create a new Fast Mode site/project
8
40
  *
9
41
  * @param name - The name of the project
10
42
  * @param subdomain - Optional: Custom subdomain (auto-generated from name if not provided)
43
+ * @param confirmCreate - Optional: Skip similar-name check (set to true after user confirms)
11
44
  */
12
- async function createSite(name, subdomain) {
45
+ async function createSite(name, subdomain, confirmCreate) {
13
46
  // Check authentication
14
47
  if (await (0, api_client_1.needsAuthentication)()) {
15
48
  const authResult = await (0, device_flow_1.ensureAuthenticated)();
@@ -29,6 +62,48 @@ create_site(name: "My Awesome Website")
29
62
  \`\`\`
30
63
  `;
31
64
  }
65
+ // Check for similar projects (unless confirmCreate is true)
66
+ if (!confirmCreate) {
67
+ const existingProjects = await listExistingProjects();
68
+ const similarProjects = findSimilarProjects(name, existingProjects);
69
+ if (similarProjects.length > 0) {
70
+ let output = `# Similar Project Found
71
+
72
+ Before creating "${name}", please note that you have similar existing project(s):
73
+
74
+ `;
75
+ similarProjects.forEach((project, index) => {
76
+ const url = project.customDomain || `${project.subdomain}.fastmode.ai`;
77
+ output += `${index + 1}. **${project.name}**
78
+ - ID: \`${project.id}\`
79
+ - URL: ${url}
80
+
81
+ `;
82
+ });
83
+ output += `---
84
+
85
+ ## ACTION REQUIRED
86
+
87
+ **Ask the user:** "I found a similar project called '${similarProjects[0].name}'. Would you like to:
88
+ 1. Deploy to the existing project '${similarProjects[0].name}'?
89
+ 2. Create a new project called '${name}'?"
90
+
91
+ ### If deploying to existing project:
92
+ \`\`\`
93
+ deploy_package(
94
+ packagePath: "./your-site.zip",
95
+ projectId: "${similarProjects[0].id}"
96
+ )
97
+ \`\`\`
98
+
99
+ ### If creating new project anyway:
100
+ \`\`\`
101
+ create_site(name: "${name}", confirmCreate: true)
102
+ \`\`\`
103
+ `;
104
+ return output;
105
+ }
106
+ }
32
107
  // Generate subdomain from name if not provided
33
108
  const finalSubdomain = subdomain || name
34
109
  .toLowerCase()
@@ -2,9 +2,8 @@
2
2
  * Deploy a website package to Fast Mode
3
3
  *
4
4
  * @param packagePath - Path to the zip file
5
- * @param projectId - Optional: Deploy to existing project with this ID
6
- * @param projectName - Optional: Create new project with this name
5
+ * @param projectId - Project ID to deploy to (use list_projects to find, or create_site to create new)
7
6
  * @param force - Optional: Skip GitHub connection check and deploy anyway
8
7
  */
9
- export declare function deployPackage(packagePath: string, projectId?: string, projectName?: string, force?: boolean): Promise<string>;
8
+ export declare function deployPackage(packagePath: string, projectId?: string, force?: boolean): Promise<string>;
10
9
  //# sourceMappingURL=deploy-package.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"deploy-package.d.ts","sourceRoot":"","sources":["../../src/tools/deploy-package.ts"],"names":[],"mappings":"AAmRA;;;;;;;GAOG;AACH,wBAAsB,aAAa,CACjC,WAAW,EAAE,MAAM,EACnB,SAAS,CAAC,EAAE,MAAM,EAClB,WAAW,CAAC,EAAE,MAAM,EACpB,KAAK,CAAC,EAAE,OAAO,GACd,OAAO,CAAC,MAAM,CAAC,CAwJjB"}
1
+ {"version":3,"file":"deploy-package.d.ts","sourceRoot":"","sources":["../../src/tools/deploy-package.ts"],"names":[],"mappings":"AA0QA;;;;;;GAMG;AACH,wBAAsB,aAAa,CACjC,WAAW,EAAE,MAAM,EACnB,SAAS,CAAC,EAAE,MAAM,EAClB,KAAK,CAAC,EAAE,OAAO,GACd,OAAO,CAAC,MAAM,CAAC,CAmGjB"}
@@ -103,28 +103,6 @@ deploy_package(
103
103
  **Site:** https://${subdomain}.fastmode.ai
104
104
  `;
105
105
  }
106
- /**
107
- * Create a new site/project
108
- */
109
- async function createNewSite(name, subdomain) {
110
- // Generate subdomain from name if not provided
111
- const finalSubdomain = subdomain || name
112
- .toLowerCase()
113
- .replace(/[^a-z0-9]+/g, '-')
114
- .replace(/^-|-$/g, '')
115
- .slice(0, 30);
116
- const response = await (0, api_client_1.apiRequest)('/api/tenants', {
117
- method: 'POST',
118
- body: {
119
- name,
120
- subdomain: finalSubdomain,
121
- },
122
- });
123
- if ((0, api_client_1.isApiError)(response)) {
124
- return { error: response.error };
125
- }
126
- return response.data;
127
- }
128
106
  /**
129
107
  * Upload a package to a specific tenant
130
108
  */
@@ -187,52 +165,78 @@ async function readPackage(packagePath) {
187
165
  /**
188
166
  * Format project list for user selection
189
167
  */
190
- function formatProjectChoice(projects) {
191
- let output = `# Select a Project
168
+ function formatProjectChoice(projects, packagePath) {
169
+ let output = `# Project Required
192
170
 
193
- Found ${projects.length} existing project${projects.length > 1 ? 's' : ''}:
171
+ You need to specify which project to deploy to.
172
+
173
+ ## Your Projects
194
174
 
195
175
  `;
196
- projects.forEach((project, index) => {
197
- const url = project.customDomain || `${project.subdomain}.fastmode.ai`;
198
- output += `${index + 1}. **${project.name}**
176
+ if (projects.length > 0) {
177
+ projects.forEach((project, index) => {
178
+ const url = project.customDomain || `${project.subdomain}.fastmode.ai`;
179
+ output += `${index + 1}. **${project.name}**
199
180
  - ID: \`${project.id}\`
200
181
  - URL: ${url}
201
182
  - Status: ${project.site?.status || 'pending'}
202
183
 
203
184
  `;
204
- });
205
- output += `---
185
+ });
186
+ output += `---
187
+
188
+ ## ACTION REQUIRED
206
189
 
207
- ## How to Deploy
190
+ **Ask the user:** "Which project should I deploy to? You can choose from the list above, or I can create a new project for you."
208
191
 
209
- **To an existing project:**
192
+ ### To deploy to an existing project:
210
193
  \`\`\`
211
194
  deploy_package(
212
- packagePath: "./your-site.zip",
195
+ packagePath: "${packagePath}",
213
196
  projectId: "${projects[0]?.id || 'project-id-here'}"
214
197
  )
215
198
  \`\`\`
216
199
 
217
- **To a new project:**
200
+ ### To create a NEW project first:
201
+ \`\`\`
202
+ create_site(name: "Project Name")
203
+ \`\`\`
204
+ Then deploy with the returned project ID.
205
+ `;
206
+ }
207
+ else {
208
+ output += `You don't have any projects yet.
209
+
210
+ ---
211
+
212
+ ## ACTION REQUIRED
213
+
214
+ **Ask the user:** "What would you like to name your new project?"
215
+
216
+ Then create the project:
217
+ \`\`\`
218
+ create_site(name: "User's Project Name")
219
+ \`\`\`
220
+
221
+ And deploy with the returned project ID:
218
222
  \`\`\`
219
223
  deploy_package(
220
- packagePath: "./your-site.zip",
221
- projectName: "My New Site"
224
+ packagePath: "${packagePath}",
225
+ projectId: "returned-project-id"
222
226
  )
223
227
  \`\`\`
224
228
  `;
229
+ }
225
230
  return output;
226
231
  }
227
232
  /**
228
233
  * Deploy a website package to Fast Mode
229
234
  *
230
235
  * @param packagePath - Path to the zip file
231
- * @param projectId - Optional: Deploy to existing project with this ID
232
- * @param projectName - Optional: Create new project with this name
236
+ * @param projectId - Project ID to deploy to (use list_projects to find, or create_site to create new)
233
237
  * @param force - Optional: Skip GitHub connection check and deploy anyway
234
238
  */
235
- async function deployPackage(packagePath, projectId, projectName, force) {
239
+ async function deployPackage(packagePath, projectId, force) {
236
240
  // Check authentication
237
241
  if (await (0, api_client_1.needsAuthentication)()) {
238
242
  const authResult = await (0, device_flow_1.ensureAuthenticated)();
@@ -240,26 +244,10 @@ async function deployPackage(packagePath, projectId, projectName, force) {
240
244
  return authResult.message;
241
245
  }
242
246
  }
243
- // If no projectId specified, help user choose
244
- if (!projectId && !projectName) {
247
+ // If no projectId specified, list projects and prompt for selection
248
+ if (!projectId) {
245
249
  const projects = await listExistingProjects();
246
- if (projects.length > 0) {
247
- return formatProjectChoice(projects);
248
- }
249
- // No projects exist - need a projectName
250
- return `# No Projects Found
251
-
252
- You don't have any FastMode projects yet.
253
-
254
- To create a new project and deploy, provide a project name:
255
-
256
- \`\`\`
257
- deploy_package(
258
- packagePath: "${packagePath}",
259
- projectName: "My Website"
260
- )
261
- \`\`\`
262
- `;
250
+ return formatProjectChoice(projects, packagePath);
263
251
  }
264
252
  // Read the package first
265
253
  const packageResult = await readPackage(packagePath);
@@ -272,55 +260,21 @@ Please provide a valid .zip file path.
272
260
  `;
273
261
  }
274
262
  const zipBuffer = packageResult;
275
- let targetProjectId = projectId;
276
- let subdomain = '';
277
- // Create new project if needed
278
- if (!targetProjectId && projectName) {
279
- const createResult = await createNewSite(projectName);
280
- if ('error' in createResult) {
281
- // Check if it's an auth error
282
- if (createResult.error.includes('401') || createResult.error.includes('auth')) {
283
- const authResult = await (0, device_flow_1.ensureAuthenticated)();
284
- if (!authResult.authenticated) {
285
- return authResult.message;
286
- }
287
- // Retry
288
- const retryResult = await createNewSite(projectName);
289
- if ('error' in retryResult) {
290
- return `# Failed to Create Project
291
-
292
- ${retryResult.error}
293
-
294
- Please try again or create the project manually at app.fastmode.ai
295
- `;
296
- }
297
- targetProjectId = retryResult.id;
298
- subdomain = retryResult.subdomain;
299
- }
300
- else {
301
- return `# Failed to Create Project
263
+ // Get subdomain for the project
264
+ const projects = await listExistingProjects();
265
+ const project = projects.find(p => p.id === projectId);
266
+ const subdomain = project?.subdomain || '';
267
+ if (!project) {
268
+ return `# Project Not Found
302
269
 
303
- ${createResult.error}
270
+ Could not find project with ID: \`${projectId}\`
304
271
 
305
- Please try again or create the project manually at app.fastmode.ai
272
+ Use \`list_projects\` to see available projects, or \`create_site\` to create a new one.
306
273
  `;
307
- }
308
- }
309
- else {
310
- targetProjectId = createResult.id;
311
- subdomain = createResult.subdomain;
312
- console.error(`Created new project: ${projectName} (${subdomain}.fastmode.ai)`);
313
- }
314
- }
315
- // Get subdomain for existing project
316
- if (!subdomain && targetProjectId) {
317
- const projects = await listExistingProjects();
318
- const project = projects.find(p => p.id === targetProjectId);
319
- subdomain = project?.subdomain || '';
320
274
  }
321
- // Check for GitHub connection (unless force is true or it's a new project)
322
- if (!force && !projectName && targetProjectId) {
323
- const githubStatus = await checkGitHubConnection(targetProjectId);
275
+ // Check for GitHub connection (unless force is true)
276
+ if (!force) {
277
+ const githubStatus = await checkGitHubConnection(projectId);
324
278
  if (githubStatus?.connected && githubStatus.repo) {
325
279
  // GitHub is connected - block deployment
326
280
  return formatGitHubBlockMessage(githubStatus.repo, githubStatus.branch || 'main', subdomain);
@@ -328,7 +282,7 @@ Please try again or create the project manually at app.fastmode.ai
328
282
  }
329
283
  // Upload the package
330
284
  console.error(`Deploying to ${subdomain}.fastmode.ai...`);
331
- const uploadResult = await uploadPackage(targetProjectId, zipBuffer);
285
+ const uploadResult = await uploadPackage(projectId, zipBuffer);
332
286
  if ('error' in uploadResult) {
333
287
  // Check if it's an auth error
334
288
  if ((0, api_client_1.needsAuthError)({ success: false, error: uploadResult.error, statusCode: 401 })) {
@@ -337,7 +291,7 @@ Please try again or create the project manually at app.fastmode.ai
337
291
  return authResult.message;
338
292
  }
339
293
  // Retry upload
340
- const retryResult = await uploadPackage(targetProjectId, zipBuffer);
294
+ const retryResult = await uploadPackage(projectId, zipBuffer);
341
295
  if ('error' in retryResult) {
342
296
  return `# Upload Failed
343
297
 
@@ -349,7 +303,7 @@ Please check:
349
303
  3. Try again or upload manually at app.fastmode.ai
350
304
  `;
351
305
  }
352
- return formatSuccess(subdomain, retryResult, !!projectName);
306
+ return formatSuccess(subdomain, retryResult);
353
307
  }
354
308
  return `# Upload Failed
355
309
 
@@ -361,15 +315,15 @@ Please check:
361
315
  3. Try again or upload manually at app.fastmode.ai
362
316
  `;
363
317
  }
364
- return formatSuccess(subdomain, uploadResult, !!projectName);
318
+ return formatSuccess(subdomain, uploadResult);
365
319
  }
366
320
  /**
367
321
  * Format success message
368
322
  */
369
- function formatSuccess(subdomain, result, isNewProject) {
370
- return `# Deployment Successful! 🚀
323
+ function formatSuccess(subdomain, result) {
324
+ return `# Deployment Successful!
371
325
 
372
- ${isNewProject ? '**New project created and deployed!**' : '**Package deployed!**'}
326
+ **Package deployed!**
373
327
 
374
328
  ## Live Site
375
329
 
@@ -781,7 +781,7 @@ ${SECTIONS.checklist}
781
781
 
782
782
  Use these MCP tools during conversion:
783
783
 
784
- - \`get_schema\` - Get collection field reference and token syntax
784
+ - \`get_field_types\` - Get available field types for sync_schema
785
785
  - \`get_tenant_schema\` - Get exact collections and fields for a specific project
786
786
  - \`get_example\` - Get code examples for specific patterns
787
787
  - \`validate_manifest\` - Check your manifest.json
@@ -3,7 +3,7 @@
3
3
  *
4
4
  * Returns the list of field types that can be used when CREATING new fields
5
5
  * in collections. This is NOT the list of fields in your schema - use
6
- * get_schema or get_tenant_schema for that.
6
+ * get_tenant_schema for that.
7
7
  *
8
8
  * No authentication required.
9
9
  */
@@ -1 +1 @@
1
- {"version":3,"file":"get-field-types.d.ts","sourceRoot":"","sources":["../../src/tools/get-field-types.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAEH;;GAEG;AACH,MAAM,WAAW,SAAS;IACxB,KAAK,EAAE,MAAM,CAAC;IACd,KAAK,EAAE,MAAM,CAAC;IACd,WAAW,EAAE,MAAM,CAAC;IACpB,eAAe,CAAC,EAAE,OAAO,CAAC;IAC1B,2BAA2B,CAAC,EAAE,OAAO,CAAC;CACvC;AAED;;;GAGG;AACH,eAAO,MAAM,qBAAqB,EAAE,SAAS,EAqE5C,CAAC;AAEF;;;GAGG;AACH,wBAAsB,aAAa,IAAI,OAAO,CAAC,MAAM,CAAC,CA0CrD"}
1
+ {"version":3,"file":"get-field-types.d.ts","sourceRoot":"","sources":["../../src/tools/get-field-types.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAEH;;GAEG;AACH,MAAM,WAAW,SAAS;IACxB,KAAK,EAAE,MAAM,CAAC;IACd,KAAK,EAAE,MAAM,CAAC;IACd,WAAW,EAAE,MAAM,CAAC;IACpB,eAAe,CAAC,EAAE,OAAO,CAAC;IAC1B,2BAA2B,CAAC,EAAE,OAAO,CAAC;CACvC;AAED;;;GAGG;AACH,eAAO,MAAM,qBAAqB,EAAE,SAAS,EAqE5C,CAAC;AAEF;;;GAGG;AACH,wBAAsB,aAAa,IAAI,OAAO,CAAC,MAAM,CAAC,CAyCrD"}
@@ -4,7 +4,7 @@
4
4
  *
5
5
  * Returns the list of field types that can be used when CREATING new fields
6
6
  * in collections. This is NOT the list of fields in your schema - use
7
- * get_schema or get_tenant_schema for that.
7
+ * get_tenant_schema for that.
8
8
  *
9
9
  * No authentication required.
10
10
  */
@@ -104,7 +104,7 @@ async function getFieldTypes() {
104
104
 
105
105
  These are the field types you can use when CREATING new fields with the \`sync_schema\` tool.
106
106
 
107
- **Note:** This is NOT the list of fields in your schema. Use \`get_schema\` for the generic schema or \`get_tenant_schema\` for a specific project's schema.
107
+ **Note:** This is NOT the list of fields in your schema. Use \`get_tenant_schema\` to see a specific project's schema.
108
108
 
109
109
  ## Field Types
110
110
 
@@ -118,8 +118,7 @@ When calling \`sync_schema\` to add fields, specify the \`type\` parameter:
118
118
  {
119
119
  "fieldsToAdd": [
120
120
  {
121
- "collectionSlug": "blogs",
122
- "isBuiltin": true,
121
+ "collectionSlug": "posts",
123
122
  "fields": [
124
123
  { "slug": "heroImage", "name": "Hero Image", "type": "image" },
125
124
  { "slug": "featured", "name": "Featured", "type": "boolean" },
@@ -1 +1 @@
1
- {"version":3,"file":"sync-schema.d.ts","sourceRoot":"","sources":["../../src/tools/sync-schema.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAYH,UAAU,aAAa;IACrB,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;IACb,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,UAAU,CAAC,EAAE,OAAO,CAAC;IACrB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,mBAAmB,CAAC,EAAE,MAAM,CAAC;CAC9B;AAED,UAAU,kBAAkB;IAC1B,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;IACb,YAAY,EAAE,MAAM,CAAC;IACrB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,MAAM,CAAC,EAAE,aAAa,EAAE,CAAC;CAC1B;AAED,UAAU,WAAW;IACnB,cAAc,EAAE,MAAM,CAAC;IACvB,SAAS,CAAC,EAAE,OAAO,CAAC;IACpB,MAAM,EAAE,aAAa,EAAE,CAAC;CACzB;AAED,UAAU,eAAe;IACvB,SAAS,EAAE,MAAM,CAAC;IAClB,WAAW,CAAC,EAAE,kBAAkB,EAAE,CAAC;IACnC,WAAW,CAAC,EAAE,WAAW,EAAE,CAAC;CAC7B;AAgJD;;;;GAIG;AACH,wBAAsB,UAAU,CAAC,KAAK,EAAE,eAAe,GAAG,OAAO,CAAC,MAAM,CAAC,CAmSxE"}
1
+ {"version":3,"file":"sync-schema.d.ts","sourceRoot":"","sources":["../../src/tools/sync-schema.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAYH,UAAU,aAAa;IACrB,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;IACb,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,UAAU,CAAC,EAAE,OAAO,CAAC;IACrB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,mBAAmB,CAAC,EAAE,MAAM,CAAC;CAC9B;AAED,UAAU,kBAAkB;IAC1B,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;IACb,YAAY,EAAE,MAAM,CAAC;IACrB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,MAAM,CAAC,EAAE,aAAa,EAAE,CAAC;CAC1B;AAED,UAAU,WAAW;IACnB,cAAc,EAAE,MAAM,CAAC;IACvB,SAAS,CAAC,EAAE,OAAO,CAAC;IACpB,MAAM,EAAE,aAAa,EAAE,CAAC;CACzB;AAED,UAAU,eAAe;IACvB,SAAS,EAAE,MAAM,CAAC;IAClB,WAAW,CAAC,EAAE,kBAAkB,EAAE,CAAC;IACnC,WAAW,CAAC,EAAE,WAAW,EAAE,CAAC;CAC7B;AAgJD;;;;GAIG;AACH,wBAAsB,UAAU,CAAC,KAAK,EAAE,eAAe,GAAG,OAAO,CAAC,MAAM,CAAC,CA2VxE"}
@@ -211,56 +211,34 @@ ${resolved.error}
211
211
  return `# Error\n\n${existingResult.error}`;
212
212
  }
213
213
  }
214
- const existingCollections = Array.isArray(existingResult) ? existingResult : [];
214
+ let existingCollections = Array.isArray(existingResult) ? existingResult : [];
215
215
  // Track results
216
- const results = [];
216
+ const collectionResults = [];
217
+ const fieldResults = [];
217
218
  const created = { collections: 0, fields: 0 };
218
219
  const skipped = { collections: 0, fields: 0 };
219
- // Create new collections
220
+ const failed = { collections: 0, fields: 0 };
221
+ // Build a map of collection slug -> ID (for both existing and newly created)
222
+ const collectionIdMap = new Map();
223
+ const collectionFieldsMap = new Map();
224
+ // Initialize with existing collections
225
+ for (const col of existingCollections) {
226
+ collectionIdMap.set(col.slug.toLowerCase(), col.id);
227
+ collectionFieldsMap.set(col.slug.toLowerCase(), col.fields);
228
+ }
229
+ // ============ PHASE 1: Create/Resolve ALL Collections ============
220
230
  if (collections && collections.length > 0) {
231
+ collectionResults.push('### Phase 1: Collections\n');
221
232
  for (const col of collections) {
233
+ const slugLower = col.slug.toLowerCase();
222
234
  // Check if collection already exists
223
- const existing = existingCollections.find(c => c.slug.toLowerCase() === col.slug.toLowerCase());
224
- if (existing) {
225
- results.push(`Skipped collection "${col.slug}" (already exists)`);
235
+ if (collectionIdMap.has(slugLower)) {
236
+ collectionResults.push(`| ${col.slug} | Skipped | Already exists |`);
226
237
  skipped.collections++;
227
- // But still try to add any new fields
228
- if (col.fields && col.fields.length > 0) {
229
- for (const field of col.fields) {
230
- const fieldExists = existing.fields.some(f => f.slug.toLowerCase() === field.slug.toLowerCase());
231
- if (fieldExists) {
232
- results.push(` Skipped field "${field.slug}" (already exists)`);
233
- skipped.fields++;
234
- }
235
- else {
236
- // Add field to existing collection
237
- const fieldRes = await (0, api_client_1.apiRequest)(`/api/collections/${existing.id}/fields`, {
238
- tenantId,
239
- method: 'POST',
240
- body: {
241
- slug: field.slug,
242
- name: field.name,
243
- type: field.type,
244
- description: field.description,
245
- isRequired: field.isRequired,
246
- options: field.options,
247
- referenceCollection: field.referenceCollection,
248
- },
249
- });
250
- if ((0, api_client_1.isApiError)(fieldRes)) {
251
- results.push(` Failed to add field "${field.slug}": ${fieldRes.error}`);
252
- }
253
- else {
254
- results.push(` Added field "${field.slug}" (${field.type})`);
255
- created.fields++;
256
- }
257
- }
258
- }
259
- }
260
238
  continue;
261
239
  }
262
- // Create new collection
263
- const createRes = await (0, api_client_1.apiRequest)('/api/collections', {
240
+ // Create new collection (WITHOUT fields - those come in Phase 2)
241
+ let createRes = await (0, api_client_1.apiRequest)('/api/collections', {
264
242
  tenantId,
265
243
  method: 'POST',
266
244
  body: {
@@ -271,93 +249,157 @@ ${resolved.error}
271
249
  hasSlug: col.hasSlug ?? true,
272
250
  },
273
251
  });
252
+ // Retry once if failed
274
253
  if ((0, api_client_1.isApiError)(createRes)) {
275
- results.push(`Failed to create collection "${col.slug}": ${createRes.error}`);
254
+ await new Promise(resolve => setTimeout(resolve, 500));
255
+ createRes = await (0, api_client_1.apiRequest)('/api/collections', {
256
+ tenantId,
257
+ method: 'POST',
258
+ body: {
259
+ slug: col.slug,
260
+ name: col.name,
261
+ nameSingular: col.nameSingular,
262
+ description: col.description,
263
+ hasSlug: col.hasSlug ?? true,
264
+ },
265
+ });
266
+ }
267
+ if ((0, api_client_1.isApiError)(createRes)) {
268
+ collectionResults.push(`| ${col.slug} | FAILED | ${createRes.error} |`);
269
+ failed.collections++;
276
270
  continue;
277
271
  }
278
- results.push(`Created collection "${col.name}" (${col.slug})`);
272
+ // Add to our maps
273
+ collectionIdMap.set(slugLower, createRes.data.id);
274
+ collectionFieldsMap.set(slugLower, []); // New collection has no fields yet
275
+ collectionResults.push(`| ${col.slug} | Created | ${col.name} |`);
279
276
  created.collections++;
280
- // Add fields to the new collection
277
+ }
278
+ }
279
+ const fieldJobs = [];
280
+ // Fields from new collections
281
+ if (collections) {
282
+ for (const col of collections) {
281
283
  if (col.fields && col.fields.length > 0) {
282
- const collectionId = createRes.data.id;
283
284
  for (const field of col.fields) {
284
- const fieldRes = await (0, api_client_1.apiRequest)(`/api/collections/${collectionId}/fields`, {
285
- tenantId,
286
- method: 'POST',
287
- body: {
288
- slug: field.slug,
289
- name: field.name,
290
- type: field.type,
291
- description: field.description,
292
- isRequired: field.isRequired,
293
- options: field.options,
294
- referenceCollection: field.referenceCollection,
295
- },
296
- });
297
- if ((0, api_client_1.isApiError)(fieldRes)) {
298
- results.push(` Failed to add field "${field.slug}": ${fieldRes.error}`);
299
- }
300
- else {
301
- results.push(` Added field "${field.slug}" (${field.type})`);
302
- created.fields++;
303
- }
285
+ fieldJobs.push({ collectionSlug: col.slug, field });
304
286
  }
305
287
  }
306
288
  }
307
289
  }
308
- // Add fields to existing collections
309
- if (fieldsToAdd && fieldsToAdd.length > 0) {
290
+ // Fields from fieldsToAdd
291
+ if (fieldsToAdd) {
310
292
  for (const group of fieldsToAdd) {
311
- results.push(`\nCollection ${group.collectionSlug}:`);
312
- // All collections are custom collections now
313
- const customCollection = existingCollections.find(c => c.slug.toLowerCase() === group.collectionSlug.toLowerCase());
314
- if (!customCollection) {
315
- results.push(` Collection "${group.collectionSlug}" not found. Create it first with the collections parameter.`);
293
+ for (const field of group.fields) {
294
+ fieldJobs.push({ collectionSlug: group.collectionSlug, field });
295
+ }
296
+ }
297
+ }
298
+ if (fieldJobs.length > 0) {
299
+ fieldResults.push('### Phase 2: Fields\n');
300
+ fieldResults.push('| Collection | Field | Type | Status |');
301
+ fieldResults.push('|------------|-------|------|--------|');
302
+ for (const job of fieldJobs) {
303
+ const slugLower = job.collectionSlug.toLowerCase();
304
+ const collectionId = collectionIdMap.get(slugLower);
305
+ if (!collectionId) {
306
+ fieldResults.push(`| ${job.collectionSlug} | ${job.field.slug} | ${job.field.type} | FAILED: Collection not found |`);
307
+ failed.fields++;
316
308
  continue;
317
309
  }
318
- for (const field of group.fields) {
319
- const fieldExists = customCollection.fields.some(f => f.slug.toLowerCase() === field.slug.toLowerCase());
320
- if (fieldExists) {
321
- results.push(` Skipped field "${field.slug}" (already exists)`);
322
- skipped.fields++;
323
- continue;
324
- }
325
- const fieldRes = await (0, api_client_1.apiRequest)(`/api/collections/${customCollection.id}/fields`, {
310
+ // Check if field already exists
311
+ const existingFields = collectionFieldsMap.get(slugLower) || [];
312
+ const fieldExists = existingFields.some(f => f.slug.toLowerCase() === job.field.slug.toLowerCase());
313
+ if (fieldExists) {
314
+ fieldResults.push(`| ${job.collectionSlug} | ${job.field.slug} | ${job.field.type} | Skipped (exists) |`);
315
+ skipped.fields++;
316
+ continue;
317
+ }
318
+ // Create the field with retry logic
319
+ let fieldRes = await (0, api_client_1.apiRequest)(`/api/collections/${collectionId}/fields`, {
320
+ tenantId,
321
+ method: 'POST',
322
+ body: {
323
+ slug: job.field.slug,
324
+ name: job.field.name,
325
+ type: job.field.type,
326
+ description: job.field.description,
327
+ isRequired: job.field.isRequired,
328
+ options: job.field.options,
329
+ referenceCollection: job.field.referenceCollection,
330
+ },
331
+ });
332
+ // Retry once if failed (network issues, temporary errors)
333
+ if ((0, api_client_1.isApiError)(fieldRes)) {
334
+ // Wait a moment and retry
335
+ await new Promise(resolve => setTimeout(resolve, 500));
336
+ fieldRes = await (0, api_client_1.apiRequest)(`/api/collections/${collectionId}/fields`, {
326
337
  tenantId,
327
338
  method: 'POST',
328
339
  body: {
329
- slug: field.slug,
330
- name: field.name,
331
- type: field.type,
332
- description: field.description,
333
- isRequired: field.isRequired,
334
- options: field.options,
335
- referenceCollection: field.referenceCollection,
340
+ slug: job.field.slug,
341
+ name: job.field.name,
342
+ type: job.field.type,
343
+ description: job.field.description,
344
+ isRequired: job.field.isRequired,
345
+ options: job.field.options,
346
+ referenceCollection: job.field.referenceCollection,
336
347
  },
337
348
  });
338
- if ((0, api_client_1.isApiError)(fieldRes)) {
339
- results.push(` Failed to add field "${field.slug}": ${fieldRes.error}`);
340
- }
341
- else {
342
- results.push(` Added field "${field.slug}" (${field.type})`);
343
- created.fields++;
344
- }
349
+ }
350
+ if ((0, api_client_1.isApiError)(fieldRes)) {
351
+ fieldResults.push(`| ${job.collectionSlug} | ${job.field.slug} | ${job.field.type} | FAILED: ${fieldRes.error} |`);
352
+ failed.fields++;
353
+ }
354
+ else {
355
+ fieldResults.push(`| ${job.collectionSlug} | ${job.field.slug} | ${job.field.type} | Created |`);
356
+ created.fields++;
357
+ // Update the fields map so subsequent checks know this field exists
358
+ const fields = collectionFieldsMap.get(slugLower) || [];
359
+ fields.push({ slug: job.field.slug });
360
+ collectionFieldsMap.set(slugLower, fields);
345
361
  }
346
362
  }
347
363
  }
348
- // Summary
349
- return `# Schema Sync Complete
364
+ // ============ Build Summary ============
365
+ const hasFailures = failed.collections > 0 || failed.fields > 0;
366
+ let output = `# Schema Sync ${hasFailures ? 'Completed with Errors' : 'Complete'}
350
367
 
351
368
  **Project ID:** \`${tenantId}\`
352
369
 
353
370
  ## Summary
354
- - Collections created: ${created.collections}
355
- - Fields created: ${created.fields}
356
- - Collections skipped (already exist): ${skipped.collections}
357
- - Fields skipped (already exist): ${skipped.fields}
358
371
 
359
- ## Details
372
+ | Metric | Created | Skipped | Failed |
373
+ |--------|---------|---------|--------|
374
+ | Collections | ${created.collections} | ${skipped.collections} | ${failed.collections} |
375
+ | Fields | ${created.fields} | ${skipped.fields} | ${failed.fields} |
360
376
 
361
- ${results.join('\n')}
362
377
  `;
378
+ if (collectionResults.length > 1) {
379
+ output += `## Collections
380
+
381
+ | Slug | Status | Details |
382
+ |------|--------|---------|
383
+ ${collectionResults.slice(1).join('\n')}
384
+
385
+ `;
386
+ }
387
+ if (fieldResults.length > 0) {
388
+ output += `## Fields
389
+
390
+ ${fieldResults.join('\n')}
391
+
392
+ `;
393
+ }
394
+ if (hasFailures) {
395
+ output += `---
396
+
397
+ ## ACTION REQUIRED
398
+
399
+ Some items failed to create. Please review the errors above and:
400
+ 1. Fix any issues with field types or parameters
401
+ 2. Run sync_schema again - it will skip already-created items and retry failed ones
402
+ `;
403
+ }
404
+ return output;
363
405
  }
@@ -1 +1 @@
1
- {"version":3,"file":"validate-template.d.ts","sourceRoot":"","sources":["../../src/tools/validate-template.ts"],"names":[],"mappings":"AAMA,KAAK,YAAY,GAAG,cAAc,GAAG,eAAe,GAAG,aAAa,CAAC;AAgRrE;;;;;;;GAOG;AACH,wBAAsB,gBAAgB,CACpC,IAAI,EAAE,MAAM,EACZ,YAAY,EAAE,YAAY,EAC1B,cAAc,CAAC,EAAE,MAAM,EACvB,SAAS,CAAC,EAAE,MAAM,GACjB,OAAO,CAAC,MAAM,CAAC,CAqbjB"}
1
+ {"version":3,"file":"validate-template.d.ts","sourceRoot":"","sources":["../../src/tools/validate-template.ts"],"names":[],"mappings":"AAMA,KAAK,YAAY,GAAG,cAAc,GAAG,eAAe,GAAG,aAAa,CAAC;AAgRrE;;;;;;;GAOG;AACH,wBAAsB,gBAAgB,CACpC,IAAI,EAAE,MAAM,EACZ,YAAY,EAAE,YAAY,EAC1B,cAAc,CAAC,EAAE,MAAM,EACvB,SAAS,CAAC,EAAE,MAAM,GACjB,OAAO,CAAC,MAAM,CAAC,CAobjB"}
@@ -511,7 +511,6 @@ Use the \`sync_schema\` tool to create the missing fields before deploying. This
511
511
  "fieldsToAdd": [
512
512
  {
513
513
  "collectionSlug": "${targetCollection}",
514
- "isBuiltin": false,
515
514
  "fields": [
516
515
  ${missingFields.map(f => ` { "slug": "${f}", "name": "${f.charAt(0).toUpperCase() + f.slice(1).replace(/([A-Z])/g, ' $1').trim()}", "type": "YOUR_TYPE" }`).join(',\n')}
517
516
  ]
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "multisite-cms-mcp",
3
- "version": "1.5.4",
3
+ "version": "1.5.6",
4
4
  "description": "MCP server for Fast Mode CMS. Convert websites, validate packages, and deploy directly to Fast Mode. Includes authentication, project creation, schema sync, and one-click deployment.",
5
5
  "main": "dist/index.js",
6
6
  "bin": {