@contentful/mcp-server 1.0.3 → 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 +6 -1
- 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 +38 -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/mockClient.js +78 -0
- package/build/tools/entries/publishEntry.js +50 -0
- package/build/tools/entries/register.js +16 -0
- package/build/tools/entries/searchEntries.js +50 -0
- package/build/tools/entries/unpublishEntry.js +50 -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 +2 -1
package/README.md
CHANGED
|
@@ -31,7 +31,7 @@ This MCP server provides a comprehensive set of tools for content management, al
|
|
|
31
31
|
- [📄 License and Notices](#-license-and-notices)
|
|
32
32
|
- [🛡️ Code of Conduct](#️-code-of-conduct)
|
|
33
33
|
|
|
34
|
-
## ⚙️ Getting
|
|
34
|
+
## ⚙️ Getting started
|
|
35
35
|
|
|
36
36
|
### Prerequisites
|
|
37
37
|
|
|
@@ -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
|
|
@@ -65,6 +69,7 @@ npm run build
|
|
|
65
69
|
| `SPACE_ID` | ✅ Yes | - | Your Contentful Space ID |
|
|
66
70
|
| `ENVIRONMENT_ID` | ❌ No | `master` | Target environment within your space |
|
|
67
71
|
| `CONTENTFUL_HOST` | ❌ No | `api.contentful.com` | Contentful API host |
|
|
72
|
+
| `NODE_ENV` | ❌ No | `production` | Node Environment to run in |
|
|
68
73
|
|
|
69
74
|
### Configuration
|
|
70
75
|
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import { env } from '../config/env.js';
|
|
2
|
+
import { getVersion } from '../utils/getVersion.js';
|
|
3
|
+
/**
|
|
4
|
+
* Creates a default Contentful client configuration without actually initializing it.
|
|
5
|
+
*/
|
|
6
|
+
export function getDefaultClientConfig() {
|
|
7
|
+
if (!env.success) {
|
|
8
|
+
throw new Error('Environment variables are not properly configured');
|
|
9
|
+
}
|
|
10
|
+
const clientConfig = {
|
|
11
|
+
accessToken: env.data.CONTENTFUL_MANAGEMENT_ACCESS_TOKEN,
|
|
12
|
+
host: env.data.CONTENTFUL_HOST,
|
|
13
|
+
space: env.data.SPACE_ID,
|
|
14
|
+
headers: {
|
|
15
|
+
'X-Contentful-User-Agent-Tool': `contentful-mcp/${getVersion()}`, //Include user agent header for telemetry tracking
|
|
16
|
+
},
|
|
17
|
+
};
|
|
18
|
+
return clientConfig;
|
|
19
|
+
}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import dotenv from 'dotenv';
|
|
2
|
+
import { z } from 'zod';
|
|
3
|
+
dotenv.config();
|
|
4
|
+
const EnvSchema = z.object({
|
|
5
|
+
CONTENTFUL_MANAGEMENT_ACCESS_TOKEN: z
|
|
6
|
+
.string()
|
|
7
|
+
.describe('Contentul CMA token'),
|
|
8
|
+
CONTENTFUL_HOST: z
|
|
9
|
+
.string()
|
|
10
|
+
.optional()
|
|
11
|
+
.default('api.contentful.com')
|
|
12
|
+
.describe('Contentful API host'),
|
|
13
|
+
APP_ID: z.string().optional().describe('Contentful App ID'),
|
|
14
|
+
SPACE_ID: z.string().optional().describe('Contentful Space ID'),
|
|
15
|
+
ENVIRONMENT_ID: z
|
|
16
|
+
.string()
|
|
17
|
+
.optional()
|
|
18
|
+
.default('master')
|
|
19
|
+
.describe('Contentful environment ID'),
|
|
20
|
+
});
|
|
21
|
+
export const env = EnvSchema.safeParse(process.env);
|
|
22
|
+
if (!env.success) {
|
|
23
|
+
console.error('Invalid environment variables', env.error.format());
|
|
24
|
+
process.exit(1);
|
|
25
|
+
}
|
package/build/index.js
ADDED
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
|
|
3
|
+
import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';
|
|
4
|
+
import { registerAllPrompts } from './prompts/register.js';
|
|
5
|
+
import { registerAllResources } from './resources/register.js';
|
|
6
|
+
import { registerAllTools } from './tools/register.js';
|
|
7
|
+
import { VERSION } from './config/version.js';
|
|
8
|
+
if (process.env.NODE_ENV === 'development') {
|
|
9
|
+
try {
|
|
10
|
+
await import('mcps-logger/console');
|
|
11
|
+
}
|
|
12
|
+
catch {
|
|
13
|
+
console.warn('mcps-logger not available in production environment');
|
|
14
|
+
}
|
|
15
|
+
}
|
|
16
|
+
const MCP_SERVER_NAME = '@contentful/mcp-server';
|
|
17
|
+
async function initializeServer() {
|
|
18
|
+
const server = new McpServer({
|
|
19
|
+
name: MCP_SERVER_NAME,
|
|
20
|
+
version: VERSION,
|
|
21
|
+
});
|
|
22
|
+
registerAllTools(server);
|
|
23
|
+
registerAllPrompts(server);
|
|
24
|
+
registerAllResources(server);
|
|
25
|
+
return server;
|
|
26
|
+
}
|
|
27
|
+
async function main() {
|
|
28
|
+
try {
|
|
29
|
+
const server = await initializeServer();
|
|
30
|
+
const transport = new StdioServerTransport();
|
|
31
|
+
await server.connect(transport);
|
|
32
|
+
}
|
|
33
|
+
catch (error) {
|
|
34
|
+
console.error('Fatal error:', error);
|
|
35
|
+
process.exit(1);
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
main();
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export function registerAllPrompts(_server) { }
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export function registerAllResources(_server) { }
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
import { z } from 'zod';
|
|
2
|
+
import { createSuccessResponse, withErrorHandling, } from '../../utils/response.js';
|
|
3
|
+
import { BaseToolSchema, createToolClient } from '../../utils/tools.js';
|
|
4
|
+
import { VariableType } from '../../utils/ai-actions.js';
|
|
5
|
+
export const CreateAiActionToolParams = BaseToolSchema.extend({
|
|
6
|
+
name: z.string().describe('The name of the AI action'),
|
|
7
|
+
description: z.string().describe('The description of the AI action'),
|
|
8
|
+
instruction: z
|
|
9
|
+
.object({
|
|
10
|
+
template: z.string().describe('The template for the AI action'),
|
|
11
|
+
variables: z
|
|
12
|
+
.array(z.object({
|
|
13
|
+
id: z.string().describe('The id of the variable'),
|
|
14
|
+
name: z.string().optional().describe('The name of the variable'),
|
|
15
|
+
type: z
|
|
16
|
+
.nativeEnum(VariableType)
|
|
17
|
+
.describe('The type of the variable'),
|
|
18
|
+
description: z
|
|
19
|
+
.string()
|
|
20
|
+
.optional()
|
|
21
|
+
.describe('The description of the variable'),
|
|
22
|
+
}))
|
|
23
|
+
.describe('Array of variables for the AI action'),
|
|
24
|
+
})
|
|
25
|
+
.describe('The instruction for the AI action'),
|
|
26
|
+
configuration: z
|
|
27
|
+
.object({
|
|
28
|
+
modelType: z.string().describe('The type of model to use'),
|
|
29
|
+
modelTemperature: z.number().describe('The temperature for the model'),
|
|
30
|
+
})
|
|
31
|
+
.describe('The configuration for the AI action'),
|
|
32
|
+
testCases: z
|
|
33
|
+
.array(z.any())
|
|
34
|
+
.optional()
|
|
35
|
+
.describe('Test cases for the AI action'),
|
|
36
|
+
});
|
|
37
|
+
async function tool(args) {
|
|
38
|
+
const params = {
|
|
39
|
+
spaceId: args.spaceId,
|
|
40
|
+
environmentId: args.environmentId || 'master',
|
|
41
|
+
};
|
|
42
|
+
const contentfulClient = createToolClient({
|
|
43
|
+
...args,
|
|
44
|
+
environmentId: args.environmentId || 'master',
|
|
45
|
+
});
|
|
46
|
+
const aiAction = await contentfulClient.aiAction.create(params, {
|
|
47
|
+
name: args.name,
|
|
48
|
+
description: args.description,
|
|
49
|
+
instruction: args.instruction,
|
|
50
|
+
configuration: args.configuration,
|
|
51
|
+
testCases: args.testCases,
|
|
52
|
+
});
|
|
53
|
+
return createSuccessResponse('AI action created successfully', { aiAction });
|
|
54
|
+
}
|
|
55
|
+
export const createAiActionTool = withErrorHandling(tool, 'Error creating AI action');
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import { z } from 'zod';
|
|
2
|
+
import { createSuccessResponse, withErrorHandling, } from '../../utils/response.js';
|
|
3
|
+
import { BaseToolSchema, createToolClient } from '../../utils/tools.js';
|
|
4
|
+
export const DeleteAiActionToolParams = BaseToolSchema.extend({
|
|
5
|
+
aiActionId: z.string().describe('The ID of the AI action to delete'),
|
|
6
|
+
});
|
|
7
|
+
async function tool(args) {
|
|
8
|
+
const params = {
|
|
9
|
+
spaceId: args.spaceId,
|
|
10
|
+
environmentId: args.environmentId,
|
|
11
|
+
aiActionId: args.aiActionId,
|
|
12
|
+
};
|
|
13
|
+
const contentfulClient = createToolClient(args);
|
|
14
|
+
// First, get the AI action to store info for return
|
|
15
|
+
const aiAction = await contentfulClient.aiAction.get(params);
|
|
16
|
+
// Delete the AI action
|
|
17
|
+
await contentfulClient.aiAction.delete(params);
|
|
18
|
+
return createSuccessResponse('AI action deleted successfully', { aiAction });
|
|
19
|
+
}
|
|
20
|
+
export const deleteAiActionTool = withErrorHandling(tool, 'Error deleting AI action');
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import { z } from 'zod';
|
|
2
|
+
import { createSuccessResponse, withErrorHandling, } from '../../utils/response.js';
|
|
3
|
+
import { BaseToolSchema, createToolClient } from '../../utils/tools.js';
|
|
4
|
+
export const GetAiActionToolParams = BaseToolSchema.extend({
|
|
5
|
+
aiActionId: z.string().describe('The ID of the AI action to retrieve'),
|
|
6
|
+
});
|
|
7
|
+
async function tool(args) {
|
|
8
|
+
const params = {
|
|
9
|
+
spaceId: args.spaceId,
|
|
10
|
+
environmentId: args.environmentId,
|
|
11
|
+
aiActionId: args.aiActionId,
|
|
12
|
+
};
|
|
13
|
+
const contentfulClient = createToolClient(args);
|
|
14
|
+
// Get the AI action
|
|
15
|
+
const aiAction = await contentfulClient.aiAction.get(params);
|
|
16
|
+
return createSuccessResponse('AI action retrieved successfully', {
|
|
17
|
+
aiAction,
|
|
18
|
+
});
|
|
19
|
+
}
|
|
20
|
+
export const getAiActionTool = withErrorHandling(tool, 'Error retrieving AI action');
|
|
@@ -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 GetAiActionInvocationToolParams = BaseToolSchema.extend({
|
|
5
|
+
aiActionId: z.string().describe('The ID of the AI action'),
|
|
6
|
+
invocationId: z.string().describe('The ID of the invocation to retrieve'),
|
|
7
|
+
});
|
|
8
|
+
async function tool(args) {
|
|
9
|
+
const params = {
|
|
10
|
+
spaceId: args.spaceId,
|
|
11
|
+
environmentId: args.environmentId,
|
|
12
|
+
aiActionId: args.aiActionId,
|
|
13
|
+
invocationId: args.invocationId,
|
|
14
|
+
};
|
|
15
|
+
const contentfulClient = createToolClient(args);
|
|
16
|
+
const aiActionInvocation = await contentfulClient.aiActionInvocation.get(params);
|
|
17
|
+
return createSuccessResponse('AI action invocation retrieved successfully', {
|
|
18
|
+
aiActionInvocation,
|
|
19
|
+
});
|
|
20
|
+
}
|
|
21
|
+
export const getAiActionInvocationTool = withErrorHandling(tool, 'Error retrieving AI action invocation');
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
import { z } from 'zod';
|
|
2
|
+
import { createSuccessResponse, withErrorHandling, } from '../../utils/response.js';
|
|
3
|
+
import { BaseToolSchema, createToolClient } from '../../utils/tools.js';
|
|
4
|
+
import { OutputFormat, VariableValue, } from '../../utils/ai-actions.js';
|
|
5
|
+
export const InvokeAiActionToolParams = BaseToolSchema.extend({
|
|
6
|
+
aiActionId: z.string().describe('The ID of the AI action to invoke'),
|
|
7
|
+
fields: z.array(z.object({
|
|
8
|
+
outputFormat: z
|
|
9
|
+
.nativeEnum(OutputFormat)
|
|
10
|
+
.describe('The output format of the AI action'),
|
|
11
|
+
variables: z
|
|
12
|
+
.array(VariableValue)
|
|
13
|
+
.describe('The variable assignments within the AI action invocation'),
|
|
14
|
+
})),
|
|
15
|
+
});
|
|
16
|
+
async function pollForCompletion(contentfulClient, params, aiActions, pollInterval = 30000, maxAttempts = 10) {
|
|
17
|
+
const completedActions = new Map();
|
|
18
|
+
for (let attempt = 0; attempt < maxAttempts && completedActions.size < aiActions.length; attempt++) {
|
|
19
|
+
await new Promise((resolve) => setTimeout(resolve, pollInterval));
|
|
20
|
+
await Promise.allSettled(aiActions
|
|
21
|
+
.filter((action) => !completedActions.has(action.sys.id)) //filter out actions that have already been completed
|
|
22
|
+
.map(async (action) => {
|
|
23
|
+
try {
|
|
24
|
+
const invocationStatus = await contentfulClient.aiActionInvocation.get({
|
|
25
|
+
...params,
|
|
26
|
+
invocationId: action.sys.id,
|
|
27
|
+
});
|
|
28
|
+
const status = invocationStatus.sys
|
|
29
|
+
.status;
|
|
30
|
+
if (status === 'COMPLETED' && invocationStatus.result) {
|
|
31
|
+
completedActions.set(action.sys.id, invocationStatus.result.content);
|
|
32
|
+
}
|
|
33
|
+
else if (status === 'FAILED' || status === 'CANCELLED') {
|
|
34
|
+
throw new Error(`AI action ${action.sys.id} failed with status ${status}`);
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
catch (error) {
|
|
38
|
+
console.warn(`Error checking status for invocation ${action.sys.id}:`, error);
|
|
39
|
+
}
|
|
40
|
+
}));
|
|
41
|
+
}
|
|
42
|
+
if (completedActions.size < aiActions.length) {
|
|
43
|
+
throw new Error(`Polling timeout: ${completedActions.size}/${aiActions.length} actions completed after ${maxAttempts} attempts`);
|
|
44
|
+
}
|
|
45
|
+
return Array.from(completedActions.entries()).map(([actionId, content]) => ({
|
|
46
|
+
actionId,
|
|
47
|
+
content,
|
|
48
|
+
}));
|
|
49
|
+
}
|
|
50
|
+
async function tool(args) {
|
|
51
|
+
const params = {
|
|
52
|
+
spaceId: args.spaceId,
|
|
53
|
+
environmentId: args.environmentId,
|
|
54
|
+
};
|
|
55
|
+
const contentfulClient = createToolClient(args);
|
|
56
|
+
const aiActions = [];
|
|
57
|
+
for (const field of args.fields) {
|
|
58
|
+
const aiAction = await contentfulClient.aiAction.invoke({
|
|
59
|
+
...params,
|
|
60
|
+
aiActionId: args.aiActionId,
|
|
61
|
+
}, {
|
|
62
|
+
outputFormat: field.outputFormat,
|
|
63
|
+
variables: field.variables,
|
|
64
|
+
});
|
|
65
|
+
aiActions.push(aiAction);
|
|
66
|
+
}
|
|
67
|
+
// Poll for completion
|
|
68
|
+
const completedActions = await pollForCompletion(contentfulClient, { ...params, aiActionId: args.aiActionId }, aiActions);
|
|
69
|
+
return createSuccessResponse('AI action invoked and completed successfully', {
|
|
70
|
+
aiActions: completedActions,
|
|
71
|
+
nextStepsGuidance: `Required next step: Now that the invocation is complete, you will take the next step to complete the workflow. This will involve a CRUD operation that will take action on each of the action returned in the aiActions variable. You will need to use the appropriate tool to do this, choose a tool depending on the type of entity the action was executed on and the description of the Ai Action.`,
|
|
72
|
+
});
|
|
73
|
+
}
|
|
74
|
+
export const invokeAiActionTool = withErrorHandling(tool, 'Error invoking AI action');
|
|
@@ -0,0 +1,65 @@
|
|
|
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 ListAiActionToolParams = BaseToolSchema.extend({
|
|
6
|
+
limit: z
|
|
7
|
+
.number()
|
|
8
|
+
.optional()
|
|
9
|
+
.describe('Maximum number of AI actions to return (max 3)'),
|
|
10
|
+
skip: z
|
|
11
|
+
.number()
|
|
12
|
+
.optional()
|
|
13
|
+
.describe('Skip this many AI actions for pagination'),
|
|
14
|
+
select: z
|
|
15
|
+
.string()
|
|
16
|
+
.optional()
|
|
17
|
+
.describe('Comma-separated list of fields to return'),
|
|
18
|
+
include: z
|
|
19
|
+
.number()
|
|
20
|
+
.optional()
|
|
21
|
+
.describe('Include this many levels of linked entries'),
|
|
22
|
+
order: z.string().optional().describe('Order AI actions by this field'),
|
|
23
|
+
});
|
|
24
|
+
async function tool(args) {
|
|
25
|
+
const params = {
|
|
26
|
+
spaceId: args.spaceId,
|
|
27
|
+
environmentId: args.environmentId,
|
|
28
|
+
};
|
|
29
|
+
const contentfulClient = createToolClient(args);
|
|
30
|
+
const aiActions = await contentfulClient.aiAction.getMany({
|
|
31
|
+
...params,
|
|
32
|
+
query: {
|
|
33
|
+
limit: Math.min(args.limit || 3, 3),
|
|
34
|
+
skip: args.skip || 0,
|
|
35
|
+
...(args.select && { select: args.select }),
|
|
36
|
+
...(args.include && { include: args.include }),
|
|
37
|
+
...(args.order && { order: args.order }),
|
|
38
|
+
},
|
|
39
|
+
});
|
|
40
|
+
const summarizedAiActions = aiActions.items.map((aiAction) => ({
|
|
41
|
+
id: aiAction.sys.id,
|
|
42
|
+
name: aiAction.name || 'Untitled',
|
|
43
|
+
description: aiAction.description || null,
|
|
44
|
+
instruction: aiAction.instruction || null,
|
|
45
|
+
configuration: aiAction.configuration || null,
|
|
46
|
+
testCases: aiAction.testCases || null,
|
|
47
|
+
createdAt: aiAction.sys.createdAt,
|
|
48
|
+
updatedAt: aiAction.sys.updatedAt,
|
|
49
|
+
publishedVersion: aiAction.sys.publishedVersion,
|
|
50
|
+
}));
|
|
51
|
+
const summarized = summarizeData({
|
|
52
|
+
...aiActions,
|
|
53
|
+
items: summarizedAiActions,
|
|
54
|
+
}, {
|
|
55
|
+
maxItems: 3,
|
|
56
|
+
remainingMessage: 'To see more AI actions, please ask me to retrieve the next page using the skip parameter.',
|
|
57
|
+
});
|
|
58
|
+
return createSuccessResponse('AI actions retrieved successfully', {
|
|
59
|
+
aiActions: summarized,
|
|
60
|
+
total: aiActions.total,
|
|
61
|
+
limit: aiActions.limit,
|
|
62
|
+
skip: aiActions.skip,
|
|
63
|
+
});
|
|
64
|
+
}
|
|
65
|
+
export const listAiActionTool = withErrorHandling(tool, 'Error listing AI actions');
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import { z } from 'zod';
|
|
2
|
+
import { createSuccessResponse, withErrorHandling, } from '../../utils/response.js';
|
|
3
|
+
import { BaseToolSchema, createToolClient } from '../../utils/tools.js';
|
|
4
|
+
export const PublishAiActionToolParams = BaseToolSchema.extend({
|
|
5
|
+
aiActionId: z.string().describe('The ID of the AI action to publish'),
|
|
6
|
+
});
|
|
7
|
+
async function tool(args) {
|
|
8
|
+
const params = {
|
|
9
|
+
spaceId: args.spaceId,
|
|
10
|
+
environmentId: args.environmentId,
|
|
11
|
+
aiActionId: args.aiActionId,
|
|
12
|
+
};
|
|
13
|
+
const contentfulClient = createToolClient(args);
|
|
14
|
+
try {
|
|
15
|
+
// Get the AI action first
|
|
16
|
+
const aiAction = await contentfulClient.aiAction.get(params);
|
|
17
|
+
// Publish the AI action with the version parameter
|
|
18
|
+
const publishedAiAction = await contentfulClient.aiAction.publish({
|
|
19
|
+
...params,
|
|
20
|
+
version: aiAction.sys.version,
|
|
21
|
+
}, aiAction);
|
|
22
|
+
return createSuccessResponse('AI action published successfully', {
|
|
23
|
+
version: publishedAiAction.sys.publishedVersion,
|
|
24
|
+
aiActionId: args.aiActionId,
|
|
25
|
+
});
|
|
26
|
+
}
|
|
27
|
+
catch (error) {
|
|
28
|
+
return createSuccessResponse('AI action publish failed', {
|
|
29
|
+
status: error,
|
|
30
|
+
aiActionId: args.aiActionId,
|
|
31
|
+
});
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
export const publishAiActionTool = withErrorHandling(tool, 'Error publishing AI action');
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import { createAiActionTool, CreateAiActionToolParams, } from './createAiAction.js';
|
|
2
|
+
import { invokeAiActionTool, InvokeAiActionToolParams, } from './invokeAiAction.js';
|
|
3
|
+
import { getAiActionInvocationTool, GetAiActionInvocationToolParams, } from './getAiActionInvocation.js';
|
|
4
|
+
import { deleteAiActionTool, DeleteAiActionToolParams, } from './deleteAiAction.js';
|
|
5
|
+
import { getAiActionTool, GetAiActionToolParams } from './getAiAction.js';
|
|
6
|
+
import { listAiActionTool, ListAiActionToolParams } from './listAiActions.js';
|
|
7
|
+
import { publishAiActionTool, PublishAiActionToolParams, } from './publishAiAction.js';
|
|
8
|
+
import { unpublishAiActionTool, UnpublishAiActionToolParams, } from './unpublishAiAction.js';
|
|
9
|
+
import { updateAiActionTool, UpdateAiActionToolParams, } from './updateAiAction.js';
|
|
10
|
+
export function registerAiActionsTools(server) {
|
|
11
|
+
server.tool('create_ai_action', 'Create a new AI action with clear instructions and well-defined variables. Best practices: (1) Use descriptive names that indicate the action\'s purpose, (2) Write specific, actionable instructions in the template, (3) Define meaningful variables with clear names like "sourceContent", "targetLocale", "entryId", or "contentType", (4) Embed variables into the template using the format {{var.{variableId}}}, (5) Consider the content editor\'s workflow and make the action intuitive to use. Example variables: content fields to process, locales for translation, reference entries, formatting preferences, or validation rules.', CreateAiActionToolParams.shape, createAiActionTool);
|
|
12
|
+
server.tool('invoke_ai_action', 'Invoke an AI action with variables. MANDATORY BULK OPERATIONS: You MUST ALWAYS use bulk operations when processing multiple content pieces by adding multiple items to the fields array - never make separate calls. VARIABLES: Can be (1) String values for simple text input, or (2) Entity references to read from specific entry fields using {"entityId": "entryId", "entityPath": "fields.fieldName.locale", "entityType": "Entry"}. POLLING: Automatically polls every 3 seconds for up to 60 seconds. CRITICAL FOLLOW-UP: After invoking AI actions, you MUST immediately take the next step to complete the workflow. The tool response will provide specific guidance on required next steps - you must follow this guidance.', InvokeAiActionToolParams.shape, invokeAiActionTool);
|
|
13
|
+
server.tool('get_ai_action_invocation', 'Retrieve information about a specific AI action invocation by its ID.', GetAiActionInvocationToolParams.shape, getAiActionInvocationTool);
|
|
14
|
+
server.tool('delete_ai_action', 'Delete a specific AI action from your Contentful space', DeleteAiActionToolParams.shape, deleteAiActionTool);
|
|
15
|
+
server.tool('get_ai_action', 'Retrieve details about a specific AI action including its configuration, instructions, and defined variables', GetAiActionToolParams.shape, getAiActionTool);
|
|
16
|
+
server.tool('list_ai_actions', 'List AI actions in a space. Returns a maximum of 3 items per request. Use skip parameter to paginate through results.', ListAiActionToolParams.shape, listAiActionTool);
|
|
17
|
+
server.tool('publish_ai_action', 'Publish an AI action to make it available for use in the Contentful web app', PublishAiActionToolParams.shape, publishAiActionTool);
|
|
18
|
+
server.tool('unpublish_ai_action', 'Unpublish an AI action to remove it from use in the Contentful web app', UnpublishAiActionToolParams.shape, unpublishAiActionTool);
|
|
19
|
+
server.tool('update_ai_action', 'Update an existing AI action. Your updates will be merged with the existing AI action data, so you only need to provide the fields you want to change.', UpdateAiActionToolParams.shape, updateAiActionTool);
|
|
20
|
+
}
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import { z } from 'zod';
|
|
2
|
+
import { createSuccessResponse, withErrorHandling, } from '../../utils/response.js';
|
|
3
|
+
import { BaseToolSchema, createToolClient } from '../../utils/tools.js';
|
|
4
|
+
export const UnpublishAiActionToolParams = BaseToolSchema.extend({
|
|
5
|
+
aiActionId: z.string().describe('The ID of the AI action to unpublish'),
|
|
6
|
+
});
|
|
7
|
+
async function tool(args) {
|
|
8
|
+
const params = {
|
|
9
|
+
spaceId: args.spaceId,
|
|
10
|
+
environmentId: args.environmentId,
|
|
11
|
+
aiActionId: args.aiActionId,
|
|
12
|
+
};
|
|
13
|
+
const contentfulClient = createToolClient(args);
|
|
14
|
+
try {
|
|
15
|
+
// Unpublish the AI action
|
|
16
|
+
await contentfulClient.aiAction.unpublish(params);
|
|
17
|
+
return createSuccessResponse('AI action unpublished successfully', {
|
|
18
|
+
aiActionId: args.aiActionId,
|
|
19
|
+
});
|
|
20
|
+
}
|
|
21
|
+
catch (error) {
|
|
22
|
+
return createSuccessResponse('AI action unpublish failed', {
|
|
23
|
+
status: error,
|
|
24
|
+
aiActionId: args.aiActionId,
|
|
25
|
+
});
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
export const unpublishAiActionTool = withErrorHandling(tool, 'Error unpublishing AI action');
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
import { z } from 'zod';
|
|
2
|
+
import { createSuccessResponse, withErrorHandling, } from '../../utils/response.js';
|
|
3
|
+
import { BaseToolSchema, createToolClient } from '../../utils/tools.js';
|
|
4
|
+
import { VariableType } from '../../utils/ai-actions.js';
|
|
5
|
+
export const UpdateAiActionToolParams = BaseToolSchema.extend({
|
|
6
|
+
aiActionId: z.string().describe('The ID of the AI action to update'),
|
|
7
|
+
name: z.string().optional().describe('The name of the AI action'),
|
|
8
|
+
description: z
|
|
9
|
+
.string()
|
|
10
|
+
.optional()
|
|
11
|
+
.describe('The description of the AI action'),
|
|
12
|
+
instruction: z
|
|
13
|
+
.object({
|
|
14
|
+
template: z.string().describe('The template for the AI action'),
|
|
15
|
+
variables: z
|
|
16
|
+
.array(z.object({
|
|
17
|
+
id: z.string().describe('The id of the variable'),
|
|
18
|
+
name: z.string().optional().describe('The name of the variable'),
|
|
19
|
+
type: z
|
|
20
|
+
.nativeEnum(VariableType)
|
|
21
|
+
.describe('The type of the variable'),
|
|
22
|
+
description: z
|
|
23
|
+
.string()
|
|
24
|
+
.optional()
|
|
25
|
+
.describe('The description of the variable'),
|
|
26
|
+
}))
|
|
27
|
+
.describe('Array of variables for the AI action'),
|
|
28
|
+
})
|
|
29
|
+
.optional()
|
|
30
|
+
.describe('The instruction for the AI action'),
|
|
31
|
+
configuration: z
|
|
32
|
+
.object({
|
|
33
|
+
modelType: z.string().describe('The type of model to use'),
|
|
34
|
+
modelTemperature: z.number().describe('The temperature for the model'),
|
|
35
|
+
})
|
|
36
|
+
.optional()
|
|
37
|
+
.describe('The configuration for the AI action'),
|
|
38
|
+
testCases: z
|
|
39
|
+
.array(z.any())
|
|
40
|
+
.optional()
|
|
41
|
+
.describe('Test cases for the AI action'),
|
|
42
|
+
});
|
|
43
|
+
async function tool(args) {
|
|
44
|
+
const params = {
|
|
45
|
+
spaceId: args.spaceId,
|
|
46
|
+
environmentId: args.environmentId,
|
|
47
|
+
aiActionId: args.aiActionId,
|
|
48
|
+
};
|
|
49
|
+
const contentfulClient = createToolClient(args);
|
|
50
|
+
// Get existing AI action, merge fields, and update
|
|
51
|
+
const existingAiAction = await contentfulClient.aiAction.get(params);
|
|
52
|
+
const updatedAiAction = await contentfulClient.aiAction.update(params, {
|
|
53
|
+
...existingAiAction,
|
|
54
|
+
...(args.name && { name: args.name }),
|
|
55
|
+
...(args.description && { description: args.description }),
|
|
56
|
+
...(args.instruction && { instruction: args.instruction }),
|
|
57
|
+
...(args.configuration && { configuration: args.configuration }),
|
|
58
|
+
...(args.testCases && { testCases: args.testCases }),
|
|
59
|
+
});
|
|
60
|
+
return createSuccessResponse('AI action updated successfully', {
|
|
61
|
+
updatedAiAction,
|
|
62
|
+
});
|
|
63
|
+
}
|
|
64
|
+
export const updateAiActionTool = withErrorHandling(tool, 'Error updating AI action');
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import { z } from 'zod';
|
|
2
|
+
import { createSuccessResponse, withErrorHandling, } from '../../utils/response.js';
|
|
3
|
+
import { BaseToolSchema, createToolClient } from '../../utils/tools.js';
|
|
4
|
+
export const DeleteAssetToolParams = BaseToolSchema.extend({
|
|
5
|
+
assetId: z.string().describe('The ID of the asset to delete'),
|
|
6
|
+
});
|
|
7
|
+
async function tool(args) {
|
|
8
|
+
const params = {
|
|
9
|
+
spaceId: args.spaceId,
|
|
10
|
+
environmentId: args.environmentId,
|
|
11
|
+
assetId: args.assetId,
|
|
12
|
+
};
|
|
13
|
+
const contentfulClient = createToolClient(args);
|
|
14
|
+
// First, get the asset to store info for return
|
|
15
|
+
const asset = await contentfulClient.asset.get(params);
|
|
16
|
+
// Delete the asset
|
|
17
|
+
await contentfulClient.asset.delete(params);
|
|
18
|
+
return createSuccessResponse('Asset deleted successfully', { asset });
|
|
19
|
+
}
|
|
20
|
+
export const deleteAssetTool = withErrorHandling(tool, 'Error deleting asset');
|
|
@@ -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 GetAssetToolParams = BaseToolSchema.extend({
|
|
5
|
+
assetId: z.string().describe('The ID of the asset to retrieve'),
|
|
6
|
+
});
|
|
7
|
+
async function tool(args) {
|
|
8
|
+
const params = {
|
|
9
|
+
spaceId: args.spaceId,
|
|
10
|
+
environmentId: args.environmentId,
|
|
11
|
+
assetId: args.assetId,
|
|
12
|
+
};
|
|
13
|
+
const contentfulClient = createToolClient(args);
|
|
14
|
+
// Get the asset
|
|
15
|
+
const asset = await contentfulClient.asset.get(params);
|
|
16
|
+
return createSuccessResponse('Asset retrieved successfully', { asset });
|
|
17
|
+
}
|
|
18
|
+
export const getAssetTool = withErrorHandling(tool, 'Error retrieving asset');
|
|
@@ -0,0 +1,68 @@
|
|
|
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 ListAssetsToolParams = BaseToolSchema.extend({
|
|
6
|
+
limit: z
|
|
7
|
+
.number()
|
|
8
|
+
.optional()
|
|
9
|
+
.describe('Maximum number of assets to return (max 3)'),
|
|
10
|
+
skip: z.number().optional().describe('Skip this many assets for pagination'),
|
|
11
|
+
select: z
|
|
12
|
+
.string()
|
|
13
|
+
.optional()
|
|
14
|
+
.describe('Comma-separated list of fields to return'),
|
|
15
|
+
include: z
|
|
16
|
+
.number()
|
|
17
|
+
.optional()
|
|
18
|
+
.describe('Include this many levels of linked entries'),
|
|
19
|
+
order: z.string().optional().describe('Order assets by this field'),
|
|
20
|
+
links_to_entry: z
|
|
21
|
+
.string()
|
|
22
|
+
.optional()
|
|
23
|
+
.describe('Find assets that link to the specified entry ID'),
|
|
24
|
+
});
|
|
25
|
+
async function tool(args) {
|
|
26
|
+
const params = {
|
|
27
|
+
spaceId: args.spaceId,
|
|
28
|
+
environmentId: args.environmentId,
|
|
29
|
+
};
|
|
30
|
+
const contentfulClient = createToolClient(args);
|
|
31
|
+
const assets = await contentfulClient.asset.getMany({
|
|
32
|
+
...params,
|
|
33
|
+
query: {
|
|
34
|
+
limit: Math.min(args.limit || 3, 3),
|
|
35
|
+
skip: args.skip || 0,
|
|
36
|
+
...(args.select && { select: args.select }),
|
|
37
|
+
...(args.include && { include: args.include }),
|
|
38
|
+
...(args.order && { order: args.order }),
|
|
39
|
+
...(args.links_to_entry && { links_to_entry: args.links_to_entry }),
|
|
40
|
+
},
|
|
41
|
+
});
|
|
42
|
+
const summarizedAssets = assets.items.map((asset) => ({
|
|
43
|
+
id: asset.sys.id,
|
|
44
|
+
title: asset.fields.title?.['en-US'] || 'Untitled',
|
|
45
|
+
description: asset.fields.description?.['en-US'] || null,
|
|
46
|
+
fileName: asset.fields.file?.['en-US']?.fileName || null,
|
|
47
|
+
contentType: asset.fields.file?.['en-US']?.contentType || null,
|
|
48
|
+
url: asset.fields.file?.['en-US']?.url || null,
|
|
49
|
+
size: asset.fields.file?.['en-US']?.details?.size || null,
|
|
50
|
+
createdAt: asset.sys.createdAt,
|
|
51
|
+
updatedAt: asset.sys.updatedAt,
|
|
52
|
+
publishedVersion: asset.sys.publishedVersion,
|
|
53
|
+
}));
|
|
54
|
+
const summarized = summarizeData({
|
|
55
|
+
...assets,
|
|
56
|
+
items: summarizedAssets,
|
|
57
|
+
}, {
|
|
58
|
+
maxItems: 3,
|
|
59
|
+
remainingMessage: 'To see more assets, please ask me to retrieve the next page using the skip parameter.',
|
|
60
|
+
});
|
|
61
|
+
return createSuccessResponse('Assets retrieved successfully', {
|
|
62
|
+
assets: summarized,
|
|
63
|
+
total: assets.total,
|
|
64
|
+
limit: assets.limit,
|
|
65
|
+
skip: assets.skip,
|
|
66
|
+
});
|
|
67
|
+
}
|
|
68
|
+
export const listAssetsTool = withErrorHandling(tool, 'Error listing assets');
|