@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.
Files changed (73) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +165 -0
  3. package/build/config/contentful.js +19 -0
  4. package/build/config/env.js +25 -0
  5. package/build/config/version.js +2 -0
  6. package/build/index.js +33 -0
  7. package/build/prompts/register.js +1 -0
  8. package/build/resources/register.js +1 -0
  9. package/build/tools/ai-actions/createAiAction.js +55 -0
  10. package/build/tools/ai-actions/deleteAiAction.js +20 -0
  11. package/build/tools/ai-actions/getAiAction.js +20 -0
  12. package/build/tools/ai-actions/getAiActionInvocation.js +21 -0
  13. package/build/tools/ai-actions/invokeAiAction.js +74 -0
  14. package/build/tools/ai-actions/listAiActions.js +65 -0
  15. package/build/tools/ai-actions/publishAiAction.js +34 -0
  16. package/build/tools/ai-actions/register.js +20 -0
  17. package/build/tools/ai-actions/unpublishAiAction.js +28 -0
  18. package/build/tools/ai-actions/updateAiAction.js +64 -0
  19. package/build/tools/assets/deleteAsset.js +20 -0
  20. package/build/tools/assets/getAsset.js +18 -0
  21. package/build/tools/assets/listAssets.js +68 -0
  22. package/build/tools/assets/publishAsset.js +58 -0
  23. package/build/tools/assets/register.js +16 -0
  24. package/build/tools/assets/unpublishAsset.js +58 -0
  25. package/build/tools/assets/updateAsset.js +42 -0
  26. package/build/tools/assets/uploadAsset.js +51 -0
  27. package/build/tools/context/getInitialContextTool.js +41 -0
  28. package/build/tools/context/instructions.js +111 -0
  29. package/build/tools/context/middleware.js +8 -0
  30. package/build/tools/context/register.js +4 -0
  31. package/build/tools/context/store.js +15 -0
  32. package/build/tools/entries/createEntry.js +39 -0
  33. package/build/tools/entries/deleteEntry.js +21 -0
  34. package/build/tools/entries/getEntry.js +18 -0
  35. package/build/tools/entries/publishEntry.js +58 -0
  36. package/build/tools/entries/register.js +16 -0
  37. package/build/tools/entries/searchEntries.js +50 -0
  38. package/build/tools/entries/unpublishEntry.js +58 -0
  39. package/build/tools/entries/updateEntry.js +50 -0
  40. package/build/tools/environments/createEnvironment.js +21 -0
  41. package/build/tools/environments/deleteEnvironment.js +19 -0
  42. package/build/tools/environments/listEnvironments.js +62 -0
  43. package/build/tools/environments/register.js +8 -0
  44. package/build/tools/locales/createLocale.js +49 -0
  45. package/build/tools/locales/deleteLocale.js +21 -0
  46. package/build/tools/locales/getLocale.js +18 -0
  47. package/build/tools/locales/listLocales.js +61 -0
  48. package/build/tools/locales/register.js +12 -0
  49. package/build/tools/locales/updateLocale.js +47 -0
  50. package/build/tools/register.js +20 -0
  51. package/build/tools/spaces/getSpace.js +20 -0
  52. package/build/tools/spaces/listSpaces.js +53 -0
  53. package/build/tools/spaces/register.js +6 -0
  54. package/build/tools/tags/createTag.js +24 -0
  55. package/build/tools/tags/listTags.js +50 -0
  56. package/build/tools/tags/register.js +6 -0
  57. package/build/tools/types/createContentType.js +33 -0
  58. package/build/tools/types/deleteContentType.js +20 -0
  59. package/build/tools/types/getContentType.js +24 -0
  60. package/build/tools/types/listContentTypes.js +59 -0
  61. package/build/tools/types/publishContentType.js +22 -0
  62. package/build/tools/types/register.js +16 -0
  63. package/build/tools/types/unpublishContentType.js +20 -0
  64. package/build/tools/types/updateContentType.js +78 -0
  65. package/build/types/fieldSchema.js +29 -0
  66. package/build/utils/ai-actions.js +46 -0
  67. package/build/utils/bulkOperations.js +94 -0
  68. package/build/utils/formatters.js +39 -0
  69. package/build/utils/getVersion.js +11 -0
  70. package/build/utils/response.js +37 -0
  71. package/build/utils/summarizer.js +39 -0
  72. package/build/utils/tools.js +20 -0
  73. package/package.json +64 -0
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2025 Contentful
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy of
6
+ this software and associated documentation files (the "Software"), to deal in
7
+ the Software without restriction, including without limitation the rights to
8
+ use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
9
+ of the Software, and to permit persons to whom the Software is furnished to do
10
+ so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,165 @@
1
+ # Contentful MCP Server
2
+
3
+ A [Model Context Protocol (MCP)](https://modelcontextprotocol.io/) server that provides AI assistants with comprehensive tools to interact with [Contentful](https://www.contentful.com/) APIs.
4
+
5
+ ## 🚀 Example Use Cases
6
+
7
+ This MCP server provides a comprehensive set of tools for content management, allowing AI to help you create, edit, organize, and publish content directly within Contentful. Once configured, you can use natural language in your AI assistant of choice to manage and interact with your Contentful spaces, including:
8
+
9
+ - **Content Creation**: "Create a new blog post for our fall product launch"
10
+ - **Content Management**: "Update all product entries to include the new pricing structure"
11
+ - **Asset Organization**: "Upload and organize these marketing images by campaign"
12
+ - **Workflow Automation**: "Create an AI action that translates content to Spanish"
13
+ - **Content Modeling**: "Add a new field to the product content type for customer ratings"
14
+
15
+ ## 📓 Table of Contents
16
+
17
+ - [⚙️ Getting Started](#-getting-started)
18
+ - [Prerequisites](#prerequisites)
19
+ - [Installation](#installation)
20
+ - [Environment Variables](#environment-variables)
21
+ - [Configuration](#configuration)
22
+ - [🛠️ Available Tools](#️-available-tools)
23
+ - [🔍 Development](#-development)
24
+ - [Testing with MCP Inspector](#testing-with-mcp-inspector)
25
+ - [Linting](#linting)
26
+ - [📦 Releases](#-releases)
27
+ - [🤝 Contributing](#-contributing)
28
+ - [Development Setup](#development-setup)
29
+ - [📚 Documentation](#-documentation)
30
+ - [❓ Help & Support](#-help--support)
31
+ - [📄 License and Notices](#-license-and-notices)
32
+ - [🛡️ Code of Conduct](#️-code-of-conduct)
33
+
34
+ ## ⚙️ Getting Started
35
+
36
+ ### Prerequisites
37
+
38
+ - Node.js
39
+ - npm
40
+ - A Contentful account with a [Space ID](https://www.contentful.com/help/spaces/find-space-id/)
41
+ - [Contentful Management API personal access token](https://www.contentful.com/help/token-management/personal-access-tokens/)
42
+
43
+ ### Installation
44
+
45
+ #### Install from source
46
+
47
+ ```bash
48
+ git clone https://github.com/contentful/contentful-mcp-server.git
49
+ cd contentful-mcp-server
50
+ npm install
51
+ npm run build
52
+ ```
53
+
54
+ ### Environment Variables
55
+
56
+ | Environment Variable | Required | Default Value | Description |
57
+ | ------------------------------------ | -------- | -------------------- | ---------------------------------------------------- |
58
+ | `CONTENTFUL_MANAGEMENT_ACCESS_TOKEN` | ✅ Yes | - | Your Contentful Management API personal access token |
59
+ | `SPACE_ID` | ✅ Yes | - | Your Contentful Space ID |
60
+ | `ENVIRONMENT_ID` | ❌ No | `master` | Target environment within your space |
61
+ | `CONTENTFUL_HOST` | ❌ No | `api.contentful.com` | Contentful API host |
62
+
63
+ ### Configuration
64
+
65
+ Refer to the documentation for your AI tool of choice for how to configure MCP servers. For example, see the documentation for [Cursor](https://docs.cursor.com/context/mcp), [VS Code](https://code.visualstudio.com/docs/copilot/chat/mcp-servers), or [Claude Desktop](https://modelcontextprotocol.io/quickstart/user).
66
+
67
+ Below is a sample configuration for Cursor:
68
+
69
+ ```json
70
+ {
71
+ "mcpServers": {
72
+ "contentful-mcp": {
73
+ "command": "npx",
74
+ "args": ["-y", "<your_full_path_to_the_package>/index.js"],
75
+ "env": {
76
+ "CONTENTFUL_MANAGEMENT_ACCESS_TOKEN": "your-CMA-token",
77
+ "SPACE_ID": "your-space-id",
78
+ "ENVIRONMENT_ID": "master",
79
+ "CONTENTFUL_HOST": "api.contentful.com"
80
+ }
81
+ }
82
+ }
83
+ }
84
+ ```
85
+
86
+ ## 🛠️ Available Tools
87
+
88
+ | Category | Tool Name | Description |
89
+ | ------------------------- | -------------------------- | ------------------------------------------------ |
90
+ | **Context & Setup** | `get_initial_context` | Initialize connection and get usage instructions |
91
+ | **Content Types** | `list_content_types` | List all content types |
92
+ | | `get_content_type` | Get detailed content type information |
93
+ | | `create_content_type` | Create new content types |
94
+ | | `update_content_type` | Modify existing content types |
95
+ | | `publish_content_type` | Publish content type changes |
96
+ | | `unpublish_content_type` | Unpublish content types |
97
+ | | `delete_content_type` | Remove content types |
98
+ | **Entries** | `search_entries` | Search and filter entries |
99
+ | | `get_entry` | Retrieve specific entries |
100
+ | | `create_entry` | Create new content entries |
101
+ | | `update_entry` | Modify existing entries |
102
+ | | `publish_entry` | Publish entries (single or bulk) |
103
+ | | `unpublish_entry` | Unpublish entries (single or bulk) |
104
+ | | `delete_entry` | Remove entries |
105
+ | **Assets** | `upload_asset` | Upload new assets |
106
+ | | `list_assets` | List and browse assets |
107
+ | | `get_asset` | Retrieve specific assets |
108
+ | | `update_asset` | Modify asset metadata |
109
+ | | `publish_asset` | Publish assets (single or bulk) |
110
+ | | `unpublish_asset` | Unpublish assets (single or bulk) |
111
+ | | `delete_asset` | Remove assets |
112
+ | **Spaces & Environments** | `list_spaces` | List available spaces |
113
+ | | `get_space` | Get space details |
114
+ | | `list_environments` | List environments |
115
+ | | `create_environment` | Create new environments |
116
+ | | `delete_environment` | Remove environments |
117
+ | **Locales** | `list_locales` | List all locales in your environment |
118
+ | | `get_locale` | Retrieve specific locale information |
119
+ | | `create_locale` | Create new locales for multi-language content |
120
+ | | `update_locale` | Modify existing locale settings |
121
+ | | `delete_locale` | Remove locales from environment |
122
+ | **Tags** | `list_tags` | List all tags |
123
+ | | `create_tag` | Create new tags |
124
+ | **AI Actions** | `create_ai_action` | Create custom AI-powered workflows |
125
+ | | `invoke_ai_action` | Invoke an AI action with variables |
126
+ | | `get_ai_action_invocation` | Get AI action invocation details |
127
+ | | `get_ai_action` | Retrieve AI action details and configuration |
128
+ | | `list_ai_actions` | List AI actions in a space |
129
+ | | `update_ai_action` | Update existing AI actions |
130
+ | | `publish_ai_action` | Publish AI actions for use |
131
+ | | `unpublish_ai_action` | Unpublish AI actions |
132
+ | | `delete_ai_action` | Remove AI actions |
133
+
134
+ ## 🤝 Contributing
135
+
136
+ We welcome contributions! Please see our [Contributing Guide](CONTRIBUTING.md) for local development setup and contribution guidelines.
137
+
138
+ ## 📦 Releases
139
+
140
+ This project uses [Nx Release](https://nx.dev/features/manage-releases) for automated versioning and publishing. Releases are automatically generated based on [Conventional Commits](https://www.conventionalcommits.org/). See [Contributing Guide](CONTRIBUTING.md) for more information on release process.
141
+
142
+ ## 📚 Documentation
143
+
144
+ - [Model Context Protocol Documentation](https://modelcontextprotocol.io/)
145
+ - [Contentful Management API Documentation](https://www.contentful.com/developers/docs/references/content-management-api/)
146
+
147
+ ## ❓ Help & Support
148
+
149
+ - [Contentful support resources](https://www.contentful.com/help/getting-started/how-to-get-help/)
150
+ - [Report bugs or request features](https://github.com/contentful/contentful-mcp-server/issues)
151
+ - [Contentful Community Discord](https://www.contentful.com/discord/)
152
+
153
+ ## 📄 License and Notices
154
+
155
+ This project is licensed under the MIT License. See [LICENSE](./LICENSE) for details.
156
+
157
+ It also includes open-source components licensed under MIT, BSD-2-Clause, and Apache-2.0. For details, see the [NOTICE](./NOTICE) file.
158
+
159
+ This project includes an automated license management system that keeps track of all dependencies and their licenses. See the [AUTOMATION-FOR-LICENSES](./AUTOMATION-FOR-LICENSES.md) file for more information.
160
+
161
+ ## 🛡️ Code of Conduct
162
+
163
+ We want to provide a safe, inclusive, welcoming, and harassment-free space and experience for all participants, regardless of gender identity and expression, sexual orientation, disability, physical appearance, socioeconomic status, body size, ethnicity, nationality, level of experience, age, religion (or lack thereof), or other identity markers.
164
+
165
+ [Read our full Code of Conduct](https://www.contentful.com/developers/code-of-conduct/).
@@ -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
+ }
@@ -0,0 +1,2 @@
1
+ // Generated file - do not edit
2
+ export const VERSION = '0.0.1';
package/build/index.js ADDED
@@ -0,0 +1,33 @@
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 !== 'production') {
9
+ import('mcps-logger/console');
10
+ }
11
+ const MCP_SERVER_NAME = '@contentful/mcp-server';
12
+ async function initializeServer() {
13
+ const server = new McpServer({
14
+ name: MCP_SERVER_NAME,
15
+ version: VERSION,
16
+ });
17
+ registerAllTools(server);
18
+ registerAllPrompts(server);
19
+ registerAllResources(server);
20
+ return server;
21
+ }
22
+ async function main() {
23
+ try {
24
+ const server = await initializeServer();
25
+ const transport = new StdioServerTransport();
26
+ await server.connect(transport);
27
+ }
28
+ catch (error) {
29
+ console.error('Fatal error:', error);
30
+ process.exit(1);
31
+ }
32
+ }
33
+ 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');