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 +39 -0
- package/dist/index.js +192 -0
- package/dist/lib/api-client.d.ts +16 -0
- package/dist/lib/api-client.d.ts.map +1 -1
- package/dist/lib/api-client.js +44 -0
- package/dist/tools/cms-items.d.ts +56 -0
- package/dist/tools/cms-items.d.ts.map +1 -0
- package/dist/tools/cms-items.js +298 -0
- package/dist/tools/get-conversion-guide.d.ts.map +1 -1
- package/dist/tools/get-conversion-guide.js +15 -0
- package/dist/tools/get-field-types.d.ts.map +1 -1
- package/dist/tools/get-field-types.js +15 -3
- package/dist/tools/sync-schema.d.ts.map +1 -1
- package/dist/tools/sync-schema.js +30 -2
- package/package.json +1 -1
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}` }],
|
package/dist/lib/api-client.d.ts
CHANGED
|
@@ -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"}
|
package/dist/lib/api-client.js
CHANGED
|
@@ -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,
|
|
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,
|
|
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
|
|
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
|
|
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;
|
|
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:
|
|
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:
|
|
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.
|
|
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": {
|