@kontent-ai/mcp-server 0.22.1 → 0.23.1

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 (34) hide show
  1. package/README.md +3 -3
  2. package/build/schemas/contentItemSchemas.js +9 -6
  3. package/build/schemas/filterVariantSchemas.js +1 -1
  4. package/build/schemas/searchOperationSchemas.js +1 -4
  5. package/build/schemas/workflowSchemas.js +6 -14
  6. package/build/server.js +2 -6
  7. package/build/telemetry/applicationInsights.js +1 -1
  8. package/build/test/schemas/contentItemSchemas.spec.js +81 -0
  9. package/build/tools/change-variant-workflow-step-mapi.js +3 -4
  10. package/build/tools/create-variant-version-mapi.js +1 -2
  11. package/build/tools/get-asset-mapi.js +1 -1
  12. package/build/tools/get-item-mapi.js +1 -1
  13. package/build/tools/get-latest-variant-mapi.js +1 -1
  14. package/build/tools/get-patch-guide.js +12 -0
  15. package/build/tools/get-published-variant-mapi.js +1 -1
  16. package/build/tools/get-taxonomy-group-mapi.js +1 -1
  17. package/build/tools/get-type-mapi.js +1 -1
  18. package/build/tools/get-type-snippet-mapi.js +1 -1
  19. package/build/tools/list-assets-mapi.js +1 -1
  20. package/build/tools/list-collections-mapi.js +1 -1
  21. package/build/tools/list-content-type-snippets-mapi.js +1 -1
  22. package/build/tools/list-content-types-mapi.js +1 -1
  23. package/build/tools/list-languages-mapi.js +1 -1
  24. package/build/tools/list-taxonomy-groups-mapi.js +1 -1
  25. package/build/tools/list-workflows-mapi.js +1 -1
  26. package/build/tools/patch-collections-mapi.js +1 -1
  27. package/build/tools/patch-content-type-mapi.js +1 -1
  28. package/build/tools/patch-language-mapi.js +1 -1
  29. package/build/tools/publish-variant-mapi.js +3 -5
  30. package/build/tools/unpublish-variant-mapi.js +3 -5
  31. package/build/tools/upsert-language-variant-mapi.js +1 -1
  32. package/package.json +14 -14
  33. package/build/tools/context/initial-context.js +0 -201
  34. package/build/tools/get-initial-context.js +0 -13
package/README.md CHANGED
@@ -61,9 +61,9 @@ npx @kontent-ai/mcp-server@latest shttp
61
61
 
62
62
  ## 🛠️ Available Tools
63
63
 
64
- ### Context and Setup
64
+ ### Patch Operations Guide
65
65
 
66
- * **get-initial-context** – 🚨 **MANDATORY FIRST STEP**: This tool MUST be called before using ANY other tools. It provides essential context, configuration, and operational guidelines for Kontent.ai
66
+ * **get-patch-guide** – 🚨 **REQUIRED before any patch operation**. Get JSON Patch operations guide for Kontent.ai Management API
67
67
 
68
68
  ### Content Type Management
69
69
 
@@ -99,7 +99,7 @@ npx @kontent-ai/mcp-server@latest shttp
99
99
  * **add-content-item-mapi** – Add new Kontent.ai content item via Management API. This creates the content item structure but does not add content to language variants. Use upsert-language-variant-mapi to add content to the item
100
100
  * **update-content-item-mapi** – Update existing Kontent.ai content item by internal ID via Management API. The content item must already exist - this tool will not create new items
101
101
  * **delete-content-item-mapi** – Delete Kontent.ai content item by internal ID from Management API
102
- * **upsert-language-variant-mapi** – Create or update Kontent.ai language variant of a content item via Management API. This adds actual content to the content item elements. When updating an existing variant, only the provided elements will be modified
102
+ * **upsert-language-variant-mapi** – Create or update Kontent.ai language variant of a content item via Management API. Element values must fulfill limitations and guidelines defined in content type. When updating, only provided elements will be modified
103
103
  * **create-variant-version-mapi** – Create new version of Kontent.ai language variant via Management API. This operation creates a new version of an existing language variant, useful for content versioning and creating new drafts from published content
104
104
  * **delete-language-variant-mapi** – Delete Kontent.ai language variant from Management API
105
105
  * **filter-variants-mapi** – Filter Kontent.ai language variants of content items using Management API. Use for exact keyword matching and finding specific terms in content. Supports full filtering capabilities (content types, workflow steps, taxonomies, etc.) and optionally includes full content of variants. Returns paginated results with continuation token for fetching subsequent pages
@@ -1,14 +1,17 @@
1
1
  import { z } from "zod";
2
2
  import { referenceObjectSchema } from "./referenceObjectSchema.js";
3
- const languageVariantElementBaseSchema = z.object({
3
+ // Recursive schema for rich text elements within components
4
+ const richTextElementInComponentSchema = z.lazy(() => z.object({
4
5
  element: referenceObjectSchema,
5
- value: z.any(),
6
- });
7
- const richTextComponentSchema = z.object({
6
+ value: z.string().nullable(),
7
+ components: z.array(richTextComponentSchema).nullable().optional(),
8
+ }));
9
+ // Recursive schema for rich text components
10
+ const richTextComponentSchema = z.lazy(() => z.object({
8
11
  id: z.string(),
9
12
  type: referenceObjectSchema,
10
- elements: z.array(languageVariantElementBaseSchema),
11
- });
13
+ elements: z.array(richTextElementInComponentSchema),
14
+ }));
12
15
  const assetInVariantElementSchema = z.object({
13
16
  element: referenceObjectSchema,
14
17
  value: z.array(referenceObjectSchema).nullable(),
@@ -10,7 +10,7 @@ const userReferenceSchema = z
10
10
  }),
11
11
  z.object({
12
12
  id: z.never().optional(),
13
- email: z.string().email().describe("User email address"),
13
+ email: z.email().describe("User email address"),
14
14
  }),
15
15
  ])
16
16
  .describe("Reference to a user by either their id or email (but not both)");
@@ -5,10 +5,7 @@ export const searchOperationSchema = z.object({
5
5
  .describe("Search phrase for AI-powered semantic search. Uses vector database to find content by meaning and similarity, not just exact keyword matching"),
6
6
  filter: z
7
7
  .object({
8
- variantId: z
9
- .string()
10
- .uuid()
11
- .describe("UUID of the language variant to filter by"),
8
+ variantId: z.uuid().describe("UUID of the language variant to filter by"),
12
9
  })
13
10
  .describe("Mandatory content item variant filter to restrict search scope. Must specify a valid variant UUID"),
14
11
  });
@@ -2,7 +2,6 @@ import { z } from "zod";
2
2
  // Schema for a workflow step
3
3
  const workflowStepSchema = z.object({
4
4
  id: z
5
- .string()
6
5
  .uuid()
7
6
  .describe("The unique identifier of the workflow step in UUID format"),
8
7
  name: z.string().describe("The human-readable name of the workflow step"),
@@ -10,18 +9,17 @@ const workflowStepSchema = z.object({
10
9
  .string()
11
10
  .describe("The codename of the workflow step used for API operations"),
12
11
  transitions_to: z
13
- .array(z.string().uuid())
12
+ .array(z.uuid())
14
13
  .describe("Array of workflow step IDs that this step can transition to")
15
14
  .optional(),
16
15
  role_ids: z
17
- .array(z.string().uuid())
16
+ .array(z.uuid())
18
17
  .describe("Array of role IDs that have permissions for this workflow step")
19
18
  .optional(),
20
19
  });
21
20
  // Schema for the published step
22
21
  const publishedStepSchema = z.object({
23
22
  id: z
24
- .string()
25
23
  .uuid()
26
24
  .describe("The unique identifier of the published step in UUID format"),
27
25
  name: z
@@ -31,18 +29,17 @@ const publishedStepSchema = z.object({
31
29
  .string()
32
30
  .describe("The codename of the published step - typically 'published'"),
33
31
  unpublish_role_ids: z
34
- .array(z.string().uuid())
32
+ .array(z.uuid())
35
33
  .describe("Array of role IDs that can unpublish content from this step")
36
34
  .optional(),
37
35
  create_new_version_role_ids: z
38
- .array(z.string().uuid())
36
+ .array(z.uuid())
39
37
  .describe("Array of role IDs that can create new versions of content in this step")
40
38
  .optional(),
41
39
  });
42
40
  // Schema for the scheduled step
43
41
  const scheduledStepSchema = z.object({
44
42
  id: z
45
- .string()
46
43
  .uuid()
47
44
  .describe("The unique identifier of the scheduled step in UUID format"),
48
45
  name: z
@@ -55,7 +52,6 @@ const scheduledStepSchema = z.object({
55
52
  // Schema for the archived step
56
53
  const archivedStepSchema = z.object({
57
54
  id: z
58
- .string()
59
55
  .uuid()
60
56
  .describe("The unique identifier of the archived step in UUID format"),
61
57
  name: z
@@ -65,7 +61,7 @@ const archivedStepSchema = z.object({
65
61
  .string()
66
62
  .describe("The codename of the archived step - typically 'archived'"),
67
63
  role_ids: z
68
- .array(z.string().uuid())
64
+ .array(z.uuid())
69
65
  .describe("Array of role IDs that can unarchive content from this step")
70
66
  .optional(),
71
67
  });
@@ -74,7 +70,6 @@ const workflowScopeSchema = z.object({
74
70
  content_types: z
75
71
  .array(z.object({
76
72
  id: z
77
- .string()
78
73
  .uuid()
79
74
  .describe("The unique identifier of the content type in UUID format"),
80
75
  }))
@@ -82,10 +77,7 @@ const workflowScopeSchema = z.object({
82
77
  });
83
78
  // Main workflow schema
84
79
  export const workflowSchema = z.object({
85
- id: z
86
- .string()
87
- .uuid()
88
- .describe("The unique identifier of the workflow in UUID format"),
80
+ id: z.uuid().describe("The unique identifier of the workflow in UUID format"),
89
81
  name: z.string().describe("The human-readable name of the workflow"),
90
82
  codename: z
91
83
  .string()
package/build/server.js CHANGED
@@ -12,9 +12,9 @@ import { registerTool as registerDeleteContentTypeMapi } from "./tools/delete-co
12
12
  import { registerTool as registerDeleteLanguageVariantMapi } from "./tools/delete-language-variant-mapi.js";
13
13
  import { registerTool as registerFilterVariantsMapi } from "./tools/filter-variants-mapi.js";
14
14
  import { registerTool as registerGetAssetMapi } from "./tools/get-asset-mapi.js";
15
- import { registerTool as registerGetInitialContext } from "./tools/get-initial-context.js";
16
15
  import { registerTool as registerGetItemMapi } from "./tools/get-item-mapi.js";
17
16
  import { registerTool as registerGetLatestVariantMapi } from "./tools/get-latest-variant-mapi.js";
17
+ import { registerTool as registerGetPatchGuide } from "./tools/get-patch-guide.js";
18
18
  import { registerTool as registerGetPublishedVariantMapi } from "./tools/get-published-variant-mapi.js";
19
19
  import { registerTool as registerGetTaxonomyGroupMapi } from "./tools/get-taxonomy-group-mapi.js";
20
20
  import { registerTool as registerGetTypeMapi } from "./tools/get-type-mapi.js";
@@ -45,13 +45,9 @@ export const createServer = () => {
45
45
  const server = new McpServer({
46
46
  name: "kontent-ai",
47
47
  version: packageJson.version,
48
- capabilities: {
49
- resources: {},
50
- tools: {},
51
- },
52
48
  });
53
49
  // Register all tools
54
- registerGetInitialContext(server);
50
+ registerGetPatchGuide(server);
55
51
  registerGetItemMapi(server);
56
52
  registerGetLatestVariantMapi(server);
57
53
  registerGetPublishedVariantMapi(server);
@@ -148,7 +148,7 @@ export function initializeApplicationInsights() {
148
148
  .setAutoCollectDependencies(false)
149
149
  .setAutoDependencyCorrelation(false)
150
150
  .setAutoCollectHeartbeat(false)
151
- .setAutoCollectPerformance(false)
151
+ .setAutoCollectPerformance(false, false)
152
152
  .setAutoCollectIncomingRequestAzureFunctions(false)
153
153
  .setAutoCollectPreAggregatedMetrics(false)
154
154
  .start();
@@ -0,0 +1,81 @@
1
+ import * as assert from "node:assert";
2
+ import { describe, it } from "mocha";
3
+ import { languageVariantElementSchema } from "../../schemas/contentItemSchemas.js";
4
+ describe("languageVariantElementSchema", () => {
5
+ describe("nested rich text components", () => {
6
+ it("validates rich text element with deeply nested components", () => {
7
+ const nestedRichTextElement = {
8
+ element: { id: "1fefb369-53b9-4108-9ca3-837c99010c29" },
9
+ value: '<p>rich1</p>\n<object type="application/kenticocloud" data-type="component" data-id="20a941ec-91bf-4a40-89c8-90b5e31dae27"></object>',
10
+ components: [
11
+ {
12
+ id: "20a941ec-91bf-4a40-89c8-90b5e31dae27",
13
+ type: { id: "da3871d1-f942-4d8c-b518-f4c6c97c5174" },
14
+ elements: [
15
+ {
16
+ element: { id: "1fefb369-53b9-4108-9ca3-837c99010c29" },
17
+ value: '<p>rich2 nested</p>\n<object type="application/kenticocloud" data-type="component" data-id="5dd61cc6-d375-49d7-a8e3-e4dcd029be1a"></object>',
18
+ components: [
19
+ {
20
+ id: "5dd61cc6-d375-49d7-a8e3-e4dcd029be1a",
21
+ type: { id: "da3871d1-f942-4d8c-b518-f4c6c97c5174" },
22
+ elements: [
23
+ {
24
+ value: "<p>rich3 nested nested</p>",
25
+ element: { id: "1fefb369-53b9-4108-9ca3-837c99010c29" },
26
+ components: [],
27
+ },
28
+ ],
29
+ },
30
+ ],
31
+ },
32
+ ],
33
+ },
34
+ ],
35
+ };
36
+ const result = languageVariantElementSchema.safeParse(nestedRichTextElement);
37
+ assert.strictEqual(result.success, true, `Schema validation failed: ${result.success ? "" : JSON.stringify(result.error.issues, null, 2)}`);
38
+ if (result.success) {
39
+ assert.deepStrictEqual(result.data, nestedRichTextElement, "Parsed data should match input exactly");
40
+ }
41
+ });
42
+ it("validates rich text element with single level components", () => {
43
+ const richTextElement = {
44
+ element: { id: "1849f877-cd37-4ed0-8df6-3173748a126f" },
45
+ value: "<p>Some content</p>",
46
+ components: [
47
+ {
48
+ id: "af87f9d1-a448-489b-a5bd-9ab3e5e5f2e2",
49
+ type: { id: "924daea8-5d87-4e7c-8c22-7b66044593c3" },
50
+ elements: [
51
+ {
52
+ element: { id: "924daea8-5d87-4e7c-8c22-7b66044593c3" },
53
+ value: "Nested value",
54
+ },
55
+ ],
56
+ },
57
+ ],
58
+ };
59
+ const result = languageVariantElementSchema.safeParse(richTextElement);
60
+ assert.strictEqual(result.success, true);
61
+ });
62
+ it("validates rich text element with empty components array", () => {
63
+ const richTextElement = {
64
+ element: { id: "c0021332-250b-46d0-a283-c114c85f6062" },
65
+ value: "<p>Some content</p>",
66
+ components: [],
67
+ };
68
+ const result = languageVariantElementSchema.safeParse(richTextElement);
69
+ assert.strictEqual(result.success, true);
70
+ });
71
+ it("validates rich text element with null components", () => {
72
+ const richTextElement = {
73
+ element: { id: "1ae5d294-5861-4555-881c-9279343443a5" },
74
+ value: "<p>Some content</p>",
75
+ components: null,
76
+ };
77
+ const result = languageVariantElementSchema.safeParse(richTextElement);
78
+ assert.strictEqual(result.success, true);
79
+ });
80
+ });
81
+ });
@@ -4,13 +4,12 @@ import { handleMcpToolError } from "../utils/errorHandler.js";
4
4
  import { createMcpToolSuccessResponse } from "../utils/responseHelper.js";
5
5
  export const registerTool = (server) => {
6
6
  server.tool("change-variant-workflow-step-mapi", "Change Kontent.ai variant workflow step", {
7
- itemId: z.string().uuid().describe("Content item UUID"),
7
+ itemId: z.uuid().describe("Content item UUID"),
8
8
  languageId: z
9
- .string()
10
9
  .uuid()
11
10
  .describe("Language variant UUID (default: 00000000-0000-0000-0000-000000000000)"),
12
- workflowId: z.string().uuid().describe("Workflow UUID"),
13
- workflowStepId: z.string().uuid().describe("Target workflow step UUID"),
11
+ workflowId: z.uuid().describe("Workflow UUID"),
12
+ workflowStepId: z.uuid().describe("Target workflow step UUID"),
14
13
  }, async ({ itemId, languageId, workflowId, workflowStepId }, { authInfo: { token, clientId } = {} }) => {
15
14
  const client = createMapiClient(clientId, token);
16
15
  try {
@@ -4,9 +4,8 @@ import { handleMcpToolError } from "../utils/errorHandler.js";
4
4
  import { createMcpToolSuccessResponse } from "../utils/responseHelper.js";
5
5
  export const registerTool = (server) => {
6
6
  server.tool("create-variant-version-mapi", "Create new version of Kontent.ai variant", {
7
- itemId: z.string().uuid().describe("Content item UUID"),
7
+ itemId: z.uuid().describe("Content item UUID"),
8
8
  languageId: z
9
- .string()
10
9
  .uuid()
11
10
  .describe("Language variant UUID (default: 00000000-0000-0000-0000-000000000000)"),
12
11
  }, async ({ itemId, languageId }, { authInfo: { token, clientId } = {} }) => {
@@ -3,7 +3,7 @@ import { createMapiClient } from "../clients/kontentClients.js";
3
3
  import { handleMcpToolError } from "../utils/errorHandler.js";
4
4
  import { createMcpToolSuccessResponse } from "../utils/responseHelper.js";
5
5
  export const registerTool = (server) => {
6
- server.tool("get-asset-mapi", "Get Kontent.ai asset by ID", {
6
+ server.tool("get-asset-mapi", "Get Kontent.ai asset. Assets are digital files (images, videos, documents) referenced in content.", {
7
7
  assetId: z.string().describe("Asset ID"),
8
8
  }, async ({ assetId }, { authInfo: { token, clientId } = {} }) => {
9
9
  const client = createMapiClient(clientId, token);
@@ -3,7 +3,7 @@ import { createMapiClient } from "../clients/kontentClients.js";
3
3
  import { handleMcpToolError } from "../utils/errorHandler.js";
4
4
  import { createMcpToolSuccessResponse } from "../utils/responseHelper.js";
5
5
  export const registerTool = (server) => {
6
- server.tool("get-item-mapi", "Get Kontent.ai item by ID", {
6
+ server.tool("get-item-mapi", "Get Kontent.ai content item. Items are language-neutral containers; one item has multiple language variants.", {
7
7
  id: z.string().describe("Item ID"),
8
8
  }, async ({ id }, { authInfo: { token, clientId } = {} }) => {
9
9
  const client = createMapiClient(clientId, token);
@@ -3,7 +3,7 @@ import { createMapiClient } from "../clients/kontentClients.js";
3
3
  import { handleMcpToolError } from "../utils/errorHandler.js";
4
4
  import { createVariantMcpToolSuccessResponse } from "../utils/responseHelper.js";
5
5
  export const registerTool = (server) => {
6
- server.tool("get-latest-variant-mapi", "Get latest version of Kontent.ai language variant from Management API", {
6
+ server.tool("get-latest-variant-mapi", "Get latest Kontent.ai language variant. Variants hold language-specific content; structure defined by content type and its snippets.", {
7
7
  itemId: z.string().describe("Item ID"),
8
8
  languageId: z.string().describe("Language variant ID"),
9
9
  }, async ({ itemId, languageId }, { authInfo: { token, clientId } = {} }) => {
@@ -0,0 +1,12 @@
1
+ import { createMcpToolSuccessResponse } from "../utils/responseHelper.js";
2
+ import { patchOperationsGuide } from "./context/patch-operations-guide.js";
3
+ export const registerTool = (server) => {
4
+ server.tool("get-patch-guide", "REQUIRED before any patch operation. Get JSON Patch operations guide for Kontent.ai Management API.", {}, async () => {
5
+ try {
6
+ return createMcpToolSuccessResponse(patchOperationsGuide);
7
+ }
8
+ catch (error) {
9
+ throw new Error(`Failed to read patch guide: ${error instanceof Error ? error.message : "Unknown error"}`);
10
+ }
11
+ });
12
+ };
@@ -3,7 +3,7 @@ import { createMapiClient } from "../clients/kontentClients.js";
3
3
  import { handleMcpToolError } from "../utils/errorHandler.js";
4
4
  import { createVariantMcpToolSuccessResponse } from "../utils/responseHelper.js";
5
5
  export const registerTool = (server) => {
6
- server.tool("get-published-variant-mapi", "Get published version of Kontent.ai language variant from Management API", {
6
+ server.tool("get-published-variant-mapi", "Get published Kontent.ai language variant. Variants hold language-specific content; structure defined by content type and its snippets.", {
7
7
  itemId: z.string().describe("Item ID"),
8
8
  languageId: z.string().describe("Language variant ID"),
9
9
  }, async ({ itemId, languageId }, { authInfo: { token, clientId } = {} }) => {
@@ -3,7 +3,7 @@ import { createMapiClient } from "../clients/kontentClients.js";
3
3
  import { handleMcpToolError } from "../utils/errorHandler.js";
4
4
  import { createMcpToolSuccessResponse } from "../utils/responseHelper.js";
5
5
  export const registerTool = (server) => {
6
- server.tool("get-taxonomy-group-mapi", "Get Kontent.ai taxonomy group by ID", {
6
+ server.tool("get-taxonomy-group-mapi", "Get Kontent.ai taxonomy group. Taxonomies provide hierarchical categorization for organizing and tagging content.", {
7
7
  id: z.string().describe("Taxonomy group ID"),
8
8
  }, async ({ id }, { authInfo: { token, clientId } = {} }) => {
9
9
  const client = createMapiClient(clientId, token);
@@ -3,7 +3,7 @@ import { createMapiClient } from "../clients/kontentClients.js";
3
3
  import { handleMcpToolError } from "../utils/errorHandler.js";
4
4
  import { createMcpToolSuccessResponse } from "../utils/responseHelper.js";
5
5
  export const registerTool = (server) => {
6
- server.tool("get-type-mapi", "Get Kontent.ai content type by ID", {
6
+ server.tool("get-type-mapi", "Get Kontent.ai content type. Types define variant structure: field definitions, validation rules, and element types.", {
7
7
  id: z.string().describe("Content type ID"),
8
8
  }, async ({ id }, { authInfo: { token, clientId } = {} }) => {
9
9
  const client = createMapiClient(clientId, token);
@@ -3,7 +3,7 @@ import { createMapiClient } from "../clients/kontentClients.js";
3
3
  import { handleMcpToolError } from "../utils/errorHandler.js";
4
4
  import { createMcpToolSuccessResponse } from "../utils/responseHelper.js";
5
5
  export const registerTool = (server) => {
6
- server.tool("get-type-snippet-mapi", "Get Kontent.ai content type snippet by ID", {
6
+ server.tool("get-type-snippet-mapi", "Get Kontent.ai content type snippet. Snippets are reusable element sets included in content types via snippet element.", {
7
7
  id: z.string().describe("Snippet ID"),
8
8
  }, async ({ id }, { authInfo: { token, clientId } = {} }) => {
9
9
  const client = createMapiClient(clientId, token);
@@ -3,7 +3,7 @@ import { listAssetsSchema } from "../schemas/listSchemas.js";
3
3
  import { handleMcpToolError } from "../utils/errorHandler.js";
4
4
  import { createMcpToolSuccessResponse } from "../utils/responseHelper.js";
5
5
  export const registerTool = (server) => {
6
- server.tool("list-assets-mapi", "Get all Kontent.ai assets (paginated)", listAssetsSchema.shape, async ({ continuation_token }, { authInfo: { token, clientId } = {} }) => {
6
+ server.tool("list-assets-mapi", "Get all Kontent.ai assets (paginated). Assets are digital files (images, videos, documents) referenced in content.", listAssetsSchema.shape, async ({ continuation_token }, { authInfo: { token, clientId } = {} }) => {
7
7
  const client = createMapiClient(clientId, token);
8
8
  try {
9
9
  const query = client.listAssets();
@@ -2,7 +2,7 @@ import { createMapiClient } from "../clients/kontentClients.js";
2
2
  import { handleMcpToolError } from "../utils/errorHandler.js";
3
3
  import { createMcpToolSuccessResponse } from "../utils/responseHelper.js";
4
4
  export const registerTool = (server) => {
5
- server.tool("list-collections-mapi", "Get all Kontent.ai collections", {}, async (_, { authInfo: { token, clientId } = {} }) => {
5
+ server.tool("list-collections-mapi", "Get all Kontent.ai collections. Collections organize content items into logical groups by team, brand, or project.", {}, async (_, { authInfo: { token, clientId } = {} }) => {
6
6
  const client = createMapiClient(clientId, token);
7
7
  try {
8
8
  const response = await client.listCollections().toPromise();
@@ -3,7 +3,7 @@ import { listContentTypeSnippetsSchema } from "../schemas/listSchemas.js";
3
3
  import { handleMcpToolError } from "../utils/errorHandler.js";
4
4
  import { createMcpToolSuccessResponse } from "../utils/responseHelper.js";
5
5
  export const registerTool = (server) => {
6
- server.tool("list-content-type-snippets-mapi", "Get all Kontent.ai content type snippets (paginated)", listContentTypeSnippetsSchema.shape, async ({ continuation_token }, { authInfo: { token, clientId } = {} }) => {
6
+ server.tool("list-content-type-snippets-mapi", "Get all Kontent.ai content type snippets (paginated). Snippets are reusable element sets included in content types via snippet element.", listContentTypeSnippetsSchema.shape, async ({ continuation_token }, { authInfo: { token, clientId } = {} }) => {
7
7
  const client = createMapiClient(clientId, token);
8
8
  try {
9
9
  const query = client.listContentTypeSnippets();
@@ -3,7 +3,7 @@ import { listContentTypesSchema } from "../schemas/listSchemas.js";
3
3
  import { handleMcpToolError } from "../utils/errorHandler.js";
4
4
  import { createMcpToolSuccessResponse } from "../utils/responseHelper.js";
5
5
  export const registerTool = (server) => {
6
- server.tool("list-content-types-mapi", "Get all Kontent.ai content types (paginated)", listContentTypesSchema.shape, async ({ continuation_token }, { authInfo: { token, clientId } = {} }) => {
6
+ server.tool("list-content-types-mapi", "Get all Kontent.ai content types (paginated). Types define variant structure: field definitions, validation rules, and element types.", listContentTypesSchema.shape, async ({ continuation_token }, { authInfo: { token, clientId } = {} }) => {
7
7
  const client = createMapiClient(clientId, token);
8
8
  try {
9
9
  const query = client.listContentTypes();
@@ -3,7 +3,7 @@ import { listLanguagesSchema } from "../schemas/listSchemas.js";
3
3
  import { handleMcpToolError } from "../utils/errorHandler.js";
4
4
  import { createMcpToolSuccessResponse } from "../utils/responseHelper.js";
5
5
  export const registerTool = (server) => {
6
- server.tool("list-languages-mapi", "Get all Kontent.ai languages (paginated)", listLanguagesSchema.shape, async ({ continuation_token }, { authInfo: { token, clientId } = {} }) => {
6
+ server.tool("list-languages-mapi", "Get all Kontent.ai languages (paginated). Languages define available locales; each can have fallback language for content inheritance.", listLanguagesSchema.shape, async ({ continuation_token }, { authInfo: { token, clientId } = {} }) => {
7
7
  const client = createMapiClient(clientId, token);
8
8
  try {
9
9
  const query = client.listLanguages();
@@ -3,7 +3,7 @@ import { listTaxonomyGroupsSchema } from "../schemas/listSchemas.js";
3
3
  import { handleMcpToolError } from "../utils/errorHandler.js";
4
4
  import { createMcpToolSuccessResponse } from "../utils/responseHelper.js";
5
5
  export const registerTool = (server) => {
6
- server.tool("list-taxonomy-groups-mapi", "Get all Kontent.ai taxonomy groups (paginated)", listTaxonomyGroupsSchema.shape, async ({ continuation_token }, { authInfo: { token, clientId } = {} }) => {
6
+ server.tool("list-taxonomy-groups-mapi", "Get all Kontent.ai taxonomy groups (paginated). Taxonomies provide hierarchical categorization for organizing and tagging content.", listTaxonomyGroupsSchema.shape, async ({ continuation_token }, { authInfo: { token, clientId } = {} }) => {
7
7
  const client = createMapiClient(clientId, token);
8
8
  try {
9
9
  const query = client.listTaxonomies();
@@ -2,7 +2,7 @@ import { createMapiClient } from "../clients/kontentClients.js";
2
2
  import { handleMcpToolError } from "../utils/errorHandler.js";
3
3
  import { createMcpToolSuccessResponse } from "../utils/responseHelper.js";
4
4
  export const registerTool = (server) => {
5
- server.tool("list-workflows-mapi", "Get all Kontent.ai workflows", {}, async (_, { authInfo: { token, clientId } = {} }) => {
5
+ server.tool("list-workflows-mapi", "Get all Kontent.ai workflows. Workflow states manage content lifecycle: drafting, review, published, archived.", {}, async (_, { authInfo: { token, clientId } = {} }) => {
6
6
  const client = createMapiClient(clientId, token);
7
7
  try {
8
8
  const response = await client.listWorkflows().toPromise();
@@ -3,7 +3,7 @@ import { collectionPatchOperationsSchema } from "../schemas/collectionSchemas.js
3
3
  import { handleMcpToolError } from "../utils/errorHandler.js";
4
4
  import { createMcpToolSuccessResponse } from "../utils/responseHelper.js";
5
5
  export const registerTool = (server) => {
6
- server.tool("patch-collections-mapi", "Update Kontent.ai collections using patch operations (addInto, move, remove, replace)", {
6
+ server.tool("patch-collections-mapi", "Update Kontent.ai collections using patch operations. Call get-patch-guide first for operations reference.", {
7
7
  operations: collectionPatchOperationsSchema.describe("Patch operations array. Call list-collections-mapi first. Use addInto to add new collections, move to reorder, remove to delete empty collections, replace to rename."),
8
8
  }, async ({ operations }, { authInfo: { token, clientId } = {} }) => {
9
9
  const client = createMapiClient(clientId, token);
@@ -4,7 +4,7 @@ import { patchOperationsSchema } from "../schemas/patchSchemas/contentTypePatchS
4
4
  import { handleMcpToolError } from "../utils/errorHandler.js";
5
5
  import { createMcpToolSuccessResponse } from "../utils/responseHelper.js";
6
6
  export const registerTool = (server) => {
7
- server.tool("patch-content-type-mapi", "Update Kontent.ai content type using JSON Patch (move, addInto, remove, replace)", {
7
+ server.tool("patch-content-type-mapi", "Update Kontent.ai content type using JSON Patch. Call get-patch-guide first for operations reference.", {
8
8
  codename: z.string().describe("Content type codename"),
9
9
  operations: patchOperationsSchema.describe("Patch operations array. CRITICAL: Always call get-type-mapi first. Use addInto/remove for arrays, replace for primitives/objects. See context for details."),
10
10
  }, async ({ codename, operations }, { authInfo: { token, clientId } = {} }) => {
@@ -3,7 +3,7 @@ import { patchLanguageSchema } from "../schemas/languageSchemas.js";
3
3
  import { handleMcpToolError } from "../utils/errorHandler.js";
4
4
  import { createMcpToolSuccessResponse } from "../utils/responseHelper.js";
5
5
  export const registerTool = (server) => {
6
- server.tool("patch-language-mapi", "Update Kontent.ai language using replace operations via Management API. Only active languages can be modified - if deactivated, is_active: true must be first operation.", patchLanguageSchema.shape, async ({ languageId, operations }, { authInfo: { token, clientId } = {} }) => {
6
+ server.tool("patch-language-mapi", "Update Kontent.ai language using replace operations. Call get-patch-guide first. If deactivated, is_active: true must be first operation.", patchLanguageSchema.shape, async ({ languageId, operations }, { authInfo: { token, clientId } = {} }) => {
7
7
  const client = createMapiClient(clientId, token);
8
8
  try {
9
9
  const response = await client
@@ -3,14 +3,12 @@ import { createMapiClient } from "../clients/kontentClients.js";
3
3
  import { handleMcpToolError } from "../utils/errorHandler.js";
4
4
  import { createMcpToolSuccessResponse } from "../utils/responseHelper.js";
5
5
  export const registerTool = (server) => {
6
- server.tool("publish-variant-mapi", "Publish or schedule Kontent.ai variant", {
7
- itemId: z.string().uuid().describe("Content item UUID"),
6
+ server.tool("publish-variant-mapi", "Publish or schedule Kontent.ai variant. For scheduling, verify current UTC time before using scheduledTo.", {
7
+ itemId: z.uuid().describe("Content item UUID"),
8
8
  languageId: z
9
- .string()
10
9
  .uuid()
11
10
  .describe("Language variant UUID (default: 00000000-0000-0000-0000-000000000000)"),
12
- scheduledTo: z
13
- .string()
11
+ scheduledTo: z.iso
14
12
  .datetime({ offset: true })
15
13
  .optional()
16
14
  .describe("ISO 8601 datetime for scheduled publish (omit for immediate)"),
@@ -3,14 +3,12 @@ import { createMapiClient } from "../clients/kontentClients.js";
3
3
  import { handleMcpToolError } from "../utils/errorHandler.js";
4
4
  import { createMcpToolSuccessResponse } from "../utils/responseHelper.js";
5
5
  export const registerTool = (server) => {
6
- server.tool("unpublish-variant-mapi", "Unpublish or schedule unpublishing of Kontent.ai variant", {
7
- itemId: z.string().uuid().describe("Content item UUID"),
6
+ server.tool("unpublish-variant-mapi", "Unpublish or schedule unpublishing Kontent.ai variant. For scheduling, verify current UTC time before using scheduledTo.", {
7
+ itemId: z.uuid().describe("Content item UUID"),
8
8
  languageId: z
9
- .string()
10
9
  .uuid()
11
10
  .describe("Language variant UUID (default: 00000000-0000-0000-0000-000000000000)"),
12
- scheduledTo: z
13
- .string()
11
+ scheduledTo: z.iso
14
12
  .datetime({ offset: true })
15
13
  .optional()
16
14
  .describe("ISO 8601 datetime for scheduled unpublish (omit for immediate)"),
@@ -4,7 +4,7 @@ import { languageVariantElementSchema } from "../schemas/contentItemSchemas.js";
4
4
  import { handleMcpToolError } from "../utils/errorHandler.js";
5
5
  import { createVariantMcpToolSuccessResponse } from "../utils/responseHelper.js";
6
6
  export const registerTool = (server) => {
7
- server.tool("upsert-language-variant-mapi", "Create or update Kontent.ai variant", {
7
+ server.tool("upsert-language-variant-mapi", "Create or update Kontent.ai variant. Element values must fulfill limitations and guidelines defined in content type.", {
8
8
  itemId: z.string().describe("Content item ID"),
9
9
  languageId: z
10
10
  .string()
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@kontent-ai/mcp-server",
3
- "version": "0.22.1",
3
+ "version": "0.23.1",
4
4
  "type": "module",
5
5
  "mcpName": "io.github.kontent-ai/mcp-server",
6
6
  "repository": {
@@ -30,22 +30,22 @@
30
30
  "license": "MIT",
31
31
  "dependencies": {
32
32
  "@kontent-ai/management-sdk": "^8.1.0",
33
- "@modelcontextprotocol/sdk": "^1.12.0",
34
- "applicationinsights": "^2.9.8",
35
- "dotenv": "^16.5.0",
36
- "express": "^5.1.0",
37
- "p-retry": "^7.0.0",
38
- "zod": "^3.25.30"
33
+ "@modelcontextprotocol/sdk": "^1.24.3",
34
+ "applicationinsights": "^3.12.1",
35
+ "dotenv": "^17.2.3",
36
+ "express": "^5.2.1",
37
+ "p-retry": "^7.1.1",
38
+ "zod": "^4.1.13"
39
39
  },
40
40
  "devDependencies": {
41
- "@biomejs/biome": "^2.0.5",
42
- "@types/express": "^5.0.2",
41
+ "@biomejs/biome": "^2.3.8",
42
+ "@types/express": "^5.0.6",
43
43
  "@types/mocha": "^10.0.10",
44
- "@types/node": "^22.15.19",
45
- "cross-env": "^7.0.3",
44
+ "@types/node": "^24.10.2",
45
+ "cross-env": "^10.1.0",
46
46
  "mocha": "^11.7.5",
47
- "rimraf": "^6.0.1",
48
- "tsx": "^4.20.3",
49
- "typescript": "^5.8.3"
47
+ "rimraf": "^6.1.2",
48
+ "tsx": "^4.21.0",
49
+ "typescript": "^5.9.3"
50
50
  }
51
51
  }
@@ -1,201 +0,0 @@
1
- export const initialContext = `
2
- # Kontent.ai Platform Guide
3
-
4
- Kontent.ai is a headless content management system (CMS) that provides two main APIs: the Management API for creating, updating, and deleting content and structure, and the Delivery API for retrieving content for applications.
5
-
6
- ## Core Entities
7
-
8
- ### Content Items
9
- Content items are language-neutral content containers that serve as the foundation for your content structure. Each content item has a unique internal ID and codename, and references a specific content type. The key relationship to understand is that one content item can have multiple language variants.
10
-
11
- ### Language Variants
12
- Language variants contain the actual language-specific content data including field values, workflow state, and language reference. Importantly, each variant is managed independently per language.
13
-
14
- ### Content Types
15
- Content types define the structure and blueprint for language variants. They specify field definitions, validation rules, and element types that language variants will use. Think of them as templates that determine what fields and data types your language variants will have.
16
-
17
- ### Content Type Snippets
18
- Content type snippets are reusable field groups that promote consistency across multiple content types. Following the DRY principle (define once, use everywhere), one snippet can be used across multiple content types. This prevents duplication and ensures consistency when you need the same fields across different content types.
19
-
20
- ### Collections
21
- Collections organize content items into logical groups by team, brand, or project. Each content item belongs to exactly one collection.
22
-
23
- ### Languages
24
- Languages define available locales for content. Each language can have a fallback language for content inheritance and can be activated or deactivated.
25
-
26
- ## Understanding Key Relationships
27
-
28
- The content structure flows from Content Type → Content Item → Language Variant(s). For reusability, Content Type Snippets can be included in multiple Content Types. For localization, each Content Item can have one Language Variant per language.
29
-
30
- ## Working with Content Types Containing Snippets
31
-
32
- **CRITICAL**: When creating language variants for content types with snippets, you must:
33
-
34
- 1. **Read the content type** to identify snippet elements (type: "snippet")
35
- 2. **Retrieve each snippet definition** to understand its internal elements
36
- 3. **Include ALL elements** from both the content type AND all snippets in the language variant
37
-
38
- **SNIPPET ELEMENT IMPLEMENTATION DETAILS**:
39
-
40
- When implementing snippet elements in language variants, follow these rules:
41
- - **Element Reference Format**: Use {"element": {"id": "internal_id_here"}} format with internal IDs
42
- - **Codename Convention**: Snippet elements use double underscore format: {snippet_codename}__{element_codename}
43
- - **Example**: For a "metadata" snippet with a "title" element, the codename becomes metadata__title
44
-
45
- **CRITICAL**: Always use internal IDs from snippet definitions, never construct codenames manually.
46
-
47
- **Complete Example**:
48
- Content type has: title, body (direct) + metadata snippet (meta_title, meta_description)
49
- ALL FOUR elements must be included in language variant using their internal IDs:
50
- - Direct element: title ("title_internal_id_here")
51
- - Direct element: body ("body_internal_id_here")
52
- - Snippet element: metadata title ("metadata_title_internal_id_here")
53
- - Snippet element: metadata description ("metadata_description_internal_id_here")
54
-
55
- **Failure to include snippet elements or using incorrect references will result in incomplete content creation.**
56
-
57
- ## Working with Content Types Containing Taxonomy Groups
58
-
59
- **CRITICAL**: When creating language variants for content types with taxonomy elements, you must:
60
-
61
- 1. **Read the content type** to identify ALL taxonomy elements (type: "taxonomy")
62
- 2. **Retrieve EACH taxonomy group definition** to understand the available terms and their hierarchical structure
63
- 3. **Fill ALL taxonomy elements** in the language variant - DO NOT leave any taxonomy elements empty
64
- 4. **Use appropriate term internal IDs** when filling taxonomy elements based on the content being created
65
-
66
- **MANDATORY REQUIREMENT**: Every taxonomy element in the content type MUST be filled with at least one appropriate term when creating language variants. Empty taxonomy elements are not acceptable and indicate incomplete content creation.
67
-
68
- **Example Process**: If a content type has three taxonomy elements:
69
- - "article_type" (required) → Must select appropriate type using its internal ID
70
- - "topics" (optional but must be filled) → Must select relevant topics using their internal IDs
71
- - "medical_specialties" (optional but must be filled) → Must select relevant specialties using their internal IDs
72
-
73
- **Failure to fill ALL taxonomy elements will result in incomplete content creation and poor content organization.**
74
-
75
- ## Operational Patterns
76
-
77
- ### Content Creation Workflow
78
- 1. Define content types (structure) - Create the blueprint for your content
79
- 2. Create content items (containers) - Establish the content containers
80
- 3. Add language variants (actual content) - Fill in the language-specific content
81
- 4. Publish variants (make live) - Make content available through the Delivery API
82
-
83
- ### Content Management Workflow
84
- - Update language variants with new content or changes
85
- - Manage workflow states as content progresses through its lifecycle
86
- - Create new language variants when expanding to additional languages
87
- - Organize with taxonomies for better content categorization
88
-
89
- ### Workflow Step Management
90
-
91
- **CRITICAL**: When changing workflow steps of language variants, you must:
92
-
93
- 1. **Provide the workflowId parameter** - This is mandatory for all workflow step changes
94
- 2. **Use internal IDs** for itemId, languageId, and workflowStepId parameters
95
- 3. **Ensure the target workflow step exists** in the specified workflow
96
-
97
- **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.
98
-
99
- **Example**: To move a language variant to a "review" step:
100
- - itemId: "content_item_internal_id"
101
- - languageId: "language_internal_id"
102
- - workflowStepId: "review_step_internal_id"
103
- - workflowId: "workflow_internal_id" (MANDATORY)
104
-
105
- **Failure to provide the workflowId parameter will result in workflow step change failures.**
106
-
107
-
108
- ## Essential Concepts
109
-
110
- **Taxonomies** provide hierarchical content categorization, allowing you to organize and tag content systematically.
111
-
112
- **Assets** are digital files including images, videos, and documents that can be referenced throughout your content.
113
-
114
- **Workflow states** manage the content lifecycle, tracking whether content is being drafted, is live, or has been archived.
115
-
116
- **Internal IDs** are unique identifiers that provide fast and reliable access to content entities. **ALWAYS use internal IDs when working with MCP tools for better performance and reliability.**
117
-
118
- **Codenames** are human-readable unique identifiers that provide a consistent way to reference content programmatically, but should be used primarily for readability and debugging.
119
-
120
- ## Best Practices
121
-
122
- **CRITICAL**: Never assume the current time. Always obtain the current date and time when needed for time-sensitive operations like scheduling. If the current date and time in UTC format cannot be decisively obtained by any available tool, force the user to specify the current date and time explicitly.
123
-
124
- Use snippets for common field groups to maintain consistency and avoid duplication. Plan your content types before creating content to ensure proper structure. **Always use internal IDs when working with MCP tools** for optimal performance and reliability. Leverage taxonomies for organization to create logical content hierarchies. Consider your multilingual strategy early in the planning process to avoid restructuring later.
125
-
126
- When working with snippets, always retrieve and understand the complete element structure before creating content variants.
127
-
128
- When working with taxonomy elements, always retrieve and understand the taxonomy group structure and available terms before creating content variants. **NEVER leave taxonomy elements empty - all taxonomy elements must be properly categorized using internal IDs.**
129
-
130
- ## MCP Tool Usage Guidelines
131
-
132
- ### ID Reference Preferences
133
-
134
- **CRITICAL**: When using MCP tools, always prefer internal IDs over codenames:
135
-
136
- - **Content Items**: Use internal IDs to reference content items
137
- - **Language Variants**: Use internal IDs for both item and language references
138
- - **Content Types**: Use internal IDs to reference content types
139
- - **Taxonomy Terms**: Use internal IDs when referencing taxonomy terms
140
- - **Assets**: Use internal IDs when referencing assets
141
- - **Workflow Steps**: Use internal IDs for workflow step references
142
-
143
- **Why Internal IDs?** Internal IDs provide:
144
- - Better performance (faster lookups)
145
- - Immutability (won't change if names change)
146
- - Reliability (consistent across environments)
147
- - API efficiency (direct database lookups)
148
-
149
- **When to use codenames?** Codenames are useful for:
150
- - Human readability during development
151
- - Debugging and logging
152
- - Initial content setup when IDs are not yet known
153
-
154
- All MCP tools have been optimized to work with internal IDs for maximum efficiency.
155
-
156
- ### Content Search Tools
157
-
158
- **CRITICAL DISTINCTION**:
159
- - **search-variants-mapi**: Finds content WHERE THE MAIN TOPIC matches a concept (e.g., "articles about wildlife")
160
- - **filter-variants-mapi**: Finds SPECIFIC WORDS anywhere in content, regardless of topic (e.g., brand compliance, detecting prohibited terms in any content)
161
-
162
- See each tool's description for detailed usage guidelines.
163
-
164
- ## Response Optimization
165
-
166
- **IMPORTANT**: Empty values are automatically removed from ALL entity responses (content items, variants, content types, assets, taxonomies) to reduce token usage.
167
-
168
- ### Empty Element Patterns in Variants
169
-
170
- Elements with these values are removed entirely from variant responses:
171
-
172
- | Element Type | Empty Value | Removed Form |
173
- |--------------|------------|--------------|
174
- | text | "" | {"element": {"id": "uuid"}, "value": ""} |
175
- | rich_text | "<p><br/></p>" | {"element": {"id": "uuid"}, "value": "<p><br/></p>", "components": []} |
176
- | number | null | {"element": {"id": "uuid"}, "value": null} |
177
- | date_time | null | {"element": {"id": "uuid"}, "value": null, "display_timezone": null} |
178
- | asset, modular_content, subpages, multiple_choice, taxonomy | [] | {"element": {"id": "uuid"}, "value": []} |
179
- | url_slug | "" + autogenerated | {"element": {"id": "uuid"}, "mode": "autogenerated", "value": ""} |
180
- | custom | null | {"element": {"id": "uuid"}, "value": null, "searchable_value": null} |
181
-
182
- ### Understanding Missing Properties
183
-
184
- **Missing property = has default/empty value across ALL entities:**
185
-
186
- **Variants:**
187
- - Missing element → See table above for defaults
188
- - Components → Recursively optimized
189
-
190
- **Content Types:**
191
- - content_groups → []
192
- - allowed_content_types → []
193
- - allowed_blocks/formatting → []
194
- - validation_regex → not active
195
- - default values → null/""
196
-
197
- **All Entities:**
198
- - Empty arrays → []
199
- - Empty objects → {}
200
- - Null values → removed
201
- - Empty strings → ""`;
@@ -1,13 +0,0 @@
1
- import { createMcpToolSuccessResponse } from "../utils/responseHelper.js";
2
- import { initialContext } from "./context/initial-context.js";
3
- import { patchOperationsGuide } from "./context/patch-operations-guide.js";
4
- export const registerTool = (server) => {
5
- server.tool("get-initial-context", "🚨 MANDATORY FIRST STEP: This tool MUST be called before using ANY other tools. It provides essential context, configuration, and operational guidelines for Kontent.ai. If you have not called this tool, do so immediately before proceeding with any other operation.", {}, async () => {
6
- try {
7
- return createMcpToolSuccessResponse(`${initialContext}\n\n${patchOperationsGuide}`);
8
- }
9
- catch (error) {
10
- throw new Error(`Failed to read initial context: ${error instanceof Error ? error.message : "Unknown error"}`);
11
- }
12
- });
13
- };