multisite-cms-mcp 1.3.0 → 1.5.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/dist/tools/get-example.d.ts.map +1 -1
- package/dist/tools/get-example.js +48 -18
- package/dist/tools/validate-manifest.d.ts.map +1 -1
- package/dist/tools/validate-manifest.js +21 -30
- package/dist/tools/validate-template.d.ts.map +1 -1
- package/dist/tools/validate-template.js +164 -0
- package/package.json +1 -1
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"get-example.d.ts","sourceRoot":"","sources":["../../src/tools/get-example.ts"],"names":[],"mappings":"AAAA,KAAK,WAAW,GACZ,gBAAgB,GAChB,uBAAuB,GACvB,0BAA0B,GAC1B,qBAAqB,GACrB,oBAAoB,GACpB,eAAe,GACf,oBAAoB,GACpB,kBAAkB,GAClB,wBAAwB,GACxB,4BAA4B,GAC5B,eAAe,GACf,aAAa,GACb,gBAAgB,GAChB,iBAAiB,GACjB,gBAAgB,GAChB,WAAW,GACX,gBAAgB,GAChB,eAAe,GACf,gBAAgB,GAChB,gBAAgB,GAChB,qBAAqB,GACrB,eAAe,CAAC;
|
|
1
|
+
{"version":3,"file":"get-example.d.ts","sourceRoot":"","sources":["../../src/tools/get-example.ts"],"names":[],"mappings":"AAAA,KAAK,WAAW,GACZ,gBAAgB,GAChB,uBAAuB,GACvB,0BAA0B,GAC1B,qBAAqB,GACrB,oBAAoB,GACpB,eAAe,GACf,oBAAoB,GACpB,kBAAkB,GAClB,wBAAwB,GACxB,4BAA4B,GAC5B,eAAe,GACf,aAAa,GACb,gBAAgB,GAChB,iBAAiB,GACjB,gBAAgB,GAChB,WAAW,GACX,gBAAgB,GAChB,eAAe,GACf,gBAAgB,GAChB,gBAAgB,GAChB,qBAAqB,GACrB,eAAe,CAAC;AAiiCpB;;GAEG;AACH,wBAAsB,UAAU,CAAC,WAAW,EAAE,WAAW,GAAG,OAAO,CAAC,MAAM,CAAC,CAE1E"}
|
|
@@ -458,10 +458,10 @@ For any user-defined collection. All collections have built-in \`name\`, \`slug\
|
|
|
458
458
|
\`\`\``,
|
|
459
459
|
form_handling: `# Form Handling
|
|
460
460
|
|
|
461
|
-
Forms are automatically captured by the CMS:
|
|
461
|
+
Forms are automatically captured by the CMS using the \`data-form\` attribute:
|
|
462
462
|
|
|
463
463
|
\`\`\`html
|
|
464
|
-
<form data-form
|
|
464
|
+
<form data-form="contact" class="contact-form">
|
|
465
465
|
<div class="form-group">
|
|
466
466
|
<label for="name">Name</label>
|
|
467
467
|
<input type="text" name="name" id="name" required>
|
|
@@ -483,34 +483,64 @@ Forms are automatically captured by the CMS:
|
|
|
483
483
|
|
|
484
484
|
## Form Handler Script
|
|
485
485
|
|
|
486
|
+
Add this script to handle form submissions:
|
|
487
|
+
|
|
486
488
|
\`\`\`javascript
|
|
487
|
-
|
|
489
|
+
// Handle all forms with data-form attribute
|
|
490
|
+
document.querySelectorAll('form[data-form]').forEach(form => {
|
|
488
491
|
form.addEventListener('submit', async (e) => {
|
|
489
492
|
e.preventDefault();
|
|
490
493
|
|
|
491
|
-
const
|
|
492
|
-
const
|
|
493
|
-
const data = Object.fromEntries(formData);
|
|
494
|
+
const submitBtn = form.querySelector('button[type="submit"]');
|
|
495
|
+
const originalText = submitBtn?.textContent || 'Submit';
|
|
494
496
|
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
body: JSON.stringify(data)
|
|
500
|
-
});
|
|
497
|
+
if (submitBtn) {
|
|
498
|
+
submitBtn.disabled = true;
|
|
499
|
+
submitBtn.textContent = 'Sending...';
|
|
500
|
+
}
|
|
501
501
|
|
|
502
|
-
|
|
503
|
-
form.
|
|
504
|
-
|
|
502
|
+
try {
|
|
503
|
+
const formName = form.dataset.form || 'general';
|
|
504
|
+
const formData = new FormData(form);
|
|
505
|
+
const data = Object.fromEntries(formData);
|
|
506
|
+
|
|
507
|
+
// Endpoint is /_forms/{formName}
|
|
508
|
+
const response = await fetch('/_forms/' + formName, {
|
|
509
|
+
method: 'POST',
|
|
510
|
+
headers: { 'Content-Type': 'application/json' },
|
|
511
|
+
body: JSON.stringify(data)
|
|
512
|
+
});
|
|
513
|
+
|
|
514
|
+
if (response.ok) {
|
|
515
|
+
// Option 1: Redirect to thank you page
|
|
516
|
+
// window.location.href = '/thank-you';
|
|
517
|
+
|
|
518
|
+
// Option 2: Show success message
|
|
519
|
+
form.reset();
|
|
520
|
+
alert(form.dataset.successMessage || 'Thank you! Your message has been sent.');
|
|
521
|
+
} else {
|
|
522
|
+
throw new Error('Form submission failed');
|
|
523
|
+
}
|
|
524
|
+
} catch (error) {
|
|
525
|
+
console.error('Form error:', error);
|
|
526
|
+
alert('There was an error. Please try again.');
|
|
527
|
+
} finally {
|
|
528
|
+
if (submitBtn) {
|
|
529
|
+
submitBtn.disabled = false;
|
|
530
|
+
submitBtn.textContent = originalText;
|
|
531
|
+
}
|
|
505
532
|
}
|
|
506
533
|
});
|
|
507
534
|
});
|
|
508
535
|
\`\`\`
|
|
509
536
|
|
|
510
537
|
**Key points:**
|
|
511
|
-
- Add \`data-form
|
|
512
|
-
- Endpoint is \`/_forms/{formName}\` (
|
|
513
|
-
- All \`name\` attributes
|
|
538
|
+
- Add \`data-form="formname"\` to identify the form (e.g., \`data-form="contact"\`)
|
|
539
|
+
- Endpoint is \`/_forms/{formName}\` (e.g., \`/_forms/contact\`)
|
|
540
|
+
- All inputs must have \`name\` attributes to be captured
|
|
541
|
+
- Add a submit button for the form to work
|
|
542
|
+
|
|
543
|
+
**Note:** The legacy \`data-form-name\` attribute is deprecated. Use \`data-form\` instead.`,
|
|
514
544
|
asset_paths: `# Asset Path Rules
|
|
515
545
|
|
|
516
546
|
**ALL asset paths must use /public/ prefix:**
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"validate-manifest.d.ts","sourceRoot":"","sources":["../../src/tools/validate-manifest.ts"],"names":[],"mappings":"AAmBA;;GAEG;AACH,wBAAsB,gBAAgB,CAAC,YAAY,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,
|
|
1
|
+
{"version":3,"file":"validate-manifest.d.ts","sourceRoot":"","sources":["../../src/tools/validate-manifest.ts"],"names":[],"mappings":"AAmBA;;GAEG;AACH,wBAAsB,gBAAgB,CAAC,YAAY,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,CA2N5E"}
|
|
@@ -116,37 +116,34 @@ Tip: Use a JSON validator (like jsonlint.com) to find the exact error location.`
|
|
|
116
116
|
}
|
|
117
117
|
}
|
|
118
118
|
}
|
|
119
|
-
// Check cmsTemplates
|
|
119
|
+
// Check cmsTemplates - enforce strict pattern-based validation
|
|
120
120
|
const templates = m.cmsTemplates;
|
|
121
121
|
const customCollections = new Set();
|
|
122
122
|
if (templates) {
|
|
123
|
-
//
|
|
124
|
-
|
|
125
|
-
const
|
|
126
|
-
for (const key of
|
|
127
|
-
|
|
128
|
-
|
|
123
|
+
// All keys must match the unified format: {slug}Index, {slug}Detail, {slug}IndexPath, {slug}DetailPath
|
|
124
|
+
const VALID_KEY_PATTERN = /^[a-z][a-zA-Z0-9]*(Index|Detail|IndexPath|DetailPath)$/;
|
|
125
|
+
const KEY_PARTS_PATTERN = /^([a-z][a-zA-Z0-9]*)(Index|Detail|IndexPath|DetailPath)$/;
|
|
126
|
+
for (const key of Object.keys(templates)) {
|
|
127
|
+
if (!VALID_KEY_PATTERN.test(key)) {
|
|
128
|
+
// Provide helpful error message with suggestion
|
|
129
|
+
let suggestion = '';
|
|
130
|
+
if (key.toLowerCase().includes('post')) {
|
|
131
|
+
suggestion = ` Did you mean "${key.replace(/[Pp]ost/g, 'Detail')}"?`;
|
|
132
|
+
}
|
|
133
|
+
else if (!key.includes('Index') && !key.includes('Detail')) {
|
|
134
|
+
suggestion = ` Did you mean "${key}Index" or "${key}Detail"?`;
|
|
135
|
+
}
|
|
136
|
+
errors.push(`- Invalid cmsTemplates key "${key}". ` +
|
|
137
|
+
`All keys must use format: {slug}Index, {slug}Detail, {slug}IndexPath, or {slug}DetailPath.${suggestion}`);
|
|
129
138
|
continue;
|
|
130
139
|
}
|
|
131
|
-
// Extract collection slug from
|
|
132
|
-
|
|
133
|
-
if (
|
|
134
|
-
|
|
135
|
-
}
|
|
136
|
-
else if (key.endsWith('Detail') && !key.endsWith('DetailPath')) {
|
|
137
|
-
slug = key.slice(0, -6); // Remove 'Detail'
|
|
138
|
-
}
|
|
139
|
-
else if (key.endsWith('IndexPath')) {
|
|
140
|
-
slug = key.slice(0, -9); // Remove 'IndexPath'
|
|
141
|
-
}
|
|
142
|
-
else if (key.endsWith('DetailPath')) {
|
|
143
|
-
slug = key.slice(0, -10); // Remove 'DetailPath'
|
|
144
|
-
}
|
|
145
|
-
if (slug && slug.length > 0) {
|
|
146
|
-
customCollections.add(slug);
|
|
140
|
+
// Extract collection slug from valid keys
|
|
141
|
+
const match = key.match(KEY_PARTS_PATTERN);
|
|
142
|
+
if (match) {
|
|
143
|
+
customCollections.add(match[1]);
|
|
147
144
|
}
|
|
148
145
|
}
|
|
149
|
-
// Validate each collection
|
|
146
|
+
// Validate each detected collection
|
|
150
147
|
for (const slug of customCollections) {
|
|
151
148
|
const indexTemplate = templates[`${slug}Index`];
|
|
152
149
|
const detailTemplate = templates[`${slug}Detail`];
|
|
@@ -183,12 +180,6 @@ Tip: Use a JSON validator (like jsonlint.com) to find the exact error location.`
|
|
|
183
180
|
if (detailPath && !detailTemplate) {
|
|
184
181
|
warnings.push(`- Collection "${slug}": has ${slug}DetailPath but no ${slug}Detail template`);
|
|
185
182
|
}
|
|
186
|
-
// Note: indexPath and detailPath can be the same (e.g., both "/videos")
|
|
187
|
-
// The system distinguishes by whether a slug segment exists after the path
|
|
188
|
-
}
|
|
189
|
-
// Warn about deprecated collectionTemplates format
|
|
190
|
-
if (templates.collectionTemplates) {
|
|
191
|
-
warnings.push('- cmsTemplates.collectionTemplates: This nested format is deprecated. Use flat format instead: {slug}Index, {slug}Detail, {slug}IndexPath, {slug}DetailPath');
|
|
192
183
|
}
|
|
193
184
|
}
|
|
194
185
|
// Build result
|
|
@@ -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;
|
|
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"}
|
|
@@ -97,6 +97,111 @@ function getRichTextFields(collectionSlug, schema) {
|
|
|
97
97
|
return [];
|
|
98
98
|
return collection.fields.filter(f => f.type === 'richText').map(f => f.slug);
|
|
99
99
|
}
|
|
100
|
+
/**
|
|
101
|
+
* Validate forms in HTML templates
|
|
102
|
+
* Checks for proper data-form attribute, input names, and submit buttons
|
|
103
|
+
*/
|
|
104
|
+
function validateForms(html) {
|
|
105
|
+
const errors = [];
|
|
106
|
+
const warnings = [];
|
|
107
|
+
const suggestions = [];
|
|
108
|
+
// Find all form elements
|
|
109
|
+
const formPattern = /<form[^>]*>/gi;
|
|
110
|
+
const forms = html.match(formPattern) || [];
|
|
111
|
+
if (forms.length === 0) {
|
|
112
|
+
return { errors, warnings, suggestions };
|
|
113
|
+
}
|
|
114
|
+
// Track forms for validation
|
|
115
|
+
let formsWithDataForm = 0;
|
|
116
|
+
let formsWithLegacyAttr = 0;
|
|
117
|
+
for (const formTag of forms) {
|
|
118
|
+
// Check for data-form attribute (correct format)
|
|
119
|
+
const hasDataForm = /data-form=["'][^"']+["']/i.test(formTag);
|
|
120
|
+
// Check for legacy data-form-name attribute
|
|
121
|
+
const hasLegacyDataFormName = /data-form-name=["'][^"']+["']/i.test(formTag);
|
|
122
|
+
if (hasDataForm) {
|
|
123
|
+
formsWithDataForm++;
|
|
124
|
+
}
|
|
125
|
+
else if (hasLegacyDataFormName) {
|
|
126
|
+
formsWithLegacyAttr++;
|
|
127
|
+
warnings.push(`- Form uses deprecated data-form-name attribute. Migrate to data-form="formname" for consistency.`);
|
|
128
|
+
}
|
|
129
|
+
else {
|
|
130
|
+
// Form without any data-form attribute - check if it looks like a CMS form
|
|
131
|
+
// Don't error on forms that might be external (like search forms)
|
|
132
|
+
if (!formTag.includes('action=')) {
|
|
133
|
+
errors.push(`- Form is missing data-form attribute. Add data-form="formname" to identify the form for CMS submission.`);
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
// Extract form content for deeper validation
|
|
138
|
+
// Find each form block
|
|
139
|
+
const formBlocks = html.match(/<form[^>]*data-form[^>]*>[\s\S]*?<\/form>/gi) || [];
|
|
140
|
+
for (const formBlock of formBlocks) {
|
|
141
|
+
// Check for inputs with name attributes
|
|
142
|
+
const inputs = formBlock.match(/<input[^>]*>/gi) || [];
|
|
143
|
+
const textareas = formBlock.match(/<textarea[^>]*>/gi) || [];
|
|
144
|
+
const selects = formBlock.match(/<select[^>]*>/gi) || [];
|
|
145
|
+
const allInputElements = [...inputs, ...textareas, ...selects];
|
|
146
|
+
let inputsWithName = 0;
|
|
147
|
+
let inputsWithoutName = 0;
|
|
148
|
+
for (const input of allInputElements) {
|
|
149
|
+
// Skip hidden inputs and submit buttons
|
|
150
|
+
if (/type=["'](?:submit|button|hidden)["']/i.test(input)) {
|
|
151
|
+
continue;
|
|
152
|
+
}
|
|
153
|
+
if (/name=["'][^"']+["']/i.test(input)) {
|
|
154
|
+
inputsWithName++;
|
|
155
|
+
}
|
|
156
|
+
else {
|
|
157
|
+
inputsWithoutName++;
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
if (inputsWithoutName > 0) {
|
|
161
|
+
errors.push(`- Found ${inputsWithoutName} form input(s) without name attribute. All inputs must have name="fieldname" to be captured.`);
|
|
162
|
+
}
|
|
163
|
+
if (inputsWithName === 0 && allInputElements.length > 0) {
|
|
164
|
+
errors.push(`- Form has no inputs with name attributes - no data will be captured.`);
|
|
165
|
+
}
|
|
166
|
+
// Check for submit button
|
|
167
|
+
const hasSubmitButton = /<button[^>]*type=["']submit["'][^>]*>/i.test(formBlock) ||
|
|
168
|
+
/<input[^>]*type=["']submit["'][^>]*>/i.test(formBlock) ||
|
|
169
|
+
/<button[^>]*>(?!.*type=["']button["'])/i.test(formBlock); // button without type defaults to submit
|
|
170
|
+
if (!hasSubmitButton) {
|
|
171
|
+
warnings.push(`- Form may be missing a submit button. Add <button type="submit">Submit</button> for the form to work.`);
|
|
172
|
+
}
|
|
173
|
+
// Check for form handler script
|
|
174
|
+
const hasFormHandlerScript = html.includes("form[data-form]") || html.includes("form[data-form-name]");
|
|
175
|
+
if (!hasFormHandlerScript) {
|
|
176
|
+
suggestions.push(`- No form handler script detected. Make sure to include JavaScript that handles form submission to /_forms/{formName}`);
|
|
177
|
+
}
|
|
178
|
+
}
|
|
179
|
+
// Summary suggestion
|
|
180
|
+
if (formsWithDataForm > 0) {
|
|
181
|
+
suggestions.push(`- Found ${formsWithDataForm} form(s) with data-form attribute - forms will submit to /_forms/{formName}`);
|
|
182
|
+
}
|
|
183
|
+
// Check for thank-you page redirect pattern
|
|
184
|
+
const hasThankYouRedirect = /window\.location\.href\s*=\s*['"]\/thank-you['"]/i.test(html) ||
|
|
185
|
+
/window\.location\s*=\s*['"]\/thank-you['"]/i.test(html);
|
|
186
|
+
if (formsWithDataForm > 0 && !hasThankYouRedirect) {
|
|
187
|
+
suggestions.push(`- Consider adding a /thank-you page and redirecting there on successful submission for better UX`);
|
|
188
|
+
}
|
|
189
|
+
return { errors, warnings, suggestions };
|
|
190
|
+
}
|
|
191
|
+
/**
|
|
192
|
+
* Extract collection slugs referenced in {{#each}} loops on static pages
|
|
193
|
+
*/
|
|
194
|
+
function extractCollectionReferences(html) {
|
|
195
|
+
const collections = [];
|
|
196
|
+
const eachLoops = html.match(/\{\{#each\s+(\w+)[^}]*\}\}/g) || [];
|
|
197
|
+
for (const loop of eachLoops) {
|
|
198
|
+
const match = loop.match(/\{\{#each\s+(\w+)/);
|
|
199
|
+
if (match) {
|
|
200
|
+
collections.push(match[1]);
|
|
201
|
+
}
|
|
202
|
+
}
|
|
203
|
+
return [...new Set(collections)]; // Unique collections
|
|
204
|
+
}
|
|
100
205
|
/**
|
|
101
206
|
* Validates an HTML template for correct CMS token usage
|
|
102
207
|
*
|
|
@@ -204,6 +309,11 @@ async function validateTemplate(html, templateType, collectionSlug, projectId) {
|
|
|
204
309
|
}
|
|
205
310
|
}
|
|
206
311
|
}
|
|
312
|
+
// ============ Form Validation ============
|
|
313
|
+
const formValidation = validateForms(html);
|
|
314
|
+
errors.push(...formValidation.errors);
|
|
315
|
+
warnings.push(...formValidation.warnings);
|
|
316
|
+
suggestions.push(...formValidation.suggestions);
|
|
207
317
|
// Validate site tokens
|
|
208
318
|
const siteTokens = html.match(/\{\{site\.(\w+)\}\}/g) || [];
|
|
209
319
|
for (const token of siteTokens) {
|
|
@@ -253,6 +363,60 @@ async function validateTemplate(html, templateType, collectionSlug, projectId) {
|
|
|
253
363
|
if (resolved) {
|
|
254
364
|
const schema = await fetchTenantSchema(resolved.tenantId);
|
|
255
365
|
if (schema) {
|
|
366
|
+
// For static pages, validate any collection references in {{#each}} loops
|
|
367
|
+
if (templateType === 'static_page') {
|
|
368
|
+
const referencedCollections = extractCollectionReferences(html);
|
|
369
|
+
if (referencedCollections.length > 0) {
|
|
370
|
+
suggestions.push(`- Found ${referencedCollections.length} collection reference(s) in static page: ${referencedCollections.join(', ')}`);
|
|
371
|
+
// Check each referenced collection exists
|
|
372
|
+
const missingCollections = [];
|
|
373
|
+
const existingCollections = [];
|
|
374
|
+
for (const collSlug of referencedCollections) {
|
|
375
|
+
const exists = schema.collections.some(c => c.slug === collSlug);
|
|
376
|
+
if (exists) {
|
|
377
|
+
existingCollections.push(collSlug);
|
|
378
|
+
}
|
|
379
|
+
else {
|
|
380
|
+
missingCollections.push(collSlug);
|
|
381
|
+
}
|
|
382
|
+
}
|
|
383
|
+
if (missingCollections.length > 0) {
|
|
384
|
+
schemaValidation += `
|
|
385
|
+
|
|
386
|
+
## ACTION REQUIRED: Missing Collections
|
|
387
|
+
|
|
388
|
+
The following collections are referenced in this static page but **do not exist**:
|
|
389
|
+
|
|
390
|
+
${missingCollections.map(c => `- \`${c}\``).join('\n')}
|
|
391
|
+
|
|
392
|
+
You must create these collections using \`sync_schema\` before deployment.
|
|
393
|
+
|
|
394
|
+
**Example:**
|
|
395
|
+
\`\`\`json
|
|
396
|
+
{
|
|
397
|
+
"projectId": "${resolved.tenantId}",
|
|
398
|
+
"collections": [
|
|
399
|
+
${missingCollections.map(c => ` {
|
|
400
|
+
"slug": "${c}",
|
|
401
|
+
"name": "${c.charAt(0).toUpperCase() + c.slice(1)}",
|
|
402
|
+
"nameSingular": "${c.endsWith('s') ? c.slice(0, -1).charAt(0).toUpperCase() + c.slice(0, -1).slice(1) : c}",
|
|
403
|
+
"fields": []
|
|
404
|
+
}`).join(',\n')}
|
|
405
|
+
]
|
|
406
|
+
}
|
|
407
|
+
\`\`\`
|
|
408
|
+
`;
|
|
409
|
+
}
|
|
410
|
+
if (existingCollections.length > 0) {
|
|
411
|
+
schemaValidation += `
|
|
412
|
+
|
|
413
|
+
## Static Page Collection References Validated
|
|
414
|
+
|
|
415
|
+
The following collections exist and can be used: ${existingCollections.map(c => `\`${c}\``).join(', ')}
|
|
416
|
+
`;
|
|
417
|
+
}
|
|
418
|
+
}
|
|
419
|
+
}
|
|
256
420
|
const targetCollection = collectionSlug || '';
|
|
257
421
|
if (targetCollection && templateType !== 'static_page') {
|
|
258
422
|
// Get richText fields for triple brace validation
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "multisite-cms-mcp",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.5.0",
|
|
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": {
|