multisite-cms-mcp 1.5.11 → 1.6.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/README.md CHANGED
@@ -77,6 +77,21 @@ These tools require a Fast Mode account. The MCP server will automatically open
77
77
  | `create_site` | Create a new Fast Mode project |
78
78
  | `deploy_package` | Deploy a website package to Fast Mode |
79
79
  | `sync_schema` | Create collections and fields in your project |
80
+ | `generate_sample_items` | Generate placeholder content for collections |
81
+
82
+ ### CMS Item Management Tools
83
+
84
+ Manage content directly from your AI assistant:
85
+
86
+ | Tool | Description |
87
+ |------|-------------|
88
+ | `create_cms_item` | Create a new item in a collection (blog post, team member, etc.) |
89
+ | `list_cms_items` | List items in a collection with optional sorting/filtering |
90
+ | `get_cms_item` | Get a single item by its slug |
91
+ | `update_cms_item` | Update an existing item's name, data, or publish status |
92
+ | `delete_cms_item` | Delete an item (requires explicit user confirmation) |
93
+
94
+ **⚠️ Delete Safety:** The `delete_cms_item` tool requires `confirmDelete: true` and will prompt you to ask the user for permission before deleting anything.
80
95
 
81
96
  ---
82
97
 
@@ -281,6 +296,25 @@ When you validate a template with a project ID, the tool will:
281
296
  "Validate my blog_post.html template against my project"
282
297
  ```
283
298
 
299
+ ### Content Management
300
+
301
+ ```
302
+ # List all blog posts
303
+ "Show me all items in my blog collection"
304
+
305
+ # Get details of a specific post
306
+ "Get the blog post with slug 'my-first-post'"
307
+
308
+ # Create a new blog post
309
+ "Create a new blog post titled 'AI in 2025' with content about artificial intelligence trends"
310
+
311
+ # Update an existing post
312
+ "Update the blog post 'my-first-post' to set featured to true"
313
+
314
+ # Delete a post (AI will ask for confirmation first)
315
+ "Delete the blog post 'old-draft-post'"
316
+ ```
317
+
284
318
  ---
285
319
 
286
320
  ## Authentication
@@ -386,6 +420,11 @@ Use the `get_conversion_guide` tool for detailed instructions.
386
420
 
387
421
  ## Changelog
388
422
 
423
+ ### v1.6.0
424
+ - **CMS Item Management** — New tools: `create_cms_item`, `list_cms_items`, `get_cms_item`, `update_cms_item`, `delete_cms_item`
425
+ - **Delete Safety** — `delete_cms_item` requires explicit `confirmDelete: true` and prompts AI to ask user for permission
426
+ - **External API Client** — New helper for calling external API endpoints with tenant context
427
+
389
428
  ### v1.5.5
390
429
  - **Removed `get_schema`** — Use `get_tenant_schema` for project-specific schema
391
430
  - **Two-Phase Schema Sync** — Collections created first, then all fields (relation fields always work)
package/dist/index.js CHANGED
@@ -16,6 +16,7 @@ const deploy_package_1 = require("./tools/deploy-package");
16
16
  const get_field_types_1 = require("./tools/get-field-types");
17
17
  const sync_schema_1 = require("./tools/sync-schema");
18
18
  const generate_samples_1 = require("./tools/generate-samples");
19
+ const cms_items_1 = require("./tools/cms-items");
19
20
  const server = new index_js_1.Server({
20
21
  name: 'multisite-cms',
21
22
  version: '1.0.0',
@@ -313,6 +314,153 @@ const TOOLS = [
313
314
  required: ['projectId'],
314
315
  },
315
316
  },
317
+ {
318
+ name: 'create_cms_item',
319
+ description: 'Create a new item in a CMS collection. Use this to add content like blog posts, team members, or any custom collection items. Requires authentication.',
320
+ inputSchema: {
321
+ type: 'object',
322
+ properties: {
323
+ projectId: {
324
+ type: 'string',
325
+ description: 'Project ID (UUID) or project name.',
326
+ },
327
+ collectionSlug: {
328
+ type: 'string',
329
+ description: 'The collection slug to add the item to (e.g., "blog", "team").',
330
+ },
331
+ name: {
332
+ type: 'string',
333
+ description: 'The name/title of the item.',
334
+ },
335
+ slug: {
336
+ type: 'string',
337
+ description: 'Optional URL slug. Auto-generated from name if not provided.',
338
+ },
339
+ data: {
340
+ type: 'object',
341
+ description: 'The field values for this item. Keys should match the collection field slugs.',
342
+ },
343
+ publishedAt: {
344
+ type: 'string',
345
+ description: 'Optional ISO date string. Defaults to now. Set to null for draft.',
346
+ },
347
+ },
348
+ required: ['projectId', 'collectionSlug', 'name', 'data'],
349
+ },
350
+ },
351
+ {
352
+ name: 'list_cms_items',
353
+ description: 'List items in a CMS collection. Use this to see what content exists in a collection. Requires authentication.',
354
+ inputSchema: {
355
+ type: 'object',
356
+ properties: {
357
+ projectId: {
358
+ type: 'string',
359
+ description: 'Project ID (UUID) or project name.',
360
+ },
361
+ collectionSlug: {
362
+ type: 'string',
363
+ description: 'The collection slug to list items from (e.g., "blog", "team").',
364
+ },
365
+ limit: {
366
+ type: 'number',
367
+ description: 'Optional: Maximum number of items to return.',
368
+ },
369
+ sort: {
370
+ type: 'string',
371
+ description: 'Optional: Field to sort by (e.g., "publishedAt", "name").',
372
+ },
373
+ order: {
374
+ type: 'string',
375
+ enum: ['asc', 'desc'],
376
+ description: 'Optional: Sort order. Defaults to desc.',
377
+ },
378
+ },
379
+ required: ['projectId', 'collectionSlug'],
380
+ },
381
+ },
382
+ {
383
+ name: 'get_cms_item',
384
+ description: 'Get a single CMS item by its slug. Use this to see the full details of a specific item. Requires authentication.',
385
+ inputSchema: {
386
+ type: 'object',
387
+ properties: {
388
+ projectId: {
389
+ type: 'string',
390
+ description: 'Project ID (UUID) or project name.',
391
+ },
392
+ collectionSlug: {
393
+ type: 'string',
394
+ description: 'The collection slug (e.g., "blog", "team").',
395
+ },
396
+ itemSlug: {
397
+ type: 'string',
398
+ description: 'The slug of the item to retrieve.',
399
+ },
400
+ },
401
+ required: ['projectId', 'collectionSlug', 'itemSlug'],
402
+ },
403
+ },
404
+ {
405
+ name: 'update_cms_item',
406
+ description: 'Update an existing CMS item. Use this to modify content in a collection. Requires authentication.',
407
+ inputSchema: {
408
+ type: 'object',
409
+ properties: {
410
+ projectId: {
411
+ type: 'string',
412
+ description: 'Project ID (UUID) or project name.',
413
+ },
414
+ collectionSlug: {
415
+ type: 'string',
416
+ description: 'The collection slug (e.g., "blog", "team").',
417
+ },
418
+ itemSlug: {
419
+ type: 'string',
420
+ description: 'The slug of the item to update.',
421
+ },
422
+ name: {
423
+ type: 'string',
424
+ description: 'Optional: New name for the item.',
425
+ },
426
+ data: {
427
+ type: 'object',
428
+ description: 'Optional: Updated field values. Only provided fields will be changed.',
429
+ },
430
+ publishedAt: {
431
+ type: 'string',
432
+ description: 'Optional: New publish date (ISO string), or null to unpublish.',
433
+ },
434
+ },
435
+ required: ['projectId', 'collectionSlug', 'itemSlug'],
436
+ },
437
+ },
438
+ {
439
+ name: 'delete_cms_item',
440
+ description: 'Delete a CMS item. IMPORTANT: You MUST get explicit permission from the user before calling this tool. Ask the user to confirm deletion first. Never delete without user confirmation. This action cannot be undone.',
441
+ inputSchema: {
442
+ type: 'object',
443
+ properties: {
444
+ projectId: {
445
+ type: 'string',
446
+ description: 'Project ID (UUID) or project name.',
447
+ },
448
+ collectionSlug: {
449
+ type: 'string',
450
+ description: 'The collection slug (e.g., "blog", "team").',
451
+ },
452
+ itemSlug: {
453
+ type: 'string',
454
+ description: 'The slug of the item to delete.',
455
+ },
456
+ confirmDelete: {
457
+ type: 'boolean',
458
+ description: 'Must be true. Only set this after the user has explicitly confirmed they want to delete the item.',
459
+ },
460
+ },
461
+ required: ['projectId', 'collectionSlug', 'itemSlug', 'confirmDelete'],
462
+ },
463
+ },
316
464
  ];
317
465
  // Handle list tools request
318
466
  server.setRequestHandler(types_js_1.ListToolsRequestSchema, async () => {
@@ -378,6 +526,50 @@ server.setRequestHandler(types_js_1.CallToolRequestSchema, async (request) => {
378
526
  collectionSlugs: params.collectionSlugs,
379
527
  });
380
528
  break;
529
+ case 'create_cms_item':
530
+ result = await (0, cms_items_1.createCmsItem)({
531
+ projectId: params.projectId,
532
+ collectionSlug: params.collectionSlug,
533
+ name: params.name,
534
+ slug: params.slug,
535
+ data: params.data,
536
+ publishedAt: params.publishedAt,
537
+ });
538
+ break;
539
+ case 'list_cms_items':
540
+ result = await (0, cms_items_1.listCmsItems)({
541
+ projectId: params.projectId,
542
+ collectionSlug: params.collectionSlug,
543
+ limit: params.limit,
544
+ sort: params.sort,
545
+ order: params.order,
546
+ });
547
+ break;
548
+ case 'get_cms_item':
549
+ result = await (0, cms_items_1.getCmsItem)({
550
+ projectId: params.projectId,
551
+ collectionSlug: params.collectionSlug,
552
+ itemSlug: params.itemSlug,
553
+ });
554
+ break;
555
+ case 'update_cms_item':
556
+ result = await (0, cms_items_1.updateCmsItem)({
557
+ projectId: params.projectId,
558
+ collectionSlug: params.collectionSlug,
559
+ itemSlug: params.itemSlug,
560
+ name: params.name,
561
+ data: params.data,
562
+ publishedAt: params.publishedAt,
563
+ });
564
+ break;
565
+ case 'delete_cms_item':
566
+ result = await (0, cms_items_1.deleteCmsItem)({
567
+ projectId: params.projectId,
568
+ collectionSlug: params.collectionSlug,
569
+ itemSlug: params.itemSlug,
570
+ confirmDelete: params.confirmDelete,
571
+ });
572
+ break;
381
573
  default:
382
574
  return {
383
575
  content: [{ type: 'text', text: `Unknown tool: ${name}` }],
@@ -62,4 +62,20 @@ export declare function isApiError(response: unknown): response is ApiError;
62
62
  * Check if error requires authentication
63
63
  */
64
64
  export declare function needsAuthError(error: ApiError): boolean;
65
+ /**
66
+ * Resolve a project identifier to a tenant ID
67
+ * Accepts either a UUID or a project name
68
+ */
69
+ export declare function resolveProjectId(projectIdentifier: string): Promise<{
70
+ tenantId: string;
71
+ } | {
72
+ error: string;
73
+ }>;
74
+ /**
75
+ * Make an authenticated external API request
76
+ * Uses the /external/* endpoints with tenant context
77
+ */
78
+ export declare function externalApiRequest<T>(tenantId: string, endpoint: string, options?: Omit<ApiRequestOptions, 'tenantId'>): Promise<{
79
+ data: T;
80
+ } | ApiError>;
65
81
  //# sourceMappingURL=api-client.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"api-client.d.ts","sourceRoot":"","sources":["../../src/lib/api-client.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAIH,MAAM,WAAW,SAAS;IACxB,MAAM,EAAE,MAAM,CAAC;IACf,SAAS,EAAE,MAAM,GAAG,IAAI,CAAC;CAC1B;AAED;;GAEG;AACH,wBAAgB,SAAS,IAAI,MAAM,CAElC;AAED;;GAEG;AACH,wBAAsB,iBAAiB,IAAI,OAAO,CAAC,SAAS,CAAC,CAgB5D;AAED;;GAEG;AACH,wBAAgB,YAAY,IAAI,SAAS,CAKxC;AAED;;GAEG;AACH,wBAAgB,gBAAgB,IAAI,OAAO,CAE1C;AAED;;GAEG;AACH,wBAAsB,mBAAmB,IAAI,OAAO,CAAC,OAAO,CAAC,CAO5D;AAED;;GAEG;AACH,wBAAgB,sBAAsB,IAAI,MAAM,CAS/C;AAED;;GAEG;AACH,MAAM,WAAW,iBAAiB;IAChC,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,MAAM,CAAC,EAAE,KAAK,GAAG,MAAM,GAAG,KAAK,GAAG,QAAQ,CAAC;IAC3C,IAAI,CAAC,EAAE,OAAO,CAAC;CAChB;AAED;;GAEG;AACH,MAAM,WAAW,QAAQ;IACvB,OAAO,EAAE,KAAK,CAAC;IACf,KAAK,EAAE,MAAM,CAAC;IACd,UAAU,EAAE,MAAM,CAAC;IACnB,SAAS,CAAC,EAAE,OAAO,CAAC;CACrB;AAED;;;GAGG;AACH,wBAAsB,UAAU,CAAC,CAAC,EAChC,QAAQ,EAAE,MAAM,EAChB,OAAO,GAAE,iBAAsB,GAC9B,OAAO,CAAC;IAAE,IAAI,EAAE,CAAC,CAAA;CAAE,GAAG,QAAQ,CAAC,CAoEjC;AAED;;GAEG;AACH,wBAAgB,UAAU,CAAC,QAAQ,EAAE,OAAO,GAAG,QAAQ,IAAI,QAAQ,CAOlE;AAED;;GAEG;AACH,wBAAgB,cAAc,CAAC,KAAK,EAAE,QAAQ,GAAG,OAAO,CAEvD"}
1
+ {"version":3,"file":"api-client.d.ts","sourceRoot":"","sources":["../../src/lib/api-client.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAIH,MAAM,WAAW,SAAS;IACxB,MAAM,EAAE,MAAM,CAAC;IACf,SAAS,EAAE,MAAM,GAAG,IAAI,CAAC;CAC1B;AAED;;GAEG;AACH,wBAAgB,SAAS,IAAI,MAAM,CAElC;AAED;;GAEG;AACH,wBAAsB,iBAAiB,IAAI,OAAO,CAAC,SAAS,CAAC,CAgB5D;AAED;;GAEG;AACH,wBAAgB,YAAY,IAAI,SAAS,CAKxC;AAED;;GAEG;AACH,wBAAgB,gBAAgB,IAAI,OAAO,CAE1C;AAED;;GAEG;AACH,wBAAsB,mBAAmB,IAAI,OAAO,CAAC,OAAO,CAAC,CAO5D;AAED;;GAEG;AACH,wBAAgB,sBAAsB,IAAI,MAAM,CAS/C;AAED;;GAEG;AACH,MAAM,WAAW,iBAAiB;IAChC,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,MAAM,CAAC,EAAE,KAAK,GAAG,MAAM,GAAG,KAAK,GAAG,QAAQ,CAAC;IAC3C,IAAI,CAAC,EAAE,OAAO,CAAC;CAChB;AAED;;GAEG;AACH,MAAM,WAAW,QAAQ;IACvB,OAAO,EAAE,KAAK,CAAC;IACf,KAAK,EAAE,MAAM,CAAC;IACd,UAAU,EAAE,MAAM,CAAC;IACnB,SAAS,CAAC,EAAE,OAAO,CAAC;CACrB;AAED;;;GAGG;AACH,wBAAsB,UAAU,CAAC,CAAC,EAChC,QAAQ,EAAE,MAAM,EAChB,OAAO,GAAE,iBAAsB,GAC9B,OAAO,CAAC;IAAE,IAAI,EAAE,CAAC,CAAA;CAAE,GAAG,QAAQ,CAAC,CAoEjC;AAED;;GAEG;AACH,wBAAgB,UAAU,CAAC,QAAQ,EAAE,OAAO,GAAG,QAAQ,IAAI,QAAQ,CAOlE;AAED;;GAEG;AACH,wBAAgB,cAAc,CAAC,KAAK,EAAE,QAAQ,GAAG,OAAO,CAEvD;AAYD;;;GAGG;AACH,wBAAsB,gBAAgB,CAAC,iBAAiB,EAAE,MAAM,GAAG,OAAO,CAAC;IAAE,QAAQ,EAAE,MAAM,CAAA;CAAE,GAAG;IAAE,KAAK,EAAE,MAAM,CAAA;CAAE,CAAC,CAwCnH;AAED;;;GAGG;AACH,wBAAsB,kBAAkB,CAAC,CAAC,EACxC,QAAQ,EAAE,MAAM,EAChB,QAAQ,EAAE,MAAM,EAChB,OAAO,GAAE,IAAI,CAAC,iBAAiB,EAAE,UAAU,CAAM,GAChD,OAAO,CAAC;IAAE,IAAI,EAAE,CAAC,CAAA;CAAE,GAAG,QAAQ,CAAC,CAOjC"}
@@ -13,6 +13,8 @@ exports.getAuthRequiredMessage = getAuthRequiredMessage;
13
13
  exports.apiRequest = apiRequest;
14
14
  exports.isApiError = isApiError;
15
15
  exports.needsAuthError = needsAuthError;
16
+ exports.resolveProjectId = resolveProjectId;
17
+ exports.externalApiRequest = externalApiRequest;
16
18
  const credentials_1 = require("./credentials");
17
19
  /**
18
20
  * Get API URL from environment or default
@@ -155,3 +157,45 @@ function isApiError(response) {
155
157
  function needsAuthError(error) {
156
158
  return error.needsAuth === true || error.statusCode === 401;
157
159
  }
160
+ /**
161
+ * Resolve a project identifier to a tenant ID
162
+ * Accepts either a UUID or a project name
163
+ */
164
+ async function resolveProjectId(projectIdentifier) {
165
+ // Check if it looks like a UUID
166
+ const uuidPattern = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
167
+ if (uuidPattern.test(projectIdentifier)) {
168
+ return { tenantId: projectIdentifier };
169
+ }
170
+ // Look up by name using /api/tenants
171
+ const response = await apiRequest('/api/tenants');
172
+ if (isApiError(response)) {
173
+ return { error: `Failed to look up project: ${response.error}` };
174
+ }
175
+ const projects = response.data;
176
+ // Find by exact name match (case-insensitive)
177
+ const match = projects.find(p => p.name.toLowerCase() === projectIdentifier.toLowerCase());
178
+ if (match) {
179
+ return { tenantId: match.id };
180
+ }
181
+ // Try partial match
182
+ const partialMatch = projects.find(p => p.name.toLowerCase().includes(projectIdentifier.toLowerCase()));
183
+ if (partialMatch) {
184
+ return { tenantId: partialMatch.id };
185
+ }
186
+ const availableProjects = projects.map(p => `- ${p.name} (${p.id})`).join('\n');
187
+ return {
188
+ error: `Project "${projectIdentifier}" not found.\n\nAvailable projects:\n${availableProjects || 'None'}`
189
+ };
190
+ }
191
+ /**
192
+ * Make an authenticated external API request
193
+ * Uses the /external/* endpoints with tenant context
194
+ */
195
+ async function externalApiRequest(tenantId, endpoint, options = {}) {
196
+ // External API endpoints are at /external/...
197
+ const fullEndpoint = endpoint.startsWith('/external')
198
+ ? endpoint
199
+ : `/external${endpoint.startsWith('/') ? endpoint : '/' + endpoint}`;
200
+ return apiRequest(fullEndpoint, { ...options, tenantId });
201
+ }
@@ -0,0 +1,56 @@
1
+ /**
2
+ * CMS Item Management Tools
3
+ *
4
+ * CRUD operations for managing items in CMS collections via the external API.
5
+ */
6
+ /**
7
+ * Create a new item in a CMS collection
8
+ */
9
+ export declare function createCmsItem(params: {
10
+ projectId: string;
11
+ collectionSlug: string;
12
+ name: string;
13
+ slug?: string;
14
+ data: Record<string, unknown>;
15
+ publishedAt?: string;
16
+ }): Promise<string>;
17
+ /**
18
+ * List items in a CMS collection
19
+ */
20
+ export declare function listCmsItems(params: {
21
+ projectId: string;
22
+ collectionSlug: string;
23
+ limit?: number;
24
+ sort?: string;
25
+ order?: 'asc' | 'desc';
26
+ }): Promise<string>;
27
+ /**
28
+ * Get a single item by slug
29
+ */
30
+ export declare function getCmsItem(params: {
31
+ projectId: string;
32
+ collectionSlug: string;
33
+ itemSlug: string;
34
+ }): Promise<string>;
35
+ /**
36
+ * Update an existing item
37
+ */
38
+ export declare function updateCmsItem(params: {
39
+ projectId: string;
40
+ collectionSlug: string;
41
+ itemSlug: string;
42
+ name?: string;
43
+ data?: Record<string, unknown>;
44
+ publishedAt?: string | null;
45
+ }): Promise<string>;
46
+ /**
47
+ * Delete an item from a collection
48
+ * REQUIRES confirmDelete: true to execute
49
+ */
50
+ export declare function deleteCmsItem(params: {
51
+ projectId: string;
52
+ collectionSlug: string;
53
+ itemSlug: string;
54
+ confirmDelete: boolean;
55
+ }): Promise<string>;
56
+ //# sourceMappingURL=cms-items.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"cms-items.d.ts","sourceRoot":"","sources":["../../src/tools/cms-items.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAkDH;;GAEG;AACH,wBAAsB,aAAa,CAAC,MAAM,EAAE;IAC1C,SAAS,EAAE,MAAM,CAAC;IAClB,cAAc,EAAE,MAAM,CAAC;IACvB,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IAC9B,WAAW,CAAC,EAAE,MAAM,CAAC;CACtB,GAAG,OAAO,CAAC,MAAM,CAAC,CAiDlB;AAkBD;;GAEG;AACH,wBAAsB,YAAY,CAAC,MAAM,EAAE;IACzC,SAAS,EAAE,MAAM,CAAC;IAClB,cAAc,EAAE,MAAM,CAAC;IACvB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,KAAK,CAAC,EAAE,KAAK,GAAG,MAAM,CAAC;CACxB,GAAG,OAAO,CAAC,MAAM,CAAC,CAyClB;AA4CD;;GAEG;AACH,wBAAsB,UAAU,CAAC,MAAM,EAAE;IACvC,SAAS,EAAE,MAAM,CAAC;IAClB,cAAc,EAAE,MAAM,CAAC;IACvB,QAAQ,EAAE,MAAM,CAAC;CAClB,GAAG,OAAO,CAAC,MAAM,CAAC,CAgClB;AAoBD;;GAEG;AACH,wBAAsB,aAAa,CAAC,MAAM,EAAE;IAC1C,SAAS,EAAE,MAAM,CAAC;IAClB,cAAc,EAAE,MAAM,CAAC;IACvB,QAAQ,EAAE,MAAM,CAAC;IACjB,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,IAAI,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IAC/B,WAAW,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;CAC7B,GAAG,OAAO,CAAC,MAAM,CAAC,CAmDlB;AAkBD;;;GAGG;AACH,wBAAsB,aAAa,CAAC,MAAM,EAAE;IAC1C,SAAS,EAAE,MAAM,CAAC;IAClB,cAAc,EAAE,MAAM,CAAC;IACvB,QAAQ,EAAE,MAAM,CAAC;IACjB,aAAa,EAAE,OAAO,CAAC;CACxB,GAAG,OAAO,CAAC,MAAM,CAAC,CAqDlB"}
@@ -0,0 +1,298 @@
1
+ "use strict";
2
+ /**
3
+ * CMS Item Management Tools
4
+ *
5
+ * CRUD operations for managing items in CMS collections via the external API.
6
+ */
7
+ Object.defineProperty(exports, "__esModule", { value: true });
8
+ exports.createCmsItem = createCmsItem;
9
+ exports.listCmsItems = listCmsItems;
10
+ exports.getCmsItem = getCmsItem;
11
+ exports.updateCmsItem = updateCmsItem;
12
+ exports.deleteCmsItem = deleteCmsItem;
13
+ const api_client_1 = require("../lib/api-client");
14
+ const device_flow_1 = require("../lib/device-flow");
15
+ /**
16
+ * Helper to ensure authentication and resolve project ID
17
+ */
18
+ async function prepareRequest(projectId) {
19
+ // Check if we need to authenticate
20
+ if (await (0, api_client_1.needsAuthentication)()) {
21
+ const authResult = await (0, device_flow_1.ensureAuthenticated)();
22
+ if (!authResult.authenticated) {
23
+ return { success: false, message: authResult.message };
24
+ }
25
+ }
26
+ // Resolve project ID
27
+ const resolved = await (0, api_client_1.resolveProjectId)(projectId);
28
+ if ('error' in resolved) {
29
+ return { success: false, message: `# Project Not Found\n\n${resolved.error}` };
30
+ }
31
+ return { success: true, tenantId: resolved.tenantId };
32
+ }
33
+ /**
34
+ * Create a new item in a CMS collection
35
+ */
36
+ async function createCmsItem(params) {
37
+ const prep = await prepareRequest(params.projectId);
38
+ if (!prep.success)
39
+ return prep.message;
40
+ const response = await (0, api_client_1.externalApiRequest)(prep.tenantId, `/collections/${params.collectionSlug}/items`, {
41
+ method: 'POST',
42
+ body: {
43
+ name: params.name,
44
+ slug: params.slug,
45
+ data: params.data,
46
+ publishedAt: params.publishedAt || new Date().toISOString(),
47
+ },
48
+ });
49
+ if ((0, api_client_1.isApiError)(response)) {
50
+ if ((0, api_client_1.needsAuthError)(response)) {
51
+ const authResult = await (0, device_flow_1.ensureAuthenticated)();
52
+ if (!authResult.authenticated)
53
+ return authResult.message;
54
+ // Retry
55
+ const retry = await (0, api_client_1.externalApiRequest)(prep.tenantId, `/collections/${params.collectionSlug}/items`, {
56
+ method: 'POST',
57
+ body: {
58
+ name: params.name,
59
+ slug: params.slug,
60
+ data: params.data,
61
+ publishedAt: params.publishedAt || new Date().toISOString(),
62
+ },
63
+ });
64
+ if ((0, api_client_1.isApiError)(retry)) {
65
+ return `# Error Creating Item\n\n${retry.error}\n\n**Status:** ${retry.statusCode}`;
66
+ }
67
+ return formatCreatedItem(retry.data, params.collectionSlug);
68
+ }
69
+ return `# Error Creating Item\n\n${response.error}\n\n**Status:** ${response.statusCode}`;
70
+ }
71
+ return formatCreatedItem(response.data, params.collectionSlug);
72
+ }
73
+ function formatCreatedItem(item, collectionSlug) {
74
+ return `# Item Created Successfully
75
+
76
+ **Collection:** ${collectionSlug}
77
+ **Name:** ${item.name}
78
+ **Slug:** ${item.slug || 'N/A'}
79
+ **ID:** ${item.id}
80
+ **Published:** ${item.publishedAt || 'Draft'}
81
+
82
+ ## Item Data
83
+ \`\`\`json
84
+ ${JSON.stringify(item.data, null, 2)}
85
+ \`\`\`
86
+ `;
87
+ }
88
+ /**
89
+ * List items in a CMS collection
90
+ */
91
+ async function listCmsItems(params) {
92
+ const prep = await prepareRequest(params.projectId);
93
+ if (!prep.success)
94
+ return prep.message;
95
+ // Build query params
96
+ const queryParams = new URLSearchParams();
97
+ if (params.limit)
98
+ queryParams.set('limit', String(params.limit));
99
+ if (params.sort)
100
+ queryParams.set('sort', params.sort);
101
+ if (params.order)
102
+ queryParams.set('order', params.order);
103
+ const queryString = queryParams.toString();
104
+ const endpoint = `/collections/${params.collectionSlug}/items${queryString ? '?' + queryString : ''}`;
105
+ const response = await (0, api_client_1.externalApiRequest)(prep.tenantId, endpoint, { method: 'GET' });
106
+ if ((0, api_client_1.isApiError)(response)) {
107
+ if ((0, api_client_1.needsAuthError)(response)) {
108
+ const authResult = await (0, device_flow_1.ensureAuthenticated)();
109
+ if (!authResult.authenticated)
110
+ return authResult.message;
111
+ const retry = await (0, api_client_1.externalApiRequest)(prep.tenantId, endpoint, { method: 'GET' });
112
+ if ((0, api_client_1.isApiError)(retry)) {
113
+ return `# Error Listing Items\n\n${retry.error}\n\n**Status:** ${retry.statusCode}`;
114
+ }
115
+ return formatItemsList(retry.data, params.collectionSlug);
116
+ }
117
+ return `# Error Listing Items\n\n${response.error}\n\n**Status:** ${response.statusCode}`;
118
+ }
119
+ return formatItemsList(response.data, params.collectionSlug);
120
+ }
121
+ function formatItemsList(items, collectionSlug) {
122
+ if (!items || items.length === 0) {
123
+ return `# No Items Found
124
+
125
+ Collection **${collectionSlug}** is empty.
126
+
127
+ Use \`create_cms_item\` to add items to this collection.
128
+ `;
129
+ }
130
+ let output = `# Items in ${collectionSlug}
131
+
132
+ Found ${items.length} item${items.length !== 1 ? 's' : ''}:
133
+
134
+ | Name | Slug | Published | ID |
135
+ |------|------|-----------|-----|
136
+ `;
137
+ for (const item of items) {
138
+ const published = item.publishedAt ? new Date(item.publishedAt).toLocaleDateString() : 'Draft';
139
+ output += `| ${item.name} | ${item.slug || '-'} | ${published} | \`${item.id.slice(0, 8)}...\` |\n`;
140
+ }
141
+ output += `
142
+ ---
143
+
144
+ ## Item Details
145
+
146
+ `;
147
+ for (const item of items) {
148
+ output += `### ${item.name}
149
+ - **Slug:** \`${item.slug || 'N/A'}\`
150
+ - **ID:** \`${item.id}\`
151
+ - **Data:** ${Object.keys(item.data).length} fields
152
+
153
+ `;
154
+ }
155
+ return output;
156
+ }
157
+ /**
158
+ * Get a single item by slug
159
+ */
160
+ async function getCmsItem(params) {
161
+ const prep = await prepareRequest(params.projectId);
162
+ if (!prep.success)
163
+ return prep.message;
164
+ const response = await (0, api_client_1.externalApiRequest)(prep.tenantId, `/collections/${params.collectionSlug}/items/${params.itemSlug}`, { method: 'GET' });
165
+ if ((0, api_client_1.isApiError)(response)) {
166
+ if ((0, api_client_1.needsAuthError)(response)) {
167
+ const authResult = await (0, device_flow_1.ensureAuthenticated)();
168
+ if (!authResult.authenticated)
169
+ return authResult.message;
170
+ const retry = await (0, api_client_1.externalApiRequest)(prep.tenantId, `/collections/${params.collectionSlug}/items/${params.itemSlug}`, { method: 'GET' });
171
+ if ((0, api_client_1.isApiError)(retry)) {
172
+ return `# Error Fetching Item\n\n${retry.error}\n\n**Status:** ${retry.statusCode}`;
173
+ }
174
+ return formatSingleItem(retry.data, params.collectionSlug);
175
+ }
176
+ return `# Error Fetching Item\n\n${response.error}\n\n**Status:** ${response.statusCode}`;
177
+ }
178
+ return formatSingleItem(response.data, params.collectionSlug);
179
+ }
180
+ function formatSingleItem(item, collectionSlug) {
181
+ return `# ${item.name}
182
+
183
+ **Collection:** ${collectionSlug}
184
+ **Slug:** \`${item.slug || 'N/A'}\`
185
+ **ID:** \`${item.id}\`
186
+ **Published:** ${item.publishedAt ? new Date(item.publishedAt).toLocaleString() : 'Draft'}
187
+ **Created:** ${new Date(item.createdAt).toLocaleString()}
188
+ **Updated:** ${new Date(item.updatedAt).toLocaleString()}
189
+
190
+ ## Item Data
191
+
192
+ \`\`\`json
193
+ ${JSON.stringify(item.data, null, 2)}
194
+ \`\`\`
195
+ `;
196
+ }
197
+ /**
198
+ * Update an existing item
199
+ */
200
+ async function updateCmsItem(params) {
201
+ const prep = await prepareRequest(params.projectId);
202
+ if (!prep.success)
203
+ return prep.message;
204
+ // Build the update body with only provided fields
205
+ const updateBody = {};
206
+ if (params.name !== undefined)
207
+ updateBody.name = params.name;
208
+ if (params.data !== undefined)
209
+ updateBody.data = params.data;
210
+ if (params.publishedAt !== undefined)
211
+ updateBody.publishedAt = params.publishedAt;
212
+ if (Object.keys(updateBody).length === 0) {
213
+ return `# No Updates Provided
214
+
215
+ You must provide at least one of: \`name\`, \`data\`, or \`publishedAt\` to update.
216
+ `;
217
+ }
218
+ const response = await (0, api_client_1.externalApiRequest)(prep.tenantId, `/collections/${params.collectionSlug}/items/${params.itemSlug}`, {
219
+ method: 'PUT',
220
+ body: updateBody,
221
+ });
222
+ if ((0, api_client_1.isApiError)(response)) {
223
+ if ((0, api_client_1.needsAuthError)(response)) {
224
+ const authResult = await (0, device_flow_1.ensureAuthenticated)();
225
+ if (!authResult.authenticated)
226
+ return authResult.message;
227
+ const retry = await (0, api_client_1.externalApiRequest)(prep.tenantId, `/collections/${params.collectionSlug}/items/${params.itemSlug}`, {
228
+ method: 'PUT',
229
+ body: updateBody,
230
+ });
231
+ if ((0, api_client_1.isApiError)(retry)) {
232
+ return `# Error Updating Item\n\n${retry.error}\n\n**Status:** ${retry.statusCode}`;
233
+ }
234
+ return formatUpdatedItem(retry.data, params.collectionSlug);
235
+ }
236
+ return `# Error Updating Item\n\n${response.error}\n\n**Status:** ${response.statusCode}`;
237
+ }
238
+ return formatUpdatedItem(response.data, params.collectionSlug);
239
+ }
240
+ function formatUpdatedItem(item, collectionSlug) {
241
+ return `# Item Updated Successfully
242
+
243
+ **Collection:** ${collectionSlug}
244
+ **Name:** ${item.name}
245
+ **Slug:** ${item.slug || 'N/A'}
246
+ **ID:** ${item.id}
247
+ **Updated:** ${new Date(item.updatedAt).toLocaleString()}
248
+
249
+ ## Updated Data
250
+ \`\`\`json
251
+ ${JSON.stringify(item.data, null, 2)}
252
+ \`\`\`
253
+ `;
254
+ }
255
+ /**
256
+ * Delete an item from a collection
257
+ * REQUIRES confirmDelete: true to execute
258
+ */
259
+ async function deleteCmsItem(params) {
260
+ // Safety check: require explicit confirmation
261
+ if (params.confirmDelete !== true) {
262
+ return `# Confirmation Required
263
+
264
+ **You must get explicit permission from the user before deleting items.**
265
+
266
+ To delete the item "${params.itemSlug}" from collection "${params.collectionSlug}":
267
+
268
+ 1. Ask the user: "Are you sure you want to delete [item name]? This cannot be undone."
269
+ 2. Only after the user confirms, call this tool again with \`confirmDelete: true\`
270
+
271
+ **Never delete without user confirmation.**
272
+ `;
273
+ }
274
+ const prep = await prepareRequest(params.projectId);
275
+ if (!prep.success)
276
+ return prep.message;
277
+ const response = await (0, api_client_1.externalApiRequest)(prep.tenantId, `/collections/${params.collectionSlug}/items/${params.itemSlug}`, { method: 'DELETE' });
278
+ if ((0, api_client_1.isApiError)(response)) {
279
+ if ((0, api_client_1.needsAuthError)(response)) {
280
+ const authResult = await (0, device_flow_1.ensureAuthenticated)();
281
+ if (!authResult.authenticated)
282
+ return authResult.message;
283
+ const retry = await (0, api_client_1.externalApiRequest)(prep.tenantId, `/collections/${params.collectionSlug}/items/${params.itemSlug}`, { method: 'DELETE' });
284
+ if ((0, api_client_1.isApiError)(retry)) {
285
+ return `# Error Deleting Item\n\n${retry.error}\n\n**Status:** ${retry.statusCode}`;
286
+ }
287
+ return `# Item Deleted
288
+
289
+ Successfully deleted item "${params.itemSlug}" from collection "${params.collectionSlug}".
290
+ `;
291
+ }
292
+ return `# Error Deleting Item\n\n${response.error}\n\n**Status:** ${response.statusCode}`;
293
+ }
294
+ return `# Item Deleted
295
+
296
+ Successfully deleted item "${params.itemSlug}" from collection "${params.collectionSlug}".
297
+ `;
298
+ }
@@ -1 +1 @@
1
- {"version":3,"file":"get-conversion-guide.d.ts","sourceRoot":"","sources":["../../src/tools/get-conversion-guide.ts"],"names":[],"mappings":"AAAA,KAAK,OAAO,GAAG,MAAM,GAAG,aAAa,GAAG,UAAU,GAAG,WAAW,GAAG,KAAK,GAAG,UAAU,GAAG,WAAW,GAAG,QAAQ,GAAG,OAAO,GAAG,QAAQ,GAAG,WAAW,GAAG,iBAAiB,CAAC;AAooCtK;;GAEG;AACH,wBAAsB,kBAAkB,CAAC,OAAO,EAAE,OAAO,GAAG,OAAO,CAAC,MAAM,CAAC,CAgF1E"}
1
+ {"version":3,"file":"get-conversion-guide.d.ts","sourceRoot":"","sources":["../../src/tools/get-conversion-guide.ts"],"names":[],"mappings":"AAAA,KAAK,OAAO,GAAG,MAAM,GAAG,aAAa,GAAG,UAAU,GAAG,WAAW,GAAG,KAAK,GAAG,UAAU,GAAG,WAAW,GAAG,QAAQ,GAAG,OAAO,GAAG,QAAQ,GAAG,WAAW,GAAG,iBAAiB,CAAC;AAooCtK;;GAEG;AACH,wBAAsB,kBAAkB,CAAC,OAAO,EAAE,OAAO,GAAG,OAAO,CAAC,MAAM,CAAC,CA+F1E"}
@@ -1162,9 +1162,24 @@ async function getConversionGuide(section) {
1162
1162
  | 4. BUILD | Create manifest.json, pages/, templates/, public/ | \`get_example\` |
1163
1163
  | 5. VALIDATE | Check all files before deploying | \`validate_manifest\`, \`validate_template\`, \`validate_package\` |
1164
1164
  | 6. DEPLOY | Upload package to project | \`deploy_package\` |
1165
+ | 7. CONTENT | Optionally manage CMS content | \`create_cms_item\`, \`list_cms_items\`, \`update_cms_item\` |
1165
1166
 
1166
1167
  **DO NOT skip steps. You MUST have a projectId before you can deploy.**
1167
1168
 
1169
+ ## CONTENT MANAGEMENT TOOLS
1170
+
1171
+ After deployment, you can manage CMS content directly:
1172
+
1173
+ | Tool | Description |
1174
+ |------|-------------|
1175
+ | \`create_cms_item\` | Create a new item (blog post, team member, etc.) |
1176
+ | \`list_cms_items\` | List items in a collection with optional filters |
1177
+ | \`get_cms_item\` | Get a single item by slug |
1178
+ | \`update_cms_item\` | Update an existing item |
1179
+ | \`delete_cms_item\` | Delete an item (**requires user confirmation**) |
1180
+
1181
+ **⚠️ DELETE SAFETY:** Never delete without asking the user first. The \`delete_cms_item\` tool requires \`confirmDelete: true\` which you should only set after explicit user permission.
1182
+
1168
1183
  ---
1169
1184
 
1170
1185
  ${SECTIONS.first_steps}
@@ -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,CAyCrD"}
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,CAqDrD"}
@@ -69,13 +69,13 @@ exports.AVAILABLE_FIELD_TYPES = [
69
69
  {
70
70
  value: 'select',
71
71
  label: 'Select',
72
- description: 'Dropdown with predefined options. Requires "options" parameter with comma-separated values.',
72
+ description: 'Dropdown with predefined options. Requires "options" parameter: comma-separated string like "Option1, Option2, Option3".',
73
73
  requiresOptions: true,
74
74
  },
75
75
  {
76
76
  value: 'multiSelect',
77
77
  label: 'Multi-Select',
78
- description: 'Multiple selections from options. Requires "options" parameter with comma-separated values.',
78
+ description: 'Multiple selections from options. Requires "options" parameter: comma-separated string like "Tag1, Tag2, Tag3".',
79
79
  requiresOptions: true,
80
80
  },
81
81
  {
@@ -122,11 +122,23 @@ When calling \`sync_schema\` to add fields, specify the \`type\` parameter:
122
122
  "fields": [
123
123
  { "slug": "heroImage", "name": "Hero Image", "type": "image" },
124
124
  { "slug": "featured", "name": "Featured", "type": "boolean" },
125
- { "slug": "category", "name": "Category", "type": "select", "options": "Tech,Business,Lifestyle" }
125
+ { "slug": "category", "name": "Category", "type": "select", "options": "Tech, Business, Lifestyle" },
126
+ { "slug": "tags", "name": "Tags", "type": "multiSelect", "options": "Featured, Popular, New, Sale" },
127
+ { "slug": "author", "name": "Author", "type": "relation", "referenceCollection": "authors" }
126
128
  ]
127
129
  }
128
130
  ]
129
131
  }
130
132
  \`\`\`
133
+
134
+ ## Select/MultiSelect Options Format
135
+
136
+ For **select** and **multiSelect** fields, provide options as a comma-separated string:
137
+
138
+ ✅ CORRECT: \`"options": "Red, Green, Blue"\`
139
+ ✅ CORRECT: \`"options": "Option1,Option2,Option3"\`
140
+
141
+ ❌ WRONG: \`"options": ["Red", "Green", "Blue"]\` (Don't use JSON array)
142
+ ❌ WRONG: \`"options": "\\"Red,Green,Blue\\""\` (Don't wrap in extra quotes)
131
143
  `;
132
144
  }
@@ -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,CA2VxE"}
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;AA2KD;;;;GAIG;AACH,wBAAsB,UAAU,CAAC,KAAK,EAAE,eAAe,GAAG,OAAO,CAAC,MAAM,CAAC,CA8VxE"}
@@ -23,6 +23,32 @@ This tool requires authentication to create collections and fields.
23
23
  Use \`list_projects\` to verify your authentication status.
24
24
  `;
25
25
  // ============ Helper Functions ============
26
+ /**
27
+ * Normalize select/multiSelect options to array format before sending to API.
28
+ * Handles comma-separated strings and converts to JSON array.
29
+ */
30
+ function normalizeOptionsForApi(options, fieldType) {
31
+ if (!options)
32
+ return undefined;
33
+ // Only process select/multiSelect fields
34
+ if (fieldType !== 'select' && fieldType !== 'multiSelect') {
35
+ return options;
36
+ }
37
+ // If it already looks like a JSON array, validate and return
38
+ if (options.startsWith('[')) {
39
+ try {
40
+ const parsed = JSON.parse(options);
41
+ if (Array.isArray(parsed))
42
+ return options;
43
+ }
44
+ catch {
45
+ // Invalid JSON, fall through to comma-separated handling
46
+ }
47
+ }
48
+ // Convert comma-separated string to JSON array
49
+ const arr = options.split(',').map(s => s.trim()).filter(Boolean);
50
+ return JSON.stringify(arr);
51
+ }
26
52
  /**
27
53
  * Validate a field type against available types
28
54
  */
@@ -315,6 +341,8 @@ ${resolved.error}
315
341
  skipped.fields++;
316
342
  continue;
317
343
  }
344
+ // Normalize options for select/multiSelect fields before sending
345
+ const normalizedOptions = normalizeOptionsForApi(job.field.options, job.field.type);
318
346
  // Create the field with retry logic
319
347
  let fieldRes = await (0, api_client_1.apiRequest)(`/api/collections/${collectionId}/fields`, {
320
348
  tenantId,
@@ -325,7 +353,7 @@ ${resolved.error}
325
353
  type: job.field.type,
326
354
  description: job.field.description,
327
355
  isRequired: job.field.isRequired,
328
- options: job.field.options,
356
+ options: normalizedOptions,
329
357
  referenceCollection: job.field.referenceCollection,
330
358
  },
331
359
  });
@@ -342,7 +370,7 @@ ${resolved.error}
342
370
  type: job.field.type,
343
371
  description: job.field.description,
344
372
  isRequired: job.field.isRequired,
345
- options: job.field.options,
373
+ options: normalizedOptions,
346
374
  referenceCollection: job.field.referenceCollection,
347
375
  },
348
376
  });
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "multisite-cms-mcp",
3
- "version": "1.5.11",
3
+ "version": "1.6.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": {