@kontent-ai/mcp-server 0.9.0 → 0.11.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -99,6 +99,17 @@ npx @kontent-ai/mcp-server@latest sse
99
99
 
100
100
  * **list-languages-mapi** – List all languages configured in the environment
101
101
 
102
+ ### Workflow Management
103
+
104
+ * **list-workflows-mapi** – List all workflows in the environment with their lifecycle stages and transitions
105
+ * **change-variant-workflow-step-mapi** – Change the workflow step of a language variant (move content through workflow stages)
106
+ * **publish-variant-mapi** – Publish or schedule a language variant for publication. Supports immediate publishing (happens right now) or scheduled publishing at a specific future date and time with optional timezone specification
107
+ * **unpublish-variant-mapi** – Unpublish or schedule unpublishing of a language variant. Supports immediate unpublishing (removes content from Delivery API right now) or scheduled unpublishing at a specific future date and time with optional timezone specification
108
+
109
+ ### Utility
110
+
111
+ * **get-current-datetime** – Get the current date and time in UTC (ISO-8601 format)
112
+
102
113
  ## ⚙️ Configuration
103
114
 
104
115
  The server requires the following environment variables:
@@ -0,0 +1,106 @@
1
+ import { z } from "zod";
2
+ // Schema for a workflow step
3
+ const workflowStepSchema = z.object({
4
+ id: z
5
+ .string()
6
+ .uuid()
7
+ .describe("The unique identifier of the workflow step in UUID format"),
8
+ name: z.string().describe("The human-readable name of the workflow step"),
9
+ codename: z
10
+ .string()
11
+ .describe("The codename of the workflow step used for API operations"),
12
+ transitions_to: z
13
+ .array(z.string().uuid())
14
+ .describe("Array of workflow step IDs that this step can transition to")
15
+ .optional(),
16
+ role_ids: z
17
+ .array(z.string().uuid())
18
+ .describe("Array of role IDs that have permissions for this workflow step")
19
+ .optional(),
20
+ });
21
+ // Schema for the published step
22
+ const publishedStepSchema = z.object({
23
+ id: z
24
+ .string()
25
+ .uuid()
26
+ .describe("The unique identifier of the published step in UUID format"),
27
+ name: z
28
+ .string()
29
+ .describe("The name of the published step - typically 'Published'"),
30
+ codename: z
31
+ .string()
32
+ .describe("The codename of the published step - typically 'published'"),
33
+ unpublish_role_ids: z
34
+ .array(z.string().uuid())
35
+ .describe("Array of role IDs that can unpublish content from this step")
36
+ .optional(),
37
+ create_new_version_role_ids: z
38
+ .array(z.string().uuid())
39
+ .describe("Array of role IDs that can create new versions of content in this step")
40
+ .optional(),
41
+ });
42
+ // Schema for the scheduled step
43
+ const scheduledStepSchema = z.object({
44
+ id: z
45
+ .string()
46
+ .uuid()
47
+ .describe("The unique identifier of the scheduled step in UUID format"),
48
+ name: z
49
+ .string()
50
+ .describe("The name of the scheduled step - typically 'Scheduled'"),
51
+ codename: z
52
+ .string()
53
+ .describe("The codename of the scheduled step - typically 'scheduled'"),
54
+ });
55
+ // Schema for the archived step
56
+ const archivedStepSchema = z.object({
57
+ id: z
58
+ .string()
59
+ .uuid()
60
+ .describe("The unique identifier of the archived step in UUID format"),
61
+ name: z
62
+ .string()
63
+ .describe("The name of the archived step - typically 'Archived'"),
64
+ codename: z
65
+ .string()
66
+ .describe("The codename of the archived step - typically 'archived'"),
67
+ role_ids: z
68
+ .array(z.string().uuid())
69
+ .describe("Array of role IDs that can unarchive content from this step")
70
+ .optional(),
71
+ });
72
+ // Schema for workflow scope
73
+ const workflowScopeSchema = z.object({
74
+ content_types: z
75
+ .array(z.object({
76
+ id: z
77
+ .string()
78
+ .uuid()
79
+ .describe("The unique identifier of the content type in UUID format"),
80
+ }))
81
+ .describe("Array of content types that this workflow applies to"),
82
+ });
83
+ // Main workflow schema
84
+ export const workflowSchema = z.object({
85
+ id: z
86
+ .string()
87
+ .uuid()
88
+ .describe("The unique identifier of the workflow in UUID format"),
89
+ name: z.string().describe("The human-readable name of the workflow"),
90
+ codename: z
91
+ .string()
92
+ .describe("The codename of the workflow used for API operations"),
93
+ scopes: z
94
+ .array(workflowScopeSchema)
95
+ .describe("Array of scopes defining which content types use this workflow"),
96
+ steps: z
97
+ .array(workflowStepSchema)
98
+ .describe("Array of custom workflow steps between draft and published states"),
99
+ published_step: publishedStepSchema.describe("The published step configuration of the workflow"),
100
+ scheduled_step: scheduledStepSchema.describe("The scheduled step configuration of the workflow"),
101
+ archived_step: archivedStepSchema.describe("The archived step configuration of the workflow"),
102
+ });
103
+ // Schema for the list workflows response
104
+ export const listWorkflowsResponseSchema = z
105
+ .array(workflowSchema)
106
+ .describe("Array of workflows in the project");
package/build/server.js CHANGED
@@ -4,10 +4,12 @@ import { registerTool as registerAddContentItemMapi } from "./tools/add-content-
4
4
  import { registerTool as registerAddContentTypeMapi } from "./tools/add-content-type-mapi.js";
5
5
  import { registerTool as registerAddContentTypeSnippetMapi } from "./tools/add-content-type-snippet-mapi.js";
6
6
  import { registerTool as registerAddTaxonomyGroupMapi } from "./tools/add-taxonomy-group-mapi.js";
7
+ import { registerTool as registerChangeVariantWorkflowStepMapi } from "./tools/change-variant-workflow-step-mapi.js";
7
8
  import { registerTool as registerDeleteContentItemMapi } from "./tools/delete-content-item-mapi.js";
8
9
  import { registerTool as registerDeleteLanguageVariantMapi } from "./tools/delete-language-variant-mapi.js";
9
10
  import { registerTool as registerFilterVariantsMapi } from "./tools/filter-variants-mapi.js";
10
11
  import { registerTool as registerGetAssetMapi } from "./tools/get-asset-mapi.js";
12
+ import { registerTool as registerGetCurrentDatetime } from "./tools/get-current-datetime.js";
11
13
  import { registerTool as registerGetInitialContext } from "./tools/get-initial-context.js";
12
14
  import { registerTool as registerGetItemDapi } from "./tools/get-item-dapi.js";
13
15
  import { registerTool as registerGetItemMapi } from "./tools/get-item-mapi.js";
@@ -20,6 +22,9 @@ import { registerTool as registerListContentTypeSnippetsMapi } from "./tools/lis
20
22
  import { registerTool as registerListContentTypesMapi } from "./tools/list-content-types-mapi.js";
21
23
  import { registerTool as registerListLanguagesMapi } from "./tools/list-languages-mapi.js";
22
24
  import { registerTool as registerListTaxonomyGroupsMapi } from "./tools/list-taxonomy-groups-mapi.js";
25
+ import { registerTool as registerListWorkflowsMapi } from "./tools/list-workflows-mapi.js";
26
+ import { registerTool as registerPublishVariantMapi } from "./tools/publish-variant-mapi.js";
27
+ import { registerTool as registerUnpublishVariantMapi } from "./tools/unpublish-variant-mapi.js";
23
28
  import { registerTool as registerUpdateContentItemMapi } from "./tools/update-content-item-mapi.js";
24
29
  import { registerTool as registerUpsertLanguageVariantMapi } from "./tools/upsert-language-variant-mapi.js";
25
30
  // Create server instance
@@ -54,6 +59,11 @@ export const createServer = () => {
54
59
  registerDeleteContentItemMapi(server);
55
60
  registerUpsertLanguageVariantMapi(server);
56
61
  registerDeleteLanguageVariantMapi(server);
62
+ registerListWorkflowsMapi(server);
63
+ registerChangeVariantWorkflowStepMapi(server);
57
64
  registerFilterVariantsMapi(server);
65
+ registerPublishVariantMapi(server);
66
+ registerUnpublishVariantMapi(server);
67
+ registerGetCurrentDatetime(server);
58
68
  return { server };
59
69
  };
@@ -0,0 +1,48 @@
1
+ import { z } from "zod";
2
+ import { createMapiClient } from "../clients/kontentClients.js";
3
+ import { handleMcpToolError } from "../utils/errorHandler.js";
4
+ import { createMcpToolSuccessResponse } from "../utils/responseHelper.js";
5
+ export const registerTool = (server) => {
6
+ server.tool("change-variant-workflow-step-mapi", "Change the workflow step of a language variant in Kontent.ai. This operation moves a language variant to a different step in the workflow, enabling content lifecycle management such as moving content from draft to review, review to published, etc.", {
7
+ itemId: z
8
+ .string()
9
+ .uuid()
10
+ .describe("Internal ID (UUID) of the content item whose language variant workflow step you want to change"),
11
+ languageId: z
12
+ .string()
13
+ .uuid()
14
+ .describe("Internal ID (UUID) of the language variant. Use '00000000-0000-0000-0000-000000000000' for the default language"),
15
+ workflowId: z
16
+ .string()
17
+ .uuid()
18
+ .describe("Internal ID (UUID) of the workflow. This is the workflow that contains the target step"),
19
+ workflowStepId: z
20
+ .string()
21
+ .uuid()
22
+ .describe("Internal ID (UUID) of the target workflow step. This must be a valid step ID from the specified workflow. Common steps include Draft, Review, Published, and Archived, but the actual IDs depend on your specific workflow configuration"),
23
+ }, async ({ itemId, languageId, workflowId, workflowStepId }) => {
24
+ const client = createMapiClient();
25
+ try {
26
+ const response = await client
27
+ .changeWorkflowOfLanguageVariant()
28
+ .byItemId(itemId)
29
+ .byLanguageId(languageId)
30
+ .withData({
31
+ workflow_identifier: {
32
+ id: workflowId,
33
+ },
34
+ step_identifier: {
35
+ id: workflowStepId,
36
+ },
37
+ })
38
+ .toPromise();
39
+ return createMcpToolSuccessResponse({
40
+ message: `Successfully changed workflow step of language variant '${languageId}' for content item '${itemId}' to workflow step '${workflowStepId}'`,
41
+ result: response.data,
42
+ });
43
+ }
44
+ catch (error) {
45
+ return handleMcpToolError(error, "Workflow Step Change");
46
+ }
47
+ });
48
+ };
@@ -80,6 +80,28 @@ ALL FOUR elements must be included in language variant using their internal IDs:
80
80
  - Create new language variants when expanding to additional languages
81
81
  - Organize with taxonomies for better content categorization
82
82
 
83
+ ### Workflow Step Management
84
+
85
+ **CRITICAL**: When changing workflow steps of language variants, you must:
86
+
87
+ 1. **Provide the workflowId parameter** - This is mandatory for all workflow step changes
88
+ 2. **Use internal IDs** for itemId, languageId, and workflowStepId parameters
89
+ 3. **Ensure the target workflow step exists** in the specified workflow
90
+
91
+ **WORKFLOW ID REQUIREMENT**: The workflowId parameter identifies which workflow contains the target step. Different content types may use different workflows, so always specify the correct workflow ID when changing workflow steps.
92
+
93
+ **Example**: To move a language variant to a "review" step:
94
+ - itemId: "content_item_internal_id"
95
+ - languageId: "language_internal_id"
96
+ - workflowStepId: "review_step_internal_id"
97
+ - workflowId: "workflow_internal_id" (MANDATORY)
98
+
99
+ **Failure to provide the workflowId parameter will result in workflow step change failures.**
100
+
101
+ ### Current date and time
102
+
103
+ **CRITICAL**: Always use the **get-current-datetime tool** to obtain the current UTC time. Never assume the current time.
104
+
83
105
  ## Essential Concepts
84
106
 
85
107
  **Taxonomies** provide hierarchical content categorization, allowing you to organize and tag content systematically.
@@ -0,0 +1,9 @@
1
+ import { createMcpToolSuccessResponse } from "../utils/responseHelper.js";
2
+ export const registerTool = (server) => {
3
+ server.tool("get-current-datetime", "Get the current date and time in UTC (ISO-8601 format)", {}, async () => {
4
+ const now = new Date();
5
+ return createMcpToolSuccessResponse({
6
+ datetime: now.toISOString(),
7
+ });
8
+ });
9
+ };
@@ -0,0 +1,15 @@
1
+ import { createMapiClient } from "../clients/kontentClients.js";
2
+ import { handleMcpToolError } from "../utils/errorHandler.js";
3
+ import { createMcpToolSuccessResponse } from "../utils/responseHelper.js";
4
+ export const registerTool = (server) => {
5
+ server.tool("list-workflows-mapi", "Get all workflows from Management API. Workflows define the content lifecycle stages and transitions between them.", {}, async () => {
6
+ const client = createMapiClient();
7
+ try {
8
+ const response = await client.listWorkflows().toPromise();
9
+ return createMcpToolSuccessResponse(response.data);
10
+ }
11
+ catch (error) {
12
+ return handleMcpToolError(error, "Workflows Listing");
13
+ }
14
+ });
15
+ };
@@ -0,0 +1,78 @@
1
+ import { z } from "zod";
2
+ import { createMapiClient } from "../clients/kontentClients.js";
3
+ import { handleMcpToolError } from "../utils/errorHandler.js";
4
+ import { createMcpToolSuccessResponse } from "../utils/responseHelper.js";
5
+ export const registerTool = (server) => {
6
+ server.tool("publish-variant-mapi", "Publish or schedule a language variant of a content item in Kontent.ai. This operation can either immediately publish the variant (publishing happens right now) or schedule it for publication at a specific future date and time. For immediate publishing: the variant is published immediately and becomes available through the Delivery API. For scheduled publishing: the variant moves to a 'Scheduled' workflow state and will automatically transition to 'Published' at the specified time. The variant must be in a valid state with all required fields filled and validation rules satisfied. A variant can only be published if a transition is defined between the variant's current workflow step and the Published workflow step.", {
7
+ itemId: z
8
+ .string()
9
+ .uuid()
10
+ .describe("Internal ID (UUID) of the content item whose language variant you want to publish or schedule. This identifies the content item container that holds all language variants."),
11
+ languageId: z
12
+ .string()
13
+ .uuid()
14
+ .describe("Internal ID (UUID) of the language variant to publish or schedule. Use '00000000-0000-0000-0000-000000000000' for the default language. Each language in your project has a unique ID that identifies the specific variant."),
15
+ scheduledTo: z
16
+ .string()
17
+ .datetime()
18
+ .optional()
19
+ .describe("ISO 8601 formatted date and time when the publish action should occur (e.g., '2024-12-25T10:00:00Z' for UTC or '2024-12-25T10:00:00+02:00' for specific timezone). If not provided, the variant will be published immediately. If provided, must be a future date/time. The actual execution may have up to 5 minutes delay from the specified time."),
20
+ displayTimezone: z
21
+ .string()
22
+ .optional()
23
+ .describe("The timezone identifier for displaying the scheduled time in the Kontent.ai UI (e.g., 'America/New_York', 'Europe/London', 'UTC'). This parameter is used for scheduled publishing to specify the timezone context for the scheduled_to parameter. If not provided, the system will use the default timezone. This helps content creators understand when content will be published in their local context."),
24
+ }, async ({ itemId, languageId, scheduledTo, displayTimezone }) => {
25
+ const client = createMapiClient();
26
+ try {
27
+ // Validate that displayTimezone can only be used with scheduledTo
28
+ if (displayTimezone && !scheduledTo) {
29
+ throw new Error("The 'displayTimezone' parameter can only be used in combination with 'scheduledTo' parameter for scheduled publishing.");
30
+ }
31
+ let action;
32
+ let message;
33
+ if (scheduledTo) {
34
+ // Scheduled publishing
35
+ const requestData = {
36
+ scheduled_to: scheduledTo,
37
+ };
38
+ // Add display_timezone if provided
39
+ if (displayTimezone) {
40
+ requestData.display_timezone = displayTimezone;
41
+ }
42
+ await client
43
+ .publishLanguageVariant()
44
+ .byItemId(itemId)
45
+ .byLanguageId(languageId)
46
+ .withData(requestData)
47
+ .toPromise();
48
+ action = "scheduled";
49
+ message = `Successfully scheduled language variant '${languageId}' for content item '${itemId}' to be published at '${scheduledTo}'${displayTimezone ? ` (timezone: ${displayTimezone})` : ""}.`;
50
+ }
51
+ else {
52
+ // Immediate publishing
53
+ await client
54
+ .publishLanguageVariant()
55
+ .byItemId(itemId)
56
+ .byLanguageId(languageId)
57
+ .withoutData()
58
+ .toPromise();
59
+ action = "published";
60
+ message = `Successfully published language variant '${languageId}' for content item '${itemId}'. The content is now live and available through Delivery API.`;
61
+ }
62
+ return createMcpToolSuccessResponse({
63
+ message,
64
+ result: {
65
+ itemId,
66
+ languageId,
67
+ scheduledTo: scheduledTo || null,
68
+ displayTimezone: displayTimezone || null,
69
+ action,
70
+ timestamp: new Date().toISOString(),
71
+ },
72
+ });
73
+ }
74
+ catch (error) {
75
+ return handleMcpToolError(error, "Publish/Schedule Language Variant");
76
+ }
77
+ });
78
+ };
@@ -0,0 +1,78 @@
1
+ import { z } from "zod";
2
+ import { createMapiClient } from "../clients/kontentClients.js";
3
+ import { handleMcpToolError } from "../utils/errorHandler.js";
4
+ import { createMcpToolSuccessResponse } from "../utils/responseHelper.js";
5
+ export const registerTool = (server) => {
6
+ server.tool("unpublish-variant-mapi", "Unpublish or schedule unpublishing of a language variant of a content item in Kontent.ai. This operation can either immediately unpublish the variant (making it unavailable through the Delivery API) or schedule it for unpublishing at a specific future date and time. For immediate unpublishing: the variant is unpublished right away and moves to the 'Archived' workflow step, becoming unavailable through the Delivery API. For scheduled unpublishing: the variant remains published but is scheduled to be automatically unpublished at the specified time. The variant must currently be in the 'Published' state for this operation to succeed.", {
7
+ itemId: z
8
+ .string()
9
+ .uuid()
10
+ .describe("Internal ID (UUID) of the content item whose language variant you want to unpublish or schedule for unpublishing. This identifies the content item container that holds all language variants."),
11
+ languageId: z
12
+ .string()
13
+ .uuid()
14
+ .describe("Internal ID (UUID) of the language variant to unpublish or schedule for unpublishing. Use '00000000-0000-0000-0000-000000000000' for the default language. Each language in your project has a unique ID that identifies the specific variant."),
15
+ scheduledTo: z
16
+ .string()
17
+ .datetime()
18
+ .optional()
19
+ .describe("ISO 8601 formatted date and time when the unpublish action should occur (e.g., '2024-12-25T10:00:00Z' for UTC or '2024-12-25T10:00:00+02:00' for specific timezone). If not provided, the variant will be unpublished immediately. If provided, must be a future date/time. The actual execution may have up to 5 minutes delay from the specified time. When unpublished, the content will no longer be available through the Delivery API."),
20
+ displayTimezone: z
21
+ .string()
22
+ .optional()
23
+ .describe("The timezone identifier for displaying the scheduled time in the Kontent.ai UI (e.g., 'America/New_York', 'Europe/London', 'UTC'). This parameter is used for scheduled unpublishing to specify the timezone context for the scheduled_to parameter. If not provided, the system will use the default timezone. This helps content creators understand when content will be unpublished in their local context."),
24
+ }, async ({ itemId, languageId, scheduledTo, displayTimezone }) => {
25
+ const client = createMapiClient();
26
+ try {
27
+ // Validate that displayTimezone can only be used with scheduledTo
28
+ if (displayTimezone && !scheduledTo) {
29
+ throw new Error("The 'displayTimezone' parameter can only be used in combination with 'scheduledTo' parameter for scheduled unpublishing.");
30
+ }
31
+ let action;
32
+ let message;
33
+ if (scheduledTo) {
34
+ // Scheduled unpublishing
35
+ const requestData = {
36
+ scheduled_to: scheduledTo,
37
+ };
38
+ // Add display_timezone if provided
39
+ if (displayTimezone) {
40
+ requestData.display_timezone = displayTimezone;
41
+ }
42
+ await client
43
+ .unpublishLanguageVariant()
44
+ .byItemId(itemId)
45
+ .byLanguageId(languageId)
46
+ .withData(requestData)
47
+ .toPromise();
48
+ action = "scheduled for unpublishing";
49
+ message = `Successfully scheduled language variant '${languageId}' for content item '${itemId}' to be unpublished at '${scheduledTo}'${displayTimezone ? ` (timezone: ${displayTimezone})` : ""}. The content will be removed from Delivery API at the scheduled time.`;
50
+ }
51
+ else {
52
+ // Immediate unpublishing
53
+ await client
54
+ .unpublishLanguageVariant()
55
+ .byItemId(itemId)
56
+ .byLanguageId(languageId)
57
+ .withoutData()
58
+ .toPromise();
59
+ action = "unpublished";
60
+ message = `Successfully unpublished language variant '${languageId}' for content item '${itemId}'. The content has been moved to Archived and is no longer available through Delivery API.`;
61
+ }
62
+ return createMcpToolSuccessResponse({
63
+ message,
64
+ result: {
65
+ itemId,
66
+ languageId,
67
+ scheduledTo: scheduledTo || null,
68
+ displayTimezone: displayTimezone || null,
69
+ action,
70
+ timestamp: new Date().toISOString(),
71
+ },
72
+ });
73
+ }
74
+ catch (error) {
75
+ return handleMcpToolError(error, "Unpublish/Schedule Unpublishing Language Variant");
76
+ }
77
+ });
78
+ };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@kontent-ai/mcp-server",
3
- "version": "0.9.0",
3
+ "version": "0.11.0",
4
4
  "type": "module",
5
5
  "scripts": {
6
6
  "build": "rimraf build && tsc",