@contentful/mcp-server 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/LICENSE +21 -0
- package/README.md +165 -0
- package/build/config/contentful.js +19 -0
- package/build/config/env.js +25 -0
- package/build/config/version.js +2 -0
- package/build/index.js +33 -0
- package/build/prompts/register.js +1 -0
- package/build/resources/register.js +1 -0
- package/build/tools/ai-actions/createAiAction.js +55 -0
- package/build/tools/ai-actions/deleteAiAction.js +20 -0
- package/build/tools/ai-actions/getAiAction.js +20 -0
- package/build/tools/ai-actions/getAiActionInvocation.js +21 -0
- package/build/tools/ai-actions/invokeAiAction.js +74 -0
- package/build/tools/ai-actions/listAiActions.js +65 -0
- package/build/tools/ai-actions/publishAiAction.js +34 -0
- package/build/tools/ai-actions/register.js +20 -0
- package/build/tools/ai-actions/unpublishAiAction.js +28 -0
- package/build/tools/ai-actions/updateAiAction.js +64 -0
- package/build/tools/assets/deleteAsset.js +20 -0
- package/build/tools/assets/getAsset.js +18 -0
- package/build/tools/assets/listAssets.js +68 -0
- package/build/tools/assets/publishAsset.js +58 -0
- package/build/tools/assets/register.js +16 -0
- package/build/tools/assets/unpublishAsset.js +58 -0
- package/build/tools/assets/updateAsset.js +42 -0
- package/build/tools/assets/uploadAsset.js +51 -0
- package/build/tools/context/getInitialContextTool.js +41 -0
- package/build/tools/context/instructions.js +111 -0
- package/build/tools/context/middleware.js +8 -0
- package/build/tools/context/register.js +4 -0
- package/build/tools/context/store.js +15 -0
- package/build/tools/entries/createEntry.js +39 -0
- package/build/tools/entries/deleteEntry.js +21 -0
- package/build/tools/entries/getEntry.js +18 -0
- package/build/tools/entries/publishEntry.js +58 -0
- package/build/tools/entries/register.js +16 -0
- package/build/tools/entries/searchEntries.js +50 -0
- package/build/tools/entries/unpublishEntry.js +58 -0
- package/build/tools/entries/updateEntry.js +50 -0
- package/build/tools/environments/createEnvironment.js +21 -0
- package/build/tools/environments/deleteEnvironment.js +19 -0
- package/build/tools/environments/listEnvironments.js +62 -0
- package/build/tools/environments/register.js +8 -0
- package/build/tools/locales/createLocale.js +49 -0
- package/build/tools/locales/deleteLocale.js +21 -0
- package/build/tools/locales/getLocale.js +18 -0
- package/build/tools/locales/listLocales.js +61 -0
- package/build/tools/locales/register.js +12 -0
- package/build/tools/locales/updateLocale.js +47 -0
- package/build/tools/register.js +20 -0
- package/build/tools/spaces/getSpace.js +20 -0
- package/build/tools/spaces/listSpaces.js +53 -0
- package/build/tools/spaces/register.js +6 -0
- package/build/tools/tags/createTag.js +24 -0
- package/build/tools/tags/listTags.js +50 -0
- package/build/tools/tags/register.js +6 -0
- package/build/tools/types/createContentType.js +33 -0
- package/build/tools/types/deleteContentType.js +20 -0
- package/build/tools/types/getContentType.js +24 -0
- package/build/tools/types/listContentTypes.js +59 -0
- package/build/tools/types/publishContentType.js +22 -0
- package/build/tools/types/register.js +16 -0
- package/build/tools/types/unpublishContentType.js +20 -0
- package/build/tools/types/updateContentType.js +78 -0
- package/build/types/fieldSchema.js +29 -0
- package/build/utils/ai-actions.js +46 -0
- package/build/utils/bulkOperations.js +94 -0
- package/build/utils/formatters.js +39 -0
- package/build/utils/getVersion.js +11 -0
- package/build/utils/response.js +37 -0
- package/build/utils/summarizer.js +39 -0
- package/build/utils/tools.js +20 -0
- package/package.json +64 -0
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import { z } from 'zod';
|
|
2
|
+
import { createSuccessResponse, withErrorHandling, } from '../../utils/response.js';
|
|
3
|
+
import { BaseToolSchema, createToolClient } from '../../utils/tools.js';
|
|
4
|
+
export const GetEntryToolParams = BaseToolSchema.extend({
|
|
5
|
+
entryId: z.string().describe('The ID of the entry to retrieve'),
|
|
6
|
+
});
|
|
7
|
+
async function tool(args) {
|
|
8
|
+
const params = {
|
|
9
|
+
spaceId: args.spaceId,
|
|
10
|
+
environmentId: args.environmentId,
|
|
11
|
+
entryId: args.entryId,
|
|
12
|
+
};
|
|
13
|
+
const contentfulClient = createToolClient(args);
|
|
14
|
+
// Get the entry
|
|
15
|
+
const entry = await contentfulClient.entry.get(params);
|
|
16
|
+
return createSuccessResponse('Entry retrieved successfully', { entry });
|
|
17
|
+
}
|
|
18
|
+
export const getEntryTool = withErrorHandling(tool, 'Error retrieving entry');
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
import { z } from 'zod';
|
|
2
|
+
import { createSuccessResponse, withErrorHandling, } from '../../utils/response.js';
|
|
3
|
+
import { BaseToolSchema, createToolClient } from '../../utils/tools.js';
|
|
4
|
+
import { createEntryVersionedLinks, createEntitiesCollection, waitForBulkActionCompletion, } from '../../utils/bulkOperations.js';
|
|
5
|
+
export const PublishEntryToolParams = BaseToolSchema.extend({
|
|
6
|
+
entryId: z
|
|
7
|
+
.union([z.string(), z.array(z.string()).max(100)])
|
|
8
|
+
.describe('The ID of the entry to publish (string) or an array of entry IDs (up to 100 entries)'),
|
|
9
|
+
});
|
|
10
|
+
async function tool(args) {
|
|
11
|
+
const baseParams = {
|
|
12
|
+
spaceId: args.spaceId,
|
|
13
|
+
environmentId: args.environmentId,
|
|
14
|
+
};
|
|
15
|
+
const contentfulClient = createToolClient(args);
|
|
16
|
+
// Normalize input to always be an array
|
|
17
|
+
const entryIds = Array.isArray(args.entryId) ? args.entryId : [args.entryId];
|
|
18
|
+
// For single entry, use individual publish for simplicity
|
|
19
|
+
if (entryIds.length === 1) {
|
|
20
|
+
try {
|
|
21
|
+
const entryId = entryIds[0];
|
|
22
|
+
const params = {
|
|
23
|
+
...baseParams,
|
|
24
|
+
entryId,
|
|
25
|
+
};
|
|
26
|
+
// Get the entry first
|
|
27
|
+
const entry = await contentfulClient.entry.get(params);
|
|
28
|
+
// Publish the entry
|
|
29
|
+
const publishedEntry = await contentfulClient.entry.publish(params, entry);
|
|
30
|
+
return createSuccessResponse('Entry published successfully', {
|
|
31
|
+
status: publishedEntry.sys.status,
|
|
32
|
+
entryId,
|
|
33
|
+
});
|
|
34
|
+
}
|
|
35
|
+
catch (error) {
|
|
36
|
+
return createSuccessResponse('Entry publish failed', {
|
|
37
|
+
status: error,
|
|
38
|
+
entryId: entryIds[0],
|
|
39
|
+
});
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
// For multiple entries, use bulk action API
|
|
43
|
+
// Get the current version of each entry
|
|
44
|
+
const entityVersions = await createEntryVersionedLinks(contentfulClient, baseParams, entryIds);
|
|
45
|
+
// Create the collection object
|
|
46
|
+
const entitiesCollection = createEntitiesCollection(entityVersions);
|
|
47
|
+
// Create the bulk action
|
|
48
|
+
const bulkAction = await contentfulClient.bulkAction.publish(baseParams, {
|
|
49
|
+
entities: entitiesCollection,
|
|
50
|
+
});
|
|
51
|
+
// Wait for the bulk action to complete
|
|
52
|
+
const action = await waitForBulkActionCompletion(contentfulClient, baseParams, bulkAction.sys.id);
|
|
53
|
+
return createSuccessResponse('Entry(s) published successfully', {
|
|
54
|
+
status: action.sys.status,
|
|
55
|
+
entryIds,
|
|
56
|
+
});
|
|
57
|
+
}
|
|
58
|
+
export const publishEntryTool = withErrorHandling(tool, 'Error publishing entry');
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import { searchEntriesTool, SearchEntriesToolParams } from './searchEntries.js';
|
|
2
|
+
import { createEntryTool, CreateEntryToolParams } from './createEntry.js';
|
|
3
|
+
import { deleteEntryTool, DeleteEntryToolParams } from './deleteEntry.js';
|
|
4
|
+
import { updateEntryTool, UpdateEntryToolParams } from './updateEntry.js';
|
|
5
|
+
import { getEntryTool, GetEntryToolParams } from './getEntry.js';
|
|
6
|
+
import { publishEntryTool, PublishEntryToolParams } from './publishEntry.js';
|
|
7
|
+
import { unpublishEntryTool, UnpublishEntryToolParams, } from './unpublishEntry.js';
|
|
8
|
+
export function registerEntriesTools(server) {
|
|
9
|
+
server.tool('search_entries', 'Search for specific entries in your Contentful space', SearchEntriesToolParams.shape, searchEntriesTool);
|
|
10
|
+
server.tool('create_entry', "Create a new entry in Contentful. Before executing this function, you need to know the contentTypeId (not the content type NAME) and the fields of that contentType. You can get the fields definition by using the GET_CONTENT_TYPE tool. IMPORTANT: All field values MUST include a locale key (e.g., 'en-US') for each value, like: { title: { 'en-US': 'My Title' } }. Every field in Contentful requires a locale even for single-language content. TAGS: To add tags to an entry, include a metadata object with a tags array. Each tag should be an object with sys.type='Link', sys.linkType='Tag', and sys.id='tagId'. Example: { metadata: { tags: [{ sys: { type: 'Link', linkType: 'Tag', id: 'myTagId' } }] } }.", CreateEntryToolParams.shape, createEntryTool);
|
|
11
|
+
server.tool('get_entry', 'Retrieve an existing entry', GetEntryToolParams.shape, getEntryTool);
|
|
12
|
+
server.tool('update_entry', "Update an existing entry. The handler will merge your field updates with the existing entry fields, so you only need to provide the fields you want to change. However, for multiple-locale fields, all existing locales must be included in the update. IMPORTANT: All field values MUST include a locale key (e.g., 'en-US') for each value, like: { title: { 'en-US': 'My Updated Title' } }. Every field in Contentful requires a locale even for single-language content. When updating entries with multiple locales, always include all existing locales in the update to prevent overwriting with empty values. RICH TEXT FIELDS: When updating rich text fields, ALL text nodes MUST include a 'marks' property (can be empty array [] for no formatting). Text nodes with formatting need appropriate marks: { nodeType: 'text', value: 'Bold text', marks: [{ type: 'bold' }], data: {} }.", UpdateEntryToolParams.shape, updateEntryTool);
|
|
13
|
+
server.tool('delete_entry', 'Delete a specific content entry from your Contentful space', DeleteEntryToolParams.shape, deleteEntryTool);
|
|
14
|
+
server.tool('publish_entry', 'Publish an entry or multiple entries. Accepts either a single entryId (string) or an array of entryIds (up to 100 entries). For a single entry, it uses the standard publish operation. For multiple entries, it automatically uses bulk publishing.', PublishEntryToolParams.shape, publishEntryTool);
|
|
15
|
+
server.tool('unpublish_entry', 'Unpublish an entry or multiple entries. Accepts either a single entryId (string) or an array of entryIds (up to 100 entries). For a single entry, it uses the standard unpublish operation. For multiple entries, it automatically uses bulk unpublishing.', UnpublishEntryToolParams.shape, unpublishEntryTool);
|
|
16
|
+
}
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
import { z } from 'zod';
|
|
2
|
+
import { createSuccessResponse, withErrorHandling, } from '../../utils/response.js';
|
|
3
|
+
import { BaseToolSchema, createToolClient } from '../../utils/tools.js';
|
|
4
|
+
import { summarizeData } from '../../utils/summarizer.js';
|
|
5
|
+
export const SearchEntriesToolParams = BaseToolSchema.extend({
|
|
6
|
+
query: z.object({
|
|
7
|
+
content_type: z.string().optional().describe('Filter by content type'),
|
|
8
|
+
include: z
|
|
9
|
+
.number()
|
|
10
|
+
.optional()
|
|
11
|
+
.describe('Include this many levels of linked entries'),
|
|
12
|
+
select: z
|
|
13
|
+
.string()
|
|
14
|
+
.optional()
|
|
15
|
+
.describe('Comma-separated list of fields to return'),
|
|
16
|
+
links_to_entry: z
|
|
17
|
+
.string()
|
|
18
|
+
.optional()
|
|
19
|
+
.describe('Find entries that link to the specified entry ID'),
|
|
20
|
+
limit: z
|
|
21
|
+
.number()
|
|
22
|
+
.optional()
|
|
23
|
+
.describe('Maximum number of entries to return'),
|
|
24
|
+
skip: z.number().optional().describe('Skip this many entries'),
|
|
25
|
+
order: z.string().optional().describe('Order entries by this field'),
|
|
26
|
+
}),
|
|
27
|
+
});
|
|
28
|
+
async function tool(args) {
|
|
29
|
+
const params = {
|
|
30
|
+
spaceId: args.spaceId,
|
|
31
|
+
environmentId: args.environmentId,
|
|
32
|
+
};
|
|
33
|
+
const contentfulClient = createToolClient(args);
|
|
34
|
+
const entries = await contentfulClient.entry.getMany({
|
|
35
|
+
...params,
|
|
36
|
+
query: {
|
|
37
|
+
...args.query,
|
|
38
|
+
limit: Math.min(args.query.limit || 3, 3),
|
|
39
|
+
skip: args.query.skip || 0,
|
|
40
|
+
},
|
|
41
|
+
});
|
|
42
|
+
const summarized = summarizeData(entries, {
|
|
43
|
+
maxItems: 3,
|
|
44
|
+
remainingMessage: 'To see more entries, please ask me to retrieve the next page.',
|
|
45
|
+
});
|
|
46
|
+
return createSuccessResponse('Entries retrieved successfully', {
|
|
47
|
+
entries: summarized,
|
|
48
|
+
});
|
|
49
|
+
}
|
|
50
|
+
export const searchEntriesTool = withErrorHandling(tool, 'Error deleting dataset');
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
import { z } from 'zod';
|
|
2
|
+
import { createSuccessResponse, withErrorHandling, } from '../../utils/response.js';
|
|
3
|
+
import { BaseToolSchema, createToolClient } from '../../utils/tools.js';
|
|
4
|
+
import { createEntryUnversionedLinks, createEntitiesCollection, waitForBulkActionCompletion, } from '../../utils/bulkOperations.js';
|
|
5
|
+
export const UnpublishEntryToolParams = BaseToolSchema.extend({
|
|
6
|
+
entryId: z
|
|
7
|
+
.union([z.string(), z.array(z.string()).max(100)])
|
|
8
|
+
.describe('The ID of the entry to unpublish (string) or an array of entry IDs (up to 100 entries)'),
|
|
9
|
+
});
|
|
10
|
+
async function tool(args) {
|
|
11
|
+
const baseParams = {
|
|
12
|
+
spaceId: args.spaceId,
|
|
13
|
+
environmentId: args.environmentId,
|
|
14
|
+
};
|
|
15
|
+
const contentfulClient = createToolClient(args);
|
|
16
|
+
// Normalize input to always be an array
|
|
17
|
+
const entryIds = Array.isArray(args.entryId) ? args.entryId : [args.entryId];
|
|
18
|
+
// For single entry, use individual publish for simplicity
|
|
19
|
+
if (entryIds.length === 1) {
|
|
20
|
+
try {
|
|
21
|
+
const entryId = entryIds[0];
|
|
22
|
+
const params = {
|
|
23
|
+
...baseParams,
|
|
24
|
+
entryId,
|
|
25
|
+
};
|
|
26
|
+
// Get the entry first
|
|
27
|
+
const entry = await contentfulClient.entry.get(params);
|
|
28
|
+
// Unpublish the entry
|
|
29
|
+
const unpublishedEntry = await contentfulClient.entry.unpublish(params, entry);
|
|
30
|
+
return createSuccessResponse('Entry unpublished successfully', {
|
|
31
|
+
status: unpublishedEntry.sys.status,
|
|
32
|
+
entryId,
|
|
33
|
+
});
|
|
34
|
+
}
|
|
35
|
+
catch (error) {
|
|
36
|
+
return createSuccessResponse('Entry unpublish failed', {
|
|
37
|
+
status: error,
|
|
38
|
+
entryId: entryIds[0],
|
|
39
|
+
});
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
// For multiple entries, use bulk action API
|
|
43
|
+
// Get the unversioned links for each entry (unpublish doesn't need version info)
|
|
44
|
+
const entityLinks = await createEntryUnversionedLinks(contentfulClient, baseParams, entryIds);
|
|
45
|
+
// Create the collection object
|
|
46
|
+
const entitiesCollection = createEntitiesCollection(entityLinks);
|
|
47
|
+
// Create the bulk action
|
|
48
|
+
const bulkAction = await contentfulClient.bulkAction.unpublish(baseParams, {
|
|
49
|
+
entities: entitiesCollection,
|
|
50
|
+
});
|
|
51
|
+
// Wait for the bulk action to complete
|
|
52
|
+
const action = await waitForBulkActionCompletion(contentfulClient, baseParams, bulkAction.sys.id);
|
|
53
|
+
return createSuccessResponse('Entry(s) unpublished successfully', {
|
|
54
|
+
status: action.sys.status,
|
|
55
|
+
entryIds,
|
|
56
|
+
});
|
|
57
|
+
}
|
|
58
|
+
export const unpublishEntryTool = withErrorHandling(tool, 'Error unpublishing entry');
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
import { z } from 'zod';
|
|
2
|
+
import { createSuccessResponse, withErrorHandling, } from '../../utils/response.js';
|
|
3
|
+
import { BaseToolSchema, createToolClient } from '../../utils/tools.js';
|
|
4
|
+
export const UpdateEntryToolParams = BaseToolSchema.extend({
|
|
5
|
+
entryId: z.string().describe('The ID of the entry to update'),
|
|
6
|
+
fields: z
|
|
7
|
+
.record(z.any())
|
|
8
|
+
.describe('The field values to update. Keys should be field IDs and values should be the field content. Will be merged with existing fields.'),
|
|
9
|
+
metadata: z
|
|
10
|
+
.object({
|
|
11
|
+
tags: z.array(z.object({
|
|
12
|
+
sys: z.object({
|
|
13
|
+
type: z.literal('Link'),
|
|
14
|
+
linkType: z.literal('Tag'),
|
|
15
|
+
id: z.string(),
|
|
16
|
+
}),
|
|
17
|
+
})),
|
|
18
|
+
})
|
|
19
|
+
.optional(),
|
|
20
|
+
});
|
|
21
|
+
async function tool(args) {
|
|
22
|
+
const params = {
|
|
23
|
+
spaceId: args.spaceId,
|
|
24
|
+
environmentId: args.environmentId,
|
|
25
|
+
entryId: args.entryId,
|
|
26
|
+
};
|
|
27
|
+
const contentfulClient = createToolClient(args);
|
|
28
|
+
// First, get the existing entry
|
|
29
|
+
const existingEntry = await contentfulClient.entry.get(params);
|
|
30
|
+
// Merge the provided fields with existing fields
|
|
31
|
+
const mergedFields = {
|
|
32
|
+
...existingEntry.fields,
|
|
33
|
+
...args.fields,
|
|
34
|
+
};
|
|
35
|
+
const allTags = [
|
|
36
|
+
...(existingEntry.metadata?.tags || []),
|
|
37
|
+
...(args.metadata?.tags || []),
|
|
38
|
+
];
|
|
39
|
+
// Update the entry with merged fields
|
|
40
|
+
const updatedEntry = await contentfulClient.entry.update(params, {
|
|
41
|
+
...existingEntry,
|
|
42
|
+
fields: mergedFields,
|
|
43
|
+
metadata: {
|
|
44
|
+
tags: allTags,
|
|
45
|
+
},
|
|
46
|
+
});
|
|
47
|
+
//return info about the entry that was updated
|
|
48
|
+
return createSuccessResponse('Entry updated successfully', { updatedEntry });
|
|
49
|
+
}
|
|
50
|
+
export const updateEntryTool = withErrorHandling(tool, 'Error updating entry');
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import { z } from 'zod';
|
|
2
|
+
import { createSuccessResponse, withErrorHandling, } from '../../utils/response.js';
|
|
3
|
+
import { BaseToolSchema, createToolClient } from '../../utils/tools.js';
|
|
4
|
+
export const CreateEnvironmentToolParams = BaseToolSchema.extend({
|
|
5
|
+
environmentId: z.string().describe('The ID of the environment to create'),
|
|
6
|
+
name: z.string().describe('The name of the environment to create'),
|
|
7
|
+
});
|
|
8
|
+
async function tool(args) {
|
|
9
|
+
const contentfulClient = createToolClient(args);
|
|
10
|
+
// Create the environment
|
|
11
|
+
const environment = await contentfulClient.environment.createWithId({
|
|
12
|
+
spaceId: args.spaceId,
|
|
13
|
+
environmentId: args.environmentId,
|
|
14
|
+
}, {
|
|
15
|
+
name: args.name,
|
|
16
|
+
});
|
|
17
|
+
return createSuccessResponse('Environment created successfully', {
|
|
18
|
+
environment,
|
|
19
|
+
});
|
|
20
|
+
}
|
|
21
|
+
export const createEnvironmentTool = withErrorHandling(tool, 'Error creating environment');
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import { z } from 'zod';
|
|
2
|
+
import { createSuccessResponse, withErrorHandling, } from '../../utils/response.js';
|
|
3
|
+
import { BaseToolSchema, createToolClient } from '../../utils/tools.js';
|
|
4
|
+
export const DeleteEnvironmentToolParams = BaseToolSchema.extend({
|
|
5
|
+
environmentId: z.string().describe('The ID of the environment to delete'),
|
|
6
|
+
});
|
|
7
|
+
async function tool(args) {
|
|
8
|
+
const params = {
|
|
9
|
+
spaceId: args.spaceId,
|
|
10
|
+
environmentId: args.environmentId,
|
|
11
|
+
};
|
|
12
|
+
const contentfulClient = createToolClient(args);
|
|
13
|
+
// Delete the environment
|
|
14
|
+
await contentfulClient.environment.delete(params);
|
|
15
|
+
return createSuccessResponse('Environment deleted successfully', {
|
|
16
|
+
environmentId: args.environmentId,
|
|
17
|
+
});
|
|
18
|
+
}
|
|
19
|
+
export const deleteEnvironmentTool = withErrorHandling(tool, 'Error deleting environment');
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
import { z } from 'zod';
|
|
2
|
+
import { createSuccessResponse, withErrorHandling, } from '../../utils/response.js';
|
|
3
|
+
import { BaseToolSchema, createToolClient } from '../../utils/tools.js';
|
|
4
|
+
import { summarizeData } from '../../utils/summarizer.js';
|
|
5
|
+
// Extend BaseToolSchema but make environmentId optional since it's not needed for listing
|
|
6
|
+
export const ListEnvironmentsToolParams = BaseToolSchema.extend({
|
|
7
|
+
environmentId: z
|
|
8
|
+
.string()
|
|
9
|
+
.optional()
|
|
10
|
+
.describe('The ID of the Contentful environment (not required for listing)'),
|
|
11
|
+
limit: z
|
|
12
|
+
.number()
|
|
13
|
+
.optional()
|
|
14
|
+
.describe('Maximum number of environments to return (max 10)'),
|
|
15
|
+
skip: z
|
|
16
|
+
.number()
|
|
17
|
+
.optional()
|
|
18
|
+
.describe('Skip this many environments for pagination'),
|
|
19
|
+
select: z
|
|
20
|
+
.string()
|
|
21
|
+
.optional()
|
|
22
|
+
.describe('Comma-separated list of fields to return'),
|
|
23
|
+
order: z.string().optional().describe('Order environments by this field'),
|
|
24
|
+
});
|
|
25
|
+
async function tool(args) {
|
|
26
|
+
// Provide a default environmentId if not specified
|
|
27
|
+
const clientArgs = {
|
|
28
|
+
spaceId: args.spaceId,
|
|
29
|
+
environmentId: args.environmentId || 'master',
|
|
30
|
+
};
|
|
31
|
+
const contentfulClient = createToolClient(clientArgs);
|
|
32
|
+
const environments = await contentfulClient.environment.getMany({
|
|
33
|
+
spaceId: args.spaceId,
|
|
34
|
+
query: {
|
|
35
|
+
limit: Math.min(args.limit || 10, 10),
|
|
36
|
+
skip: args.skip || 0,
|
|
37
|
+
...(args.select && { select: args.select }),
|
|
38
|
+
...(args.order && { order: args.order }),
|
|
39
|
+
},
|
|
40
|
+
});
|
|
41
|
+
const summarizedEnvironments = environments.items.map((environment) => ({
|
|
42
|
+
id: environment.sys.id,
|
|
43
|
+
name: environment.name,
|
|
44
|
+
status: environment.sys.status?.sys?.id || 'unknown',
|
|
45
|
+
createdAt: environment.sys.createdAt,
|
|
46
|
+
updatedAt: environment.sys.updatedAt,
|
|
47
|
+
}));
|
|
48
|
+
const summarized = summarizeData({
|
|
49
|
+
...environments,
|
|
50
|
+
items: summarizedEnvironments,
|
|
51
|
+
}, {
|
|
52
|
+
maxItems: 10,
|
|
53
|
+
remainingMessage: 'To see more environments, please ask me to retrieve the next page using the skip parameter.',
|
|
54
|
+
});
|
|
55
|
+
return createSuccessResponse('Environments retrieved successfully', {
|
|
56
|
+
environments: summarized,
|
|
57
|
+
total: environments.total,
|
|
58
|
+
limit: environments.limit,
|
|
59
|
+
skip: environments.skip,
|
|
60
|
+
});
|
|
61
|
+
}
|
|
62
|
+
export const listEnvironmentsTool = withErrorHandling(tool, 'Error listing environments');
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import { createEnvironmentTool, CreateEnvironmentToolParams, } from './createEnvironment.js';
|
|
2
|
+
import { listEnvironmentsTool, ListEnvironmentsToolParams, } from './listEnvironments.js';
|
|
3
|
+
import { deleteEnvironmentTool, DeleteEnvironmentToolParams, } from './deleteEnvironment.js';
|
|
4
|
+
export function registerEnvironmentTools(server) {
|
|
5
|
+
server.tool('create_environment', 'Create a new environment', CreateEnvironmentToolParams.shape, createEnvironmentTool);
|
|
6
|
+
server.tool('list_environments', 'List all environments in a space', ListEnvironmentsToolParams.shape, listEnvironmentsTool);
|
|
7
|
+
server.tool('delete_environment', 'Delete an environment', DeleteEnvironmentToolParams.shape, deleteEnvironmentTool);
|
|
8
|
+
}
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
import { z } from 'zod';
|
|
2
|
+
import { createSuccessResponse, withErrorHandling, } from '../../utils/response.js';
|
|
3
|
+
import { BaseToolSchema, createToolClient } from '../../utils/tools.js';
|
|
4
|
+
export const CreateLocaleToolParams = BaseToolSchema.extend({
|
|
5
|
+
name: z.string().describe('The name of the locale'),
|
|
6
|
+
code: z.string().describe('The locale code (e.g., "en-US")'),
|
|
7
|
+
fallbackCode: z
|
|
8
|
+
.string()
|
|
9
|
+
.optional()
|
|
10
|
+
.describe('The locale code to fallback to when there is no content for the current locale'),
|
|
11
|
+
contentDeliveryApi: z
|
|
12
|
+
.boolean()
|
|
13
|
+
.optional()
|
|
14
|
+
.default(true)
|
|
15
|
+
.describe('If the content under this locale should be available on the CDA (for public reading)'),
|
|
16
|
+
contentManagementApi: z
|
|
17
|
+
.boolean()
|
|
18
|
+
.optional()
|
|
19
|
+
.default(true)
|
|
20
|
+
.describe('If the content under this locale should be available on the CMA (for editing)'),
|
|
21
|
+
default: z
|
|
22
|
+
.boolean()
|
|
23
|
+
.optional()
|
|
24
|
+
.default(false)
|
|
25
|
+
.describe('If this is the default locale'),
|
|
26
|
+
optional: z
|
|
27
|
+
.boolean()
|
|
28
|
+
.optional()
|
|
29
|
+
.default(false)
|
|
30
|
+
.describe('If the locale needs to be filled in on entries or not'),
|
|
31
|
+
});
|
|
32
|
+
async function tool(args) {
|
|
33
|
+
const params = {
|
|
34
|
+
spaceId: args.spaceId,
|
|
35
|
+
environmentId: args.environmentId,
|
|
36
|
+
};
|
|
37
|
+
const contentfulClient = createToolClient(args);
|
|
38
|
+
// Create the locale
|
|
39
|
+
const newLocale = await contentfulClient.locale.create(params, {
|
|
40
|
+
name: args.name,
|
|
41
|
+
code: args.code,
|
|
42
|
+
fallbackCode: args.fallbackCode,
|
|
43
|
+
contentDeliveryApi: args.contentDeliveryApi,
|
|
44
|
+
contentManagementApi: args.contentManagementApi,
|
|
45
|
+
optional: args.optional,
|
|
46
|
+
});
|
|
47
|
+
return createSuccessResponse('Locale created successfully', { newLocale });
|
|
48
|
+
}
|
|
49
|
+
export const createLocaleTool = withErrorHandling(tool, 'Error creating locale');
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import { z } from 'zod';
|
|
2
|
+
import { createSuccessResponse, withErrorHandling, } from '../../utils/response.js';
|
|
3
|
+
import { BaseToolSchema, createToolClient } from '../../utils/tools.js';
|
|
4
|
+
export const DeleteLocaleToolParams = BaseToolSchema.extend({
|
|
5
|
+
localeId: z.string().describe('The ID of the locale to delete'),
|
|
6
|
+
});
|
|
7
|
+
async function tool(args) {
|
|
8
|
+
const params = {
|
|
9
|
+
spaceId: args.spaceId,
|
|
10
|
+
environmentId: args.environmentId,
|
|
11
|
+
localeId: args.localeId,
|
|
12
|
+
};
|
|
13
|
+
const contentfulClient = createToolClient(args);
|
|
14
|
+
// First, get the locale to check its current state
|
|
15
|
+
const locale = await contentfulClient.locale.get(params);
|
|
16
|
+
// Delete the locale
|
|
17
|
+
await contentfulClient.locale.delete(params);
|
|
18
|
+
// Return info about the locale that was deleted
|
|
19
|
+
return createSuccessResponse('Locale deleted successfully', { locale });
|
|
20
|
+
}
|
|
21
|
+
export const deleteLocaleTool = withErrorHandling(tool, 'Error deleting locale');
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import { z } from 'zod';
|
|
2
|
+
import { createSuccessResponse, withErrorHandling, } from '../../utils/response.js';
|
|
3
|
+
import { BaseToolSchema, createToolClient } from '../../utils/tools.js';
|
|
4
|
+
export const GetLocaleToolParams = BaseToolSchema.extend({
|
|
5
|
+
localeId: z.string().describe('The ID of the locale to retrieve'),
|
|
6
|
+
});
|
|
7
|
+
async function tool(args) {
|
|
8
|
+
const params = {
|
|
9
|
+
spaceId: args.spaceId,
|
|
10
|
+
environmentId: args.environmentId,
|
|
11
|
+
localeId: args.localeId,
|
|
12
|
+
};
|
|
13
|
+
const contentfulClient = createToolClient(args);
|
|
14
|
+
// Get the locale
|
|
15
|
+
const locale = await contentfulClient.locale.get(params);
|
|
16
|
+
return createSuccessResponse('Locale retrieved successfully', { locale });
|
|
17
|
+
}
|
|
18
|
+
export const getLocaleTool = withErrorHandling(tool, 'Error retrieving locale');
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
import { z } from 'zod';
|
|
2
|
+
import { createSuccessResponse, withErrorHandling, } from '../../utils/response.js';
|
|
3
|
+
import { BaseToolSchema, createToolClient } from '../../utils/tools.js';
|
|
4
|
+
import { summarizeData } from '../../utils/summarizer.js';
|
|
5
|
+
export const ListLocaleToolParams = BaseToolSchema.extend({
|
|
6
|
+
limit: z.number().optional().describe('Maximum number of locales to return'),
|
|
7
|
+
skip: z.number().optional().describe('Skip this many locales for pagination'),
|
|
8
|
+
select: z
|
|
9
|
+
.string()
|
|
10
|
+
.optional()
|
|
11
|
+
.describe('Comma-separated list of fields to return'),
|
|
12
|
+
include: z
|
|
13
|
+
.number()
|
|
14
|
+
.optional()
|
|
15
|
+
.describe('Include this many levels of linked entries'),
|
|
16
|
+
order: z.string().optional().describe('Order locales by this field'),
|
|
17
|
+
});
|
|
18
|
+
async function tool(args) {
|
|
19
|
+
const params = {
|
|
20
|
+
spaceId: args.spaceId,
|
|
21
|
+
environmentId: args.environmentId,
|
|
22
|
+
};
|
|
23
|
+
const contentfulClient = createToolClient(args);
|
|
24
|
+
const locales = await contentfulClient.locale.getMany({
|
|
25
|
+
...params,
|
|
26
|
+
query: {
|
|
27
|
+
limit: args.limit || 100,
|
|
28
|
+
skip: args.skip || 0,
|
|
29
|
+
...(args.select && { select: args.select }),
|
|
30
|
+
...(args.include && { include: args.include }),
|
|
31
|
+
...(args.order && { order: args.order }),
|
|
32
|
+
},
|
|
33
|
+
});
|
|
34
|
+
const summarizedLocales = locales.items.map((locale) => ({
|
|
35
|
+
id: locale.sys.id,
|
|
36
|
+
name: locale.name,
|
|
37
|
+
code: locale.code,
|
|
38
|
+
fallbackCode: locale.fallbackCode || null,
|
|
39
|
+
contentDeliveryApi: locale.contentDeliveryApi,
|
|
40
|
+
contentManagementApi: locale.contentManagementApi,
|
|
41
|
+
default: locale.default,
|
|
42
|
+
optional: locale.optional,
|
|
43
|
+
createdAt: locale.sys.createdAt,
|
|
44
|
+
updatedAt: locale.sys.updatedAt,
|
|
45
|
+
version: locale.sys.version,
|
|
46
|
+
}));
|
|
47
|
+
const summarized = summarizeData({
|
|
48
|
+
...locales,
|
|
49
|
+
items: summarizedLocales,
|
|
50
|
+
}, {
|
|
51
|
+
maxItems: 10,
|
|
52
|
+
remainingMessage: 'To see more locales, please ask me to retrieve the next page using the skip parameter.',
|
|
53
|
+
});
|
|
54
|
+
return createSuccessResponse('Locales retrieved successfully', {
|
|
55
|
+
locales: summarized,
|
|
56
|
+
total: locales.total,
|
|
57
|
+
limit: locales.limit,
|
|
58
|
+
skip: locales.skip,
|
|
59
|
+
});
|
|
60
|
+
}
|
|
61
|
+
export const listLocaleTool = withErrorHandling(tool, 'Error listing locales');
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import { getLocaleTool, GetLocaleToolParams } from './getLocale.js';
|
|
2
|
+
import { createLocaleTool, CreateLocaleToolParams } from './createLocale.js';
|
|
3
|
+
import { listLocaleTool, ListLocaleToolParams } from './listLocales.js';
|
|
4
|
+
import { updateLocaleTool, UpdateLocaleToolParams } from './updateLocale.js';
|
|
5
|
+
import { deleteLocaleTool, DeleteLocaleToolParams } from './deleteLocale.js';
|
|
6
|
+
export function registerLocaleTools(server) {
|
|
7
|
+
server.tool('get_locale', 'Retrieve a specific locale from your Contentful environment', GetLocaleToolParams.shape, getLocaleTool);
|
|
8
|
+
server.tool('create_locale', 'Create a new locale in your Contentful environment. A locale defines a language-region pair (e.g., "en-US" for English United States). You can specify fallback behavior, API availability settings, and whether the locale is optional for content editors. Note: setting \'default\' is currently not supported.', CreateLocaleToolParams.shape, createLocaleTool);
|
|
9
|
+
server.tool('list_locales', 'List all locales in your Contentful environment. Returns locale information including language codes, fallback settings, and API availability.', ListLocaleToolParams.shape, listLocaleTool);
|
|
10
|
+
server.tool('update_locale', 'Update an existing locale in your Contentful environment. You can modify the locale name, code, fallback behavior, API availability settings, and whether the locale is optional for content editors. Only provide the fields you want to change. IMPORTANT: internal_code cannot be updated.', UpdateLocaleToolParams.shape, updateLocaleTool);
|
|
11
|
+
server.tool('delete_locale', 'Delete a specific locale from your Contentful environment. This operation permanently removes the locale and cannot be undone.', DeleteLocaleToolParams.shape, deleteLocaleTool);
|
|
12
|
+
}
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
import { z } from 'zod';
|
|
2
|
+
import { createSuccessResponse, withErrorHandling, } from '../../utils/response.js';
|
|
3
|
+
import { BaseToolSchema, createToolClient } from '../../utils/tools.js';
|
|
4
|
+
export const UpdateLocaleToolParams = BaseToolSchema.extend({
|
|
5
|
+
localeId: z.string().describe('The ID of the locale to update'),
|
|
6
|
+
fields: z.object({
|
|
7
|
+
name: z.string().optional().describe('The name of the locale'),
|
|
8
|
+
// NOTE: internal_code changes are not allowed
|
|
9
|
+
code: z.string().optional().describe('The code of the locale'),
|
|
10
|
+
fallbackCode: z
|
|
11
|
+
.string()
|
|
12
|
+
.optional()
|
|
13
|
+
.describe('The locale code to fallback to when there is no content for the current locale'),
|
|
14
|
+
contentDeliveryApi: z
|
|
15
|
+
.boolean()
|
|
16
|
+
.optional()
|
|
17
|
+
.describe('If the content under this locale should be available on the CDA (for public reading)'),
|
|
18
|
+
contentManagementApi: z
|
|
19
|
+
.boolean()
|
|
20
|
+
.optional()
|
|
21
|
+
.describe('If the content under this locale should be available on the CMA (for editing)'),
|
|
22
|
+
optional: z
|
|
23
|
+
.boolean()
|
|
24
|
+
.optional()
|
|
25
|
+
.describe('If the locale needs to be filled in on entries or not'),
|
|
26
|
+
}),
|
|
27
|
+
});
|
|
28
|
+
async function tool(args) {
|
|
29
|
+
const params = {
|
|
30
|
+
spaceId: args.spaceId,
|
|
31
|
+
environmentId: args.environmentId,
|
|
32
|
+
localeId: args.localeId,
|
|
33
|
+
};
|
|
34
|
+
const contentfulClient = createToolClient(args);
|
|
35
|
+
// First, get the existing locale
|
|
36
|
+
const existingLocale = await contentfulClient.locale.get(params);
|
|
37
|
+
// Remove read-only fields (nternal_code cannot be updated)
|
|
38
|
+
delete existingLocale.internal_code;
|
|
39
|
+
// Build update data with only provided fields, merging with existing locale
|
|
40
|
+
const updateData = { ...existingLocale, ...args.fields };
|
|
41
|
+
// Update the locale with merged data
|
|
42
|
+
const updatedLocale = await contentfulClient.locale.update(params, updateData);
|
|
43
|
+
return createSuccessResponse('Locale updated successfully', {
|
|
44
|
+
updatedLocale,
|
|
45
|
+
});
|
|
46
|
+
}
|
|
47
|
+
export const updateLocaleTool = withErrorHandling(tool, 'Error updating locale');
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import { registerContextTools } from './context/register.js';
|
|
2
|
+
import { registerEntriesTools } from './entries/register.js';
|
|
3
|
+
import { registerContentTypesTools } from './types/register.js';
|
|
4
|
+
import { registerEnvironmentTools } from './environments/register.js';
|
|
5
|
+
import { registerAssetTools } from './assets/register.js';
|
|
6
|
+
import { registerSpaceTools } from './spaces/register.js';
|
|
7
|
+
import { registerTagsTools } from './tags/register.js';
|
|
8
|
+
import { registerAiActionsTools } from './ai-actions/register.js';
|
|
9
|
+
import { registerLocaleTools } from './locales/register.js';
|
|
10
|
+
export function registerAllTools(server) {
|
|
11
|
+
registerContextTools(server);
|
|
12
|
+
registerEntriesTools(server);
|
|
13
|
+
registerContentTypesTools(server);
|
|
14
|
+
registerEnvironmentTools(server);
|
|
15
|
+
registerAssetTools(server);
|
|
16
|
+
registerSpaceTools(server);
|
|
17
|
+
registerTagsTools(server);
|
|
18
|
+
registerAiActionsTools(server);
|
|
19
|
+
registerLocaleTools(server);
|
|
20
|
+
}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import { z } from 'zod';
|
|
2
|
+
import { createSuccessResponse, withErrorHandling, } from '../../utils/response.js';
|
|
3
|
+
import ctfl from 'contentful-management';
|
|
4
|
+
import { getDefaultClientConfig } from '../../config/contentful.js';
|
|
5
|
+
export const GetSpaceToolParams = z.object({
|
|
6
|
+
spaceId: z.string().describe('The ID of the space to retrieve'),
|
|
7
|
+
});
|
|
8
|
+
async function tool(args) {
|
|
9
|
+
// Create a client without space-specific configuration
|
|
10
|
+
const clientConfig = getDefaultClientConfig();
|
|
11
|
+
// Remove space from config since we'll specify it in the get call
|
|
12
|
+
delete clientConfig.space;
|
|
13
|
+
const contentfulClient = ctfl.createClient(clientConfig, { type: 'plain' });
|
|
14
|
+
// Get the space
|
|
15
|
+
const space = await contentfulClient.space.get({
|
|
16
|
+
spaceId: args.spaceId,
|
|
17
|
+
});
|
|
18
|
+
return createSuccessResponse('Space retrieved successfully', { space });
|
|
19
|
+
}
|
|
20
|
+
export const getSpaceTool = withErrorHandling(tool, 'Error retrieving space');
|