multisite-cms-mcp 1.0.21 → 1.0.22

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.
@@ -1 +1 @@
1
- {"version":3,"file":"validate-manifest.d.ts","sourceRoot":"","sources":["../../src/tools/validate-manifest.ts"],"names":[],"mappings":"AA8BA;;GAEG;AACH,wBAAsB,gBAAgB,CAAC,YAAY,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,CAsI5E"}
1
+ {"version":3,"file":"validate-manifest.d.ts","sourceRoot":"","sources":["../../src/tools/validate-manifest.ts"],"names":[],"mappings":"AAyCA;;GAEG;AACH,wBAAsB,gBAAgB,CAAC,YAAY,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,CA4N5E"}
@@ -8,7 +8,8 @@ const pageSchema = zod_1.z.object({
8
8
  title: zod_1.z.string(),
9
9
  editable: zod_1.z.boolean().optional(),
10
10
  });
11
- const cmsTemplatesSchema = zod_1.z.object({
11
+ // Built-in collection templates
12
+ const builtInTemplatesSchema = zod_1.z.object({
12
13
  blogIndex: zod_1.z.string().optional(),
13
14
  blogIndexPath: zod_1.z.string().startsWith('/').optional(),
14
15
  blogPost: zod_1.z.string().optional(),
@@ -20,8 +21,17 @@ const cmsTemplatesSchema = zod_1.z.object({
20
21
  authorsIndex: zod_1.z.string().optional(),
21
22
  authorDetail: zod_1.z.string().optional(),
22
23
  authorsIndexPath: zod_1.z.string().startsWith('/').optional(),
24
+ // Legacy format - deprecated
23
25
  collectionPaths: zod_1.z.record(zod_1.z.string().startsWith('/')).optional(),
24
- }).optional();
26
+ collectionTemplates: zod_1.z.record(zod_1.z.object({
27
+ path: zod_1.z.string().startsWith('/').optional(),
28
+ indexTemplate: zod_1.z.string().optional(),
29
+ detailTemplate: zod_1.z.string().optional(),
30
+ })).optional(),
31
+ });
32
+ // Custom collection templates use flat format: {slug}Index, {slug}Detail, {slug}IndexPath, {slug}DetailPath
33
+ // This schema allows any additional string keys for custom collections
34
+ const cmsTemplatesSchema = builtInTemplatesSchema.catchall(zod_1.z.string()).optional();
25
35
  const manifestSchema = zod_1.z.object({
26
36
  pages: zod_1.z.array(pageSchema),
27
37
  cmsTemplates: cmsTemplatesSchema,
@@ -85,6 +95,7 @@ The manifest.json file is not valid JSON. Check for:
85
95
  }
86
96
  // Check cmsTemplates
87
97
  const templates = m.cmsTemplates;
98
+ const customCollections = new Set();
88
99
  if (templates) {
89
100
  // Check template file paths
90
101
  const templateFields = ['blogIndex', 'blogPost', 'team', 'downloads', 'authorsIndex', 'authorDetail'];
@@ -120,9 +131,85 @@ The manifest.json file is not valid JSON. Check for:
120
131
  if (templates.authorDetail && !templates.authorsIndex) {
121
132
  warnings.push('- authorDetail template defined but no authorsIndex - authors need both templates');
122
133
  }
134
+ // Detect and validate custom collection templates (flat format)
135
+ // Pattern: {slug}Index, {slug}Detail, {slug}IndexPath, {slug}DetailPath
136
+ const allKeys = Object.keys(templates);
137
+ for (const key of allKeys) {
138
+ // Skip built-in keys
139
+ if (['blogIndex', 'blogIndexPath', 'blogPost', 'blogPostPath', 'team', 'teamPath',
140
+ 'downloads', 'downloadsPath', 'authorsIndex', 'authorDetail', 'authorsIndexPath',
141
+ 'collectionPaths', 'collectionTemplates'].includes(key)) {
142
+ continue;
143
+ }
144
+ // Extract collection slug from key pattern
145
+ let slug = null;
146
+ if (key.endsWith('Index') && !key.endsWith('IndexPath')) {
147
+ slug = key.slice(0, -5); // Remove 'Index'
148
+ }
149
+ else if (key.endsWith('Detail') && !key.endsWith('DetailPath')) {
150
+ slug = key.slice(0, -6); // Remove 'Detail'
151
+ }
152
+ else if (key.endsWith('IndexPath')) {
153
+ slug = key.slice(0, -9); // Remove 'IndexPath'
154
+ }
155
+ else if (key.endsWith('DetailPath')) {
156
+ slug = key.slice(0, -10); // Remove 'DetailPath'
157
+ }
158
+ if (slug && slug.length > 0) {
159
+ customCollections.add(slug);
160
+ }
161
+ }
162
+ // Validate each custom collection
163
+ for (const slug of customCollections) {
164
+ const indexTemplate = templates[`${slug}Index`];
165
+ const detailTemplate = templates[`${slug}Detail`];
166
+ const indexPath = templates[`${slug}IndexPath`];
167
+ const detailPath = templates[`${slug}DetailPath`];
168
+ // Check template file paths
169
+ if (typeof indexTemplate === 'string') {
170
+ if (!indexTemplate.endsWith('.html')) {
171
+ warnings.push(`- cmsTemplates.${slug}Index: should end with .html (got: ${indexTemplate})`);
172
+ }
173
+ }
174
+ if (typeof detailTemplate === 'string') {
175
+ if (!detailTemplate.endsWith('.html')) {
176
+ warnings.push(`- cmsTemplates.${slug}Detail: should end with .html (got: ${detailTemplate})`);
177
+ }
178
+ }
179
+ // Check path format
180
+ if (typeof indexPath === 'string' && !indexPath.startsWith('/')) {
181
+ errors.push(`- cmsTemplates.${slug}IndexPath: must start with "/" (got: ${indexPath})`);
182
+ }
183
+ if (typeof detailPath === 'string' && !detailPath.startsWith('/')) {
184
+ errors.push(`- cmsTemplates.${slug}DetailPath: must start with "/" (got: ${detailPath})`);
185
+ }
186
+ // Check consistency - if you have one, you should have the others
187
+ if (indexTemplate && !indexPath) {
188
+ warnings.push(`- Custom collection "${slug}": has ${slug}Index but no ${slug}IndexPath`);
189
+ }
190
+ if (detailTemplate && !detailPath) {
191
+ warnings.push(`- Custom collection "${slug}": has ${slug}Detail but no ${slug}DetailPath`);
192
+ }
193
+ if (indexPath && !indexTemplate) {
194
+ warnings.push(`- Custom collection "${slug}": has ${slug}IndexPath but no ${slug}Index template`);
195
+ }
196
+ if (detailPath && !detailTemplate) {
197
+ warnings.push(`- Custom collection "${slug}": has ${slug}DetailPath but no ${slug}Detail template`);
198
+ }
199
+ // Note: indexPath and detailPath can be the same (e.g., both "/videos")
200
+ // The system distinguishes by whether a slug segment exists after the path
201
+ }
202
+ // Warn about deprecated collectionTemplates format
203
+ if (templates.collectionTemplates) {
204
+ warnings.push('- cmsTemplates.collectionTemplates: This nested format is deprecated. Use flat format instead: {slug}Index, {slug}Detail, {slug}IndexPath, {slug}DetailPath');
205
+ }
123
206
  }
124
207
  // Build result
125
208
  let output = '';
209
+ // Build custom collections summary
210
+ const customCollectionsSummary = customCollections.size > 0
211
+ ? `\n- Custom collections: ${Array.from(customCollections).join(', ')} (using flat format: {slug}Index, {slug}Detail, {slug}IndexPath, {slug}DetailPath)`
212
+ : '';
126
213
  if (errors.length === 0 && warnings.length === 0) {
127
214
  output = `✅ MANIFEST VALID
128
215
 
@@ -130,7 +217,7 @@ The manifest.json structure is correct.
130
217
 
131
218
  Summary:
132
219
  - ${m.pages?.length || 0} static page(s) defined
133
- - CMS templates: ${templates ? 'configured in manifest' : 'not configured (can be set via Settings → CMS Templates after upload)'}
220
+ - CMS templates: ${templates ? 'configured in manifest' : 'not configured (can be set via Settings → CMS Templates after upload)'}${customCollectionsSummary}
134
221
  - Head HTML: ${m.defaultHeadHtml ? 'configured' : 'not configured'}`;
135
222
  }
136
223
  else if (errors.length === 0) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "multisite-cms-mcp",
3
- "version": "1.0.21",
3
+ "version": "1.0.22",
4
4
  "description": "MCP server for AI-assisted website conversion to CMS format. Provides validation, examples, and schema tools.",
5
5
  "main": "dist/index.js",
6
6
  "bin": {