@mybe/contensa-mcp 1.0.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 ADDED
@@ -0,0 +1,765 @@
1
+ # Contensa MCP Server
2
+
3
+ MCP (Model Context Protocol) server for Contensa CMS. This enables AI assistants like Claude (via Cursor, Claude Desktop, etc.) to interact directly with your CMS.
4
+
5
+ ## Features
6
+
7
+ - **Content Type Management**: Create, read, update, delete content types
8
+ - **Field Management**: Add, modify, remove fields from content types
9
+ - **Content Entry Management**: Create, read, update, delete, publish content entries
10
+ - **Locale Management**: Create and manage multilingual content variants with AI translation
11
+ - **Environment Management**: Create, manage, and merge/sync environments
12
+ - **Media Management**: List and manage media assets
13
+ - **AI-Powered Features**: Generate schemas and field suggestions using AI
14
+
15
+ ## Installation
16
+
17
+ ```bash
18
+ npm install @mybe/contensa-mcp
19
+ ```
20
+
21
+ Or if using the monorepo:
22
+
23
+ ```bash
24
+ cd packages/mcp-server
25
+ npm install
26
+ npm run build
27
+ ```
28
+
29
+ ## Configuration
30
+
31
+ ### For Cursor
32
+
33
+ Add to your Cursor MCP settings (`~/.cursor/mcp.json` or workspace `.cursor/mcp.json`):
34
+
35
+ ```json
36
+ {
37
+ "mcpServers": {
38
+ "contensa": {
39
+ "command": "npx",
40
+ "args": ["@mybe/contensa-mcp"],
41
+ "env": {
42
+ "CONTENSA_API_KEY": "your-api-key-here",
43
+ "CONTENSA_PROJECT_ID": "your-default-project-id"
44
+ }
45
+ }
46
+ }
47
+ }
48
+ ```
49
+
50
+ Or if running from the monorepo:
51
+
52
+ ```json
53
+ {
54
+ "mcpServers": {
55
+ "contensa": {
56
+ "command": "node",
57
+ "args": ["/path/to/mybe-cms/packages/mcp-server/dist/cli.js"],
58
+ "env": {
59
+ "CONTENSA_API_KEY": "your-api-key-here",
60
+ "CONTENSA_PROJECT_ID": "your-default-project-id"
61
+ }
62
+ }
63
+ }
64
+ }
65
+ ```
66
+
67
+ ### For Claude Desktop
68
+
69
+ Add to your Claude Desktop config (`~/Library/Application Support/Claude/claude_desktop_config.json` on macOS):
70
+
71
+ ```json
72
+ {
73
+ "mcpServers": {
74
+ "contensa": {
75
+ "command": "npx",
76
+ "args": ["@mybe/contensa-mcp"],
77
+ "env": {
78
+ "CONTENSA_API_KEY": "your-api-key-here",
79
+ "CONTENSA_PROJECT_ID": "your-default-project-id"
80
+ }
81
+ }
82
+ }
83
+ }
84
+ ```
85
+
86
+ ## Environment Variables
87
+
88
+ | Variable | Required | Description |
89
+ |----------|----------|-------------|
90
+ | `CONTENSA_API_KEY` | Yes | Your Contensa API key |
91
+ | `CONTENSA_BASE_URL` | No | Custom API base URL (defaults to Contensa production API or localhost in dev) |
92
+ | `CONTENSA_PROJECT_ID` | No | Default project ID |
93
+
94
+ ### API Base URL
95
+
96
+ The MCP server automatically determines the API base URL:
97
+
98
+ - **Development** (`NODE_ENV=development`): Uses `http://localhost:3001/api/v1`
99
+ - **Production**: Uses Contensa's production API endpoint
100
+
101
+ You can override the default by setting `CONTENSA_BASE_URL` for custom API endpoints or different environments:
102
+
103
+ ```json
104
+ {
105
+ "mcpServers": {
106
+ "contensa": {
107
+ "command": "node",
108
+ "args": ["./packages/mcp-server/dist/cli.js"],
109
+ "env": {
110
+ "CONTENSA_API_KEY": "your-api-key",
111
+ "CONTENSA_BASE_URL": "https://your-custom-api.com/api/v1"
112
+ }
113
+ }
114
+ }
115
+ }
116
+ ```
117
+
118
+ ## Available Tools
119
+
120
+ ### Project Management
121
+
122
+ | Tool | Description |
123
+ |------|-------------|
124
+ | `contensa_list_projects` | List all projects |
125
+ | `contensa_get_project` | Get project details |
126
+ | `contensa_set_active_project` | Set the active project |
127
+
128
+ ### Content Type Management
129
+
130
+ | Tool | Description |
131
+ |------|-------------|
132
+ | `contensa_list_content_types` | List all content types in a project |
133
+ | `contensa_get_content_type` | Get content type details with fields |
134
+ | `contensa_create_content_type` | Create a new content type |
135
+ | `contensa_update_content_type` | Update a content type |
136
+ | `contensa_delete_content_type` | Delete a content type |
137
+
138
+ ### Field Management
139
+
140
+ | Tool | Description |
141
+ |------|-------------|
142
+ | `contensa_list_fields` | List fields of a content type |
143
+ | `contensa_create_field` | Add a field to a content type |
144
+ | `contensa_update_field` | Update a field |
145
+ | `contensa_delete_field` | Delete a field |
146
+
147
+ ### Content Entry Management
148
+
149
+ | Tool | Description |
150
+ |------|-------------|
151
+ | `contensa_list_content_entries` | List entries of a content type |
152
+ | `contensa_get_content_entry` | Get a content entry |
153
+ | `contensa_create_content_entry` | Create a new entry |
154
+ | `contensa_update_content_entry` | Update an entry |
155
+ | `contensa_delete_content_entry` | Delete an entry |
156
+ | `contensa_publish_content_entry` | Publish an entry |
157
+ | `contensa_unpublish_content_entry` | Unpublish an entry |
158
+
159
+ ### Locale Management
160
+
161
+ | Tool | Description |
162
+ |------|-------------|
163
+ | `contensa_get_supported_locales` | Get all supported locales in the system |
164
+ | `contensa_list_locale_variants` | List all locale variants of an entry |
165
+ | `contensa_create_locale_variant` | Create a new locale variant with optional AI translation |
166
+
167
+ ### Media Management
168
+
169
+ | Tool | Description |
170
+ |------|-------------|
171
+ | `contensa_list_media_assets` | List media assets |
172
+ | `contensa_get_media_asset` | Get media asset details |
173
+ | `contensa_delete_media_asset` | Delete a media asset |
174
+
175
+ ### Environment Management
176
+
177
+ | Tool | Description |
178
+ |------|-------------|
179
+ | `contensa_list_environments` | List all environments for a project |
180
+ | `contensa_get_environment` | Get environment details |
181
+ | `contensa_create_environment` | Create a new environment (empty or cloned) |
182
+ | `contensa_update_environment` | Update environment details |
183
+ | `contensa_delete_environment` | Delete an environment |
184
+ | `contensa_set_active_environment` | Set the active environment for content operations |
185
+ | `contensa_merge_environment` | Merge content from one environment to another |
186
+ | `contensa_sync_environment` | Sync content between environments (in development) |
187
+
188
+ ### AI Features
189
+
190
+ | Tool | Description |
191
+ |------|-------------|
192
+ | `contensa_suggest_fields` | AI-powered field suggestions |
193
+ | `contensa_generate_schema` | AI-powered schema generation |
194
+
195
+ ## Usage Examples
196
+
197
+ Once configured, you can interact with your CMS through natural language:
198
+
199
+ ### Example 1: Create a Blog Content Type
200
+
201
+ ```
202
+ "Create a new content type called 'Blog Post' with fields for title, content, author, and published date"
203
+ ```
204
+
205
+ The AI will use:
206
+ 1. `contensa_create_content_type` to create "Blog Post"
207
+ 2. `contensa_create_field` multiple times to add each field
208
+
209
+ ### Example 2: Create Content
210
+
211
+ ```
212
+ "Create a new blog post titled 'Hello World' with content 'This is my first post'"
213
+ ```
214
+
215
+ The AI will use:
216
+ 1. `contensa_list_content_types` to find the Blog Post type
217
+ 2. `contensa_list_fields` to get the field API names
218
+ 3. `contensa_create_content_entry` to create the entry with proper field names
219
+
220
+ ### Example 3: Publish Content
221
+
222
+ ```
223
+ "Publish all draft blog posts"
224
+ ```
225
+
226
+ The AI will use:
227
+ 1. `contensa_list_content_entries` with status filter
228
+ 2. `contensa_publish_content_entry` for each draft
229
+
230
+ ### Example 4: AI Schema Generation
231
+
232
+ ```
233
+ "Generate a schema for an e-commerce product catalog"
234
+ ```
235
+
236
+ The AI will use:
237
+ 1. `contensa_generate_schema` to get AI suggestions
238
+ 2. `contensa_create_content_type` to create the type
239
+ 3. `contensa_create_field` to add each suggested field
240
+
241
+ ### Example 5: Environment Merging
242
+
243
+ ```
244
+ "Merge the dev environment to master"
245
+ ```
246
+
247
+ The AI will use:
248
+ 1. `contensa_list_environments` to find the dev and master environment IDs
249
+ 2. `contensa_merge_environment` with dryRun=true to preview conflicts
250
+ 3. `contensa_merge_environment` with dryRun=false to perform the merge
251
+
252
+ ### Example 6: Preview Merge Conflicts
253
+
254
+ ```
255
+ "Show me what conflicts would occur if I merge staging to production"
256
+ ```
257
+
258
+ The AI will use:
259
+ 1. `contensa_list_environments` to find environment IDs
260
+ 2. `contensa_merge_environment` with dryRun=true to preview conflicts without making changes
261
+
262
+ ### Example 7: Create Multilingual Content
263
+
264
+ ```
265
+ "Create a French version of the blog post with ID 'post-123' and translate the content using AI"
266
+ ```
267
+
268
+ The AI will use:
269
+ 1. `contensa_get_content_entry` to get the original entry
270
+ 2. `contensa_create_locale_variant` with translateContent=true to create and translate
271
+
272
+ ### Example 8: Create Locale Variants for Referenced Content
273
+
274
+ ```
275
+ "Create a Spanish version of the product 'prod-456' and also create Spanish versions of all referenced entries"
276
+ ```
277
+
278
+ The AI will use:
279
+ 1. `contensa_create_locale_variant` with translateReferences=true to recursively create variants
280
+
281
+ ## Creating Content Entries
282
+
283
+ Creating content entries requires understanding the correct payload format and field structure.
284
+
285
+ ### Required Configuration
286
+
287
+ Before creating content entries, ensure you have:
288
+
289
+ 1. **API Key**: Set in `CONTENSA_API_KEY` environment variable
290
+ 2. **User ID**: Set in `CONTENSA_USER_ID` environment variable (required for all write operations)
291
+ 3. **Project ID**: Either set as active project or provided in each request
292
+
293
+ Example configuration:
294
+
295
+ ```json
296
+ {
297
+ "mcpServers": {
298
+ "contensa": {
299
+ "command": "npx",
300
+ "args": ["@mybe/contensa-mcp"],
301
+ "env": {
302
+ "CONTENSA_API_KEY": "your-api-key-here",
303
+ "CONTENSA_USER_ID": "your-user-id-here",
304
+ "CONTENSA_PROJECT_ID": "your-default-project-id"
305
+ }
306
+ }
307
+ }
308
+ }
309
+ ```
310
+
311
+ ### Payload Format
312
+
313
+ The `contensa_create_content_entry` tool requires:
314
+
315
+ | Parameter | Required | Description |
316
+ |-----------|----------|-------------|
317
+ | `contentTypeId` | Yes | The ID of the content type |
318
+ | `data` | Yes | Object with field values (keys must be field API names) |
319
+ | `projectId` | No* | Project ID (uses active project if not provided) |
320
+ | `userId` | No* | User ID (uses config userId if not provided) |
321
+ | `status` | No | Entry status: "draft" (default) or "published" |
322
+ | `locale` | No | Locale code (default: "en-US") |
323
+
324
+ *Required but can be set via configuration or active project/user
325
+
326
+ ### Field API Names
327
+
328
+ **CRITICAL**: The `data` object keys must use **field API names**, NOT:
329
+ - ❌ Field IDs (UUIDs)
330
+ - ❌ Display names (e.g., "Title", "Description")
331
+ - ✅ API names (e.g., "title", "description", "author_name")
332
+
333
+ ### Getting Field API Names
334
+
335
+ Always use `contensa_list_fields` to get the correct API names:
336
+
337
+ ```javascript
338
+ // Example response from contensa_list_fields
339
+ {
340
+ "fields": [
341
+ {
342
+ "id": "field-uuid-123",
343
+ "api_name": "title", // ← Use this as the key
344
+ "kind": "short-text",
345
+ "is_required": true
346
+ },
347
+ {
348
+ "id": "field-uuid-456",
349
+ "api_name": "content", // ← Use this as the key
350
+ "kind": "long-text",
351
+ "is_required": true
352
+ },
353
+ {
354
+ "id": "field-uuid-789",
355
+ "api_name": "author_name", // ← Use this as the key
356
+ "kind": "short-text",
357
+ "is_required": false
358
+ }
359
+ ]
360
+ }
361
+ ```
362
+
363
+ ### Example: Creating a Blog Post
364
+
365
+ **Step 1**: Get the content type and fields
366
+
367
+ ```
368
+ "List all fields for the Blog Post content type"
369
+ ```
370
+
371
+ Response shows:
372
+ - `title` (short-text, required)
373
+ - `content` (long-text, required)
374
+ - `author_name` (short-text, optional)
375
+ - `published_date` (date, optional)
376
+
377
+ **Step 2**: Create the entry with correct field API names
378
+
379
+ ```javascript
380
+ {
381
+ "contentTypeId": "blog-post-id",
382
+ "data": {
383
+ "title": "Hello World", // ✅ Correct: using API name
384
+ "content": "This is my first post", // ✅ Correct: using API name
385
+ "author_name": "John Doe" // ✅ Correct: using API name
386
+ },
387
+ "status": "draft"
388
+ }
389
+ ```
390
+
391
+ ### Common Errors and Solutions
392
+
393
+ #### Error: "Missing required fields"
394
+
395
+ **Cause**: Required fields are not provided in the `data` object, or keys don't match field API names.
396
+
397
+ **Solution**:
398
+ 1. Use `contensa_list_fields` to get all required fields
399
+ 2. Ensure all required fields are in the `data` object
400
+ 3. Verify keys match the `api_name` property exactly
401
+
402
+ Example error response:
403
+ ```json
404
+ {
405
+ "message": "Missing required fields",
406
+ "missingFields": ["title", "content"]
407
+ }
408
+ ```
409
+
410
+ #### Error: "Project ID is required"
411
+
412
+ **Cause**: No active project set and no `projectId` provided.
413
+
414
+ **Solution**:
415
+ 1. Set active project: `contensa_set_active_project`
416
+ 2. Or provide `projectId` in the request
417
+ 3. Or set `CONTENSA_PROJECT_ID` in configuration
418
+
419
+ #### Error: "User ID is required"
420
+
421
+ **Cause**: No `CONTENSA_USER_ID` in configuration and no `userId` provided.
422
+
423
+ **Solution**:
424
+ 1. Add `CONTENSA_USER_ID` to your MCP configuration
425
+ 2. Or provide `userId` in each request
426
+
427
+ #### Error: "Invalid data format"
428
+
429
+ **Cause**: The `data` parameter is not an object or is a stringified JSON.
430
+
431
+ **Solution**:
432
+ Ensure `data` is a proper object:
433
+ ```javascript
434
+ // ✅ Correct
435
+ { "data": { "title": "Hello" } }
436
+
437
+ // ❌ Wrong
438
+ { "data": "{\"title\": \"Hello\"}" }
439
+ ```
440
+
441
+ ### Handling Optional Fields
442
+
443
+ **IMPORTANT**: When creating content entries, handle optional fields correctly to avoid frontend errors:
444
+
445
+ - ✅ **Correct**: Omit optional fields entirely from the `data` object if you don't have values
446
+ - ❌ **Wrong**: Pass empty objects `{}` for optional fields (causes "Failed to construct 'URL'" errors)
447
+ - ❌ **Wrong**: Pass `null` or `undefined` for optional fields
448
+
449
+ **Example - Correct approach**:
450
+ ```javascript
451
+ // User says "I'll add the image later"
452
+ {
453
+ "contentTypeId": "blog-post-id",
454
+ "data": {
455
+ "title": "Hello World",
456
+ "content": "This is my first post"
457
+ // ✅ image field is omitted entirely
458
+ }
459
+ }
460
+ ```
461
+
462
+ **Example - Wrong approach**:
463
+ ```javascript
464
+ // ❌ This causes frontend errors!
465
+ {
466
+ "contentTypeId": "blog-post-id",
467
+ "data": {
468
+ "title": "Hello World",
469
+ "content": "This is my first post",
470
+ "image": {} // ❌ Empty object causes "Invalid URL" errors
471
+ }
472
+ }
473
+ ```
474
+
475
+ **Why this matters**: Empty objects for media fields cause the frontend to attempt constructing URLs from invalid data, resulting in errors. The MCP server now automatically removes empty objects, `null`, and `undefined` values before sending data to the API.
476
+
477
+ ### Field Value Types
478
+
479
+ Different field types expect different value formats:
480
+
481
+ | Field Type | Value Format | Example | Notes |
482
+ |------------|--------------|---------|-------|
483
+ | `short-text` | String | `"Hello World"` | Omit if optional and no value |
484
+ | `long-text` | String | `"Long content..."` | Omit if optional and no value |
485
+ | `number` | Number | `42` | Omit if optional and no value |
486
+ | `date` | ISO 8601 string | `"2024-01-15T10:30:00Z"` | Omit if optional and no value |
487
+ | `boolean` | Boolean | `true` or `false` | Omit if optional and no value |
488
+ | `list` | Array of strings | `["tag1", "tag2"]` | Empty arrays `[]` are valid |
489
+ | `media` | Media asset ID (string) | `"media-uuid-123"` | **Never use `{}`** - omit if no value |
490
+ | `reference` | Entry ID (string) | `"entry-uuid-456"` | Omit if optional and no value |
491
+ | `references-many` | Array of entry IDs | `["entry-1", "entry-2"]` | Empty arrays `[]` are valid |
492
+
493
+ ### Complete Example Workflow
494
+
495
+ ```
496
+ User: "Create a blog post about AI with title 'The Future of AI' and content 'AI is transforming...'"
497
+
498
+ AI Assistant:
499
+ 1. Lists content types to find "Blog Post" → gets contentTypeId
500
+ 2. Lists fields for Blog Post → gets field API names and requirements
501
+ 3. Creates entry:
502
+ {
503
+ "contentTypeId": "blog-post-id",
504
+ "data": {
505
+ "title": "The Future of AI",
506
+ "content": "AI is transforming..."
507
+ },
508
+ "status": "draft"
509
+ }
510
+ 4. Returns success with entry ID
511
+ ```
512
+
513
+ ### Best Practices
514
+
515
+ 1. **Always get field API names first**: Use `contensa_list_fields` before creating entries
516
+ 2. **Validate required fields**: Check which fields are required before submitting
517
+ 3. **Use correct data types**: Match the field type's expected value format
518
+ 4. **Set active project**: Use `contensa_set_active_project` to avoid repeating projectId
519
+ 5. **Handle errors gracefully**: Check error messages for missing fields and retry with corrections
520
+
521
+ ## Field Types
522
+
523
+ Available field types for `contensa_create_field`:
524
+
525
+ | Type | Description |
526
+ |------|-------------|
527
+ | `short-text` | Single line text |
528
+ | `long-text` | Multi-line text / rich content |
529
+ | `number` | Numeric values |
530
+ | `date` | Date/datetime values |
531
+ | `boolean` | True/false values |
532
+ | `media` | Images and files |
533
+ | `list` | Array of values |
534
+ | `reference` | Link to another content entry |
535
+ | `references-many` | Multiple links to content entries |
536
+
537
+ ## Environment Merging
538
+
539
+ The MCP server supports merging content between environments (e.g., dev → staging → master).
540
+
541
+ ### Merge Strategies
542
+
543
+ When merging environments, you can specify how to handle conflicts:
544
+
545
+ | Strategy | Description |
546
+ |----------|-------------|
547
+ | `skip` | Skip conflicting items (default) |
548
+ | `replace` | Replace target with source content |
549
+ | `merge` | Attempt to merge changes |
550
+ | `create-new` | Create new entries for conflicts |
551
+
552
+ ### Dry Run Mode
553
+
554
+ Always preview changes before merging to production:
555
+
556
+ ```
557
+ "Do a dry run merge from dev to master"
558
+ ```
559
+
560
+ This will show you:
561
+ - Number of content types to be merged
562
+ - Number of entries to be merged
563
+ - Any conflicts that would occur
564
+ - Errors that would prevent the merge
565
+
566
+ ### Master Environment Protection
567
+
568
+ By default, merging into the master environment requires explicit approval (`requireMasterApproval: false`). This prevents accidental overwrites of production content.
569
+
570
+ ### Merge Workflow Example
571
+
572
+ 1. **Preview the merge**:
573
+ ```
574
+ "Show me what would happen if I merge dev to master"
575
+ ```
576
+
577
+ 2. **Review conflicts** and decide on resolution strategies
578
+
579
+ 3. **Execute the merge**:
580
+ ```
581
+ "Merge dev to master, replacing conflicts and disabling master approval"
582
+ ```
583
+
584
+ ### Sync vs Merge
585
+
586
+ - **Merge** (`contensa_merge_environment`): Fully functional, supports dry-run, conflict resolution strategies
587
+ - **Sync** (`contensa_sync_environment`): Currently in development, will support three-way merge logic
588
+
589
+ ## Locale Management
590
+
591
+ The MCP server provides comprehensive support for creating and managing multilingual content.
592
+
593
+ ### Supported Locales
594
+
595
+ The system supports 31 locales across multiple regions:
596
+
597
+ **Default**: `en-US` (English - United States)
598
+
599
+ **Europe**: `fr-FR`, `es-ES`, `de-DE`, `it-IT`, `pt-PT`, `nl-NL`, `sv-SE`, `pl-PL`, `ru-RU`, `en-GB`
600
+
601
+ **Americas**: `pt-BR`, `es-MX`, `es-AR`, `en-CA`, `fr-CA`
602
+
603
+ **Asia-Pacific**: `ja-JP`, `ko-KR`, `zh-CN`, `zh-TW`, `hi-IN`, `th-TH`, `vi-VN`, `id-ID`, `ms-MY`, `fil-PH`, `bn-BD`, `en-AU`
604
+
605
+ **Middle East & Africa**: `ar-SA`, `tr-TR`, `he-IL`
606
+
607
+ Use `contensa_get_supported_locales` to get the full list with locale codes, names, and flags.
608
+
609
+ ### Creating Locale Variants
610
+
611
+ Locale variants allow you to create multilingual versions of your content. Each variant:
612
+ - Shares the same content structure (fields) as the original
613
+ - Has its own data in the target language
614
+ - Can optionally be AI-translated from the source locale
615
+ - Can reference the same entries or have locale-specific references
616
+
617
+ ### Translation Options
618
+
619
+ When creating a locale variant, you have two key options:
620
+
621
+ #### 1. `translateContent` (default: true)
622
+
623
+ - **true**: Uses AI to translate text fields (short-text, long-text, list) to the target locale
624
+ - **false**: Copies content as-is without translation
625
+
626
+ **Note**: Reference fields are never translated - they maintain the same entry IDs across locales.
627
+
628
+ #### 2. `translateReferences` (default: false)
629
+
630
+ - **true**: Recursively creates locale variants for all referenced entries
631
+ - **false**: Only creates a variant for the main entry
632
+
633
+ **Warning**: Setting `translateReferences=true` can create many entries if you have deeply nested references. Use with caution.
634
+
635
+ ### How Translation Works
636
+
637
+ The locale system uses AI-powered translation with the following behavior:
638
+
639
+ 1. **Translatable Fields**: Only text-based fields are translated (short-text, long-text, number, list)
640
+ 2. **Reference Fields**: Reference and references-many fields are copied as-is (same entry IDs)
641
+ 3. **Retry Logic**: Translation attempts up to 2 retries on failure
642
+ 4. **Fallback**: If translation fails after retries, content is copied without translation
643
+ 5. **Metadata**: Translated entries are marked with `is_translated: true` in metadata
644
+
645
+ ### Edge Cases and Behavior
646
+
647
+ #### Existing Variants
648
+
649
+ If a locale variant already exists for an entry, the API returns a 409 Conflict error. You cannot create duplicate locale variants.
650
+
651
+ #### Circular References
652
+
653
+ The system prevents infinite loops from circular references by tracking visited entry IDs during recursive locale creation.
654
+
655
+ #### Partial Translation
656
+
657
+ If translation fails for some fields but succeeds for others, the variant is still created with whatever translation succeeded.
658
+
659
+ #### Referenced Entry Variants
660
+
661
+ When `translateReferences=true`:
662
+ - The system checks if a locale variant already exists for each referenced entry
663
+ - If it exists, it uses the existing variant
664
+ - If not, it creates a new variant (with or without translation based on settings)
665
+ - All created entries are returned in the response
666
+
667
+ ### Locale Workflow Example
668
+
669
+ 1. **Check supported locales**:
670
+ ```
671
+ "What locales are supported?"
672
+ ```
673
+
674
+ 2. **Create a simple locale variant**:
675
+ ```
676
+ "Create a French version of blog post 'post-123' with AI translation"
677
+ ```
678
+
679
+ 3. **Create variant without translation**:
680
+ ```
681
+ "Create a German version of product 'prod-456' but don't translate the content"
682
+ ```
683
+
684
+ 4. **Create variant with referenced entries**:
685
+ ```
686
+ "Create a Spanish version of the homepage entry and all its referenced content"
687
+ ```
688
+
689
+ 5. **List all variants**:
690
+ ```
691
+ "Show me all locale variants of entry 'post-123'"
692
+ ```
693
+
694
+ ### Locale Validation
695
+
696
+ Locale codes must match the BCP-47 format (language-COUNTRY):
697
+ - ✅ Valid: `en-US`, `fr-FR`, `zh-CN`
698
+ - ❌ Invalid: `en`, `french`, `EN-us`
699
+
700
+ The client automatically validates locale codes against the supported locales list.
701
+
702
+ ### Response Format
703
+
704
+ When creating a locale variant, the response includes:
705
+ - `variant`: The created locale variant entry
706
+ - `createdEntries`: Array of all entries created (if `translateReferences=true`)
707
+ - `totalCreated`: Total number of entries created (including the main variant)
708
+ - `message`: Success message
709
+
710
+ Example response:
711
+ ```json
712
+ {
713
+ "variant": { "id": "new-variant-id", "locale": "fr-FR", ... },
714
+ "createdEntries": [
715
+ { "id": "ref-1-fr", "title": "Referenced Entry 1" },
716
+ { "id": "ref-2-fr", "title": "Referenced Entry 2" }
717
+ ],
718
+ "totalCreated": 3,
719
+ "message": "Locale variant created successfully with references"
720
+ }
721
+ ```
722
+
723
+ ## Getting Your API Key
724
+
725
+ 1. Log in to your Contensa dashboard
726
+ 2. Go to Settings → API Keys
727
+ 3. Generate a new API key
728
+ 4. Copy the key and add it to your MCP configuration
729
+
730
+ ## Development
731
+
732
+ ```bash
733
+ # Install dependencies
734
+ npm install
735
+
736
+ # Build
737
+ npm run build
738
+
739
+ # Run in development
740
+ npm run dev
741
+
742
+ # Start the server
743
+ npm start
744
+ ```
745
+
746
+ ## Troubleshooting
747
+
748
+ ### "API key is required" error
749
+
750
+ Make sure `CONTENSA_API_KEY` is set in your MCP configuration.
751
+
752
+ ### Tools not showing up
753
+
754
+ 1. Restart Cursor/Claude Desktop after updating the config
755
+ 2. Check the MCP server logs for errors
756
+ 3. Verify the path to the CLI is correct
757
+
758
+ ### Permission errors
759
+
760
+ Ensure your API key has the necessary permissions for the operations you're trying to perform.
761
+
762
+ ## License
763
+
764
+ MIT
765
+