@contentful/mcp-server 1.0.4 → 1.0.5
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
|
@@ -48,6 +48,10 @@ This MCP server provides a comprehensive set of tools for content management, al
|
|
|
48
48
|
|
|
49
49
|
_Note: This requires [Cursor](https://cursor.com/) to be installed. If the link doesn't work, try the manual installation below._
|
|
50
50
|
|
|
51
|
+
**Claude Desktop:**
|
|
52
|
+
|
|
53
|
+
Download the .dxt configuration file [here](https://github.com/contentful/contentful-mcp-server/releases) from the latest release and import it into Claude Desktop to automatically configure the MCP server with your environment variables.
|
|
54
|
+
|
|
51
55
|
#### Install from source
|
|
52
56
|
|
|
53
57
|
```bash
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
import { vi } from 'vitest';
|
|
2
|
+
import { createToolClient } from '../../utils/tools.js';
|
|
3
|
+
/**
|
|
4
|
+
* Shared mock objects for entry tests
|
|
5
|
+
* Provides standardized mock client and entry objects used across all entry tests
|
|
6
|
+
*/
|
|
7
|
+
// Create mock functions for the client
|
|
8
|
+
export const mockEntryGet = vi.fn();
|
|
9
|
+
export const mockEntryCreate = vi.fn();
|
|
10
|
+
export const mockEntryUpdate = vi.fn();
|
|
11
|
+
export const mockEntryDelete = vi.fn();
|
|
12
|
+
export const mockEntryPublish = vi.fn();
|
|
13
|
+
export const mockEntryUnpublish = vi.fn();
|
|
14
|
+
export const mockEntryGetMany = vi.fn();
|
|
15
|
+
export const mockBulkActionPublish = vi.fn();
|
|
16
|
+
export const mockBulkActionUnpublish = vi.fn();
|
|
17
|
+
/**
|
|
18
|
+
* Standard mock Contentful client with all entry operations
|
|
19
|
+
*/
|
|
20
|
+
export const mockClient = {
|
|
21
|
+
entry: {
|
|
22
|
+
get: mockEntryGet,
|
|
23
|
+
create: mockEntryCreate,
|
|
24
|
+
update: mockEntryUpdate,
|
|
25
|
+
delete: mockEntryDelete,
|
|
26
|
+
publish: mockEntryPublish,
|
|
27
|
+
unpublish: mockEntryUnpublish,
|
|
28
|
+
getMany: mockEntryGetMany,
|
|
29
|
+
},
|
|
30
|
+
bulkAction: {
|
|
31
|
+
publish: mockBulkActionPublish,
|
|
32
|
+
unpublish: mockBulkActionUnpublish,
|
|
33
|
+
},
|
|
34
|
+
};
|
|
35
|
+
/**
|
|
36
|
+
* Sets up the mock client for tests
|
|
37
|
+
* Call this in beforeEach to ensure the mock is properly configured
|
|
38
|
+
*/
|
|
39
|
+
export function setupMockClient() {
|
|
40
|
+
vi.mocked(createToolClient).mockReturnValue(mockClient);
|
|
41
|
+
}
|
|
42
|
+
/**
|
|
43
|
+
* Standard mock entry object used across tests
|
|
44
|
+
*/
|
|
45
|
+
export const mockEntry = {
|
|
46
|
+
sys: {
|
|
47
|
+
id: 'test-entry-id',
|
|
48
|
+
type: 'Entry',
|
|
49
|
+
contentType: {
|
|
50
|
+
sys: {
|
|
51
|
+
id: 'test-content-type',
|
|
52
|
+
type: 'Link',
|
|
53
|
+
linkType: 'ContentType',
|
|
54
|
+
},
|
|
55
|
+
},
|
|
56
|
+
version: 1,
|
|
57
|
+
},
|
|
58
|
+
fields: {
|
|
59
|
+
title: { 'en-US': 'Test Entry Title' },
|
|
60
|
+
description: { 'en-US': 'Test Entry Description' },
|
|
61
|
+
},
|
|
62
|
+
};
|
|
63
|
+
/**
|
|
64
|
+
* Standard test arguments for entry operations
|
|
65
|
+
*/
|
|
66
|
+
export const mockArgs = {
|
|
67
|
+
spaceId: 'test-space-id',
|
|
68
|
+
environmentId: 'test-environment',
|
|
69
|
+
entryId: 'test-entry-id',
|
|
70
|
+
};
|
|
71
|
+
/**
|
|
72
|
+
* Standard test arguments for bulk operations
|
|
73
|
+
*/
|
|
74
|
+
export const mockBulkArgs = {
|
|
75
|
+
spaceId: 'test-space-id',
|
|
76
|
+
environmentId: 'test-environment',
|
|
77
|
+
entryId: ['entry-1', 'entry-2'],
|
|
78
|
+
};
|
|
@@ -17,27 +17,19 @@ async function tool(args) {
|
|
|
17
17
|
const entryIds = Array.isArray(args.entryId) ? args.entryId : [args.entryId];
|
|
18
18
|
// For single entry, use individual publish for simplicity
|
|
19
19
|
if (entryIds.length === 1) {
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
});
|
|
34
|
-
}
|
|
35
|
-
catch (error) {
|
|
36
|
-
return createSuccessResponse('Entry publish failed', {
|
|
37
|
-
status: error,
|
|
38
|
-
entryId: entryIds[0],
|
|
39
|
-
});
|
|
40
|
-
}
|
|
20
|
+
const entryId = entryIds[0];
|
|
21
|
+
const params = {
|
|
22
|
+
...baseParams,
|
|
23
|
+
entryId,
|
|
24
|
+
};
|
|
25
|
+
// Get the entry first
|
|
26
|
+
const entry = await contentfulClient.entry.get(params);
|
|
27
|
+
// Publish the entry
|
|
28
|
+
const publishedEntry = await contentfulClient.entry.publish(params, entry);
|
|
29
|
+
return createSuccessResponse('Entry published successfully', {
|
|
30
|
+
status: publishedEntry.sys.status,
|
|
31
|
+
entryId,
|
|
32
|
+
});
|
|
41
33
|
}
|
|
42
34
|
// For multiple entries, use bulk action API
|
|
43
35
|
// Get the current version of each entry
|
|
@@ -15,29 +15,21 @@ async function tool(args) {
|
|
|
15
15
|
const contentfulClient = createToolClient(args);
|
|
16
16
|
// Normalize input to always be an array
|
|
17
17
|
const entryIds = Array.isArray(args.entryId) ? args.entryId : [args.entryId];
|
|
18
|
-
// For single entry, use individual
|
|
18
|
+
// For single entry, use individual unpublish for simplicity
|
|
19
19
|
if (entryIds.length === 1) {
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
});
|
|
34
|
-
}
|
|
35
|
-
catch (error) {
|
|
36
|
-
return createSuccessResponse('Entry unpublish failed', {
|
|
37
|
-
status: error,
|
|
38
|
-
entryId: entryIds[0],
|
|
39
|
-
});
|
|
40
|
-
}
|
|
20
|
+
const entryId = entryIds[0];
|
|
21
|
+
const params = {
|
|
22
|
+
...baseParams,
|
|
23
|
+
entryId,
|
|
24
|
+
};
|
|
25
|
+
// Get the entry first
|
|
26
|
+
const entry = await contentfulClient.entry.get(params);
|
|
27
|
+
// Unpublish the entry
|
|
28
|
+
const unpublishedEntry = await contentfulClient.entry.unpublish(params, entry);
|
|
29
|
+
return createSuccessResponse('Entry unpublished successfully', {
|
|
30
|
+
status: unpublishedEntry.sys.status,
|
|
31
|
+
entryId,
|
|
32
|
+
});
|
|
41
33
|
}
|
|
42
34
|
// For multiple entries, use bulk action API
|
|
43
35
|
// Get the unversioned links for each entry (unpublish doesn't need version info)
|
|
@@ -6,7 +6,7 @@ export const CreateLocaleToolParams = BaseToolSchema.extend({
|
|
|
6
6
|
code: z.string().describe('The locale code (e.g., "en-US")'),
|
|
7
7
|
fallbackCode: z
|
|
8
8
|
.string()
|
|
9
|
-
.
|
|
9
|
+
.nullable()
|
|
10
10
|
.describe('The locale code to fallback to when there is no content for the current locale'),
|
|
11
11
|
contentDeliveryApi: z
|
|
12
12
|
.boolean()
|
|
@@ -5,8 +5,8 @@ export const FieldSchema = z.object({
|
|
|
5
5
|
type: z
|
|
6
6
|
.string()
|
|
7
7
|
.describe('The field type (e.g., "Symbol", "Text", "Integer", "Boolean", "Date", "Location", "Link", "Array", "Object")'),
|
|
8
|
-
required: z.boolean().
|
|
9
|
-
localized: z.boolean().
|
|
8
|
+
required: z.boolean().describe('Whether the field is required'),
|
|
9
|
+
localized: z.boolean().describe('Whether the field is localized'),
|
|
10
10
|
disabled: z.boolean().optional().describe('Whether the field is disabled'),
|
|
11
11
|
omitted: z
|
|
12
12
|
.boolean()
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@contentful/mcp-server",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.5",
|
|
4
4
|
"repository": {
|
|
5
5
|
"type": "git",
|
|
6
6
|
"url": "https://github.com/contentful/contentful-mcp-server"
|
|
@@ -43,6 +43,7 @@
|
|
|
43
43
|
"zod": "^3.25.76"
|
|
44
44
|
},
|
|
45
45
|
"devDependencies": {
|
|
46
|
+
"@anthropic-ai/dxt": "^0.2.6",
|
|
46
47
|
"@commitlint/cli": "^19.8.1",
|
|
47
48
|
"@commitlint/config-conventional": "^19.8.1",
|
|
48
49
|
"@modelcontextprotocol/inspector": "0.16.2",
|