@kontent-ai/mcp-server 0.21.8 → 0.21.10

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 (39) hide show
  1. package/README.md +1 -1
  2. package/build/bin.js +7 -0
  3. package/build/schemas/contentTypeSchemas.js +24 -61
  4. package/build/schemas/filterVariantSchemas.js +2 -0
  5. package/build/schemas/listSchemas.js +26 -0
  6. package/build/schemas/patchSchemas/contentTypePatchSchemas.js +12 -64
  7. package/build/schemas/referenceObjectSchema.js +3 -5
  8. package/build/schemas/taxonomySchemas.js +4 -9
  9. package/build/tools/add-content-item-mapi.js +6 -13
  10. package/build/tools/add-content-type-mapi.js +6 -11
  11. package/build/tools/add-content-type-snippet-mapi.js +5 -8
  12. package/build/tools/add-taxonomy-group-mapi.js +1 -1
  13. package/build/tools/change-variant-workflow-step-mapi.js +5 -14
  14. package/build/tools/context/patch-operations-guide.js +58 -0
  15. package/build/tools/create-variant-version-mapi.js +3 -6
  16. package/build/tools/delete-content-item-mapi.js +2 -2
  17. package/build/tools/delete-content-type-mapi.js +2 -2
  18. package/build/tools/delete-language-variant-mapi.js +3 -5
  19. package/build/tools/filter-variants-mapi.js +12 -19
  20. package/build/tools/get-asset-mapi.js +2 -2
  21. package/build/tools/get-initial-context.js +2 -1
  22. package/build/tools/get-item-mapi.js +2 -2
  23. package/build/tools/get-taxonomy-group-mapi.js +2 -2
  24. package/build/tools/get-type-mapi.js +2 -2
  25. package/build/tools/get-type-snippet-mapi.js +2 -2
  26. package/build/tools/get-variant-mapi.js +3 -5
  27. package/build/tools/list-assets-mapi.js +12 -4
  28. package/build/tools/list-content-type-snippets-mapi.js +12 -4
  29. package/build/tools/list-content-types-mapi.js +12 -4
  30. package/build/tools/list-languages-mapi.js +12 -4
  31. package/build/tools/list-taxonomy-groups-mapi.js +12 -4
  32. package/build/tools/list-workflows-mapi.js +1 -1
  33. package/build/tools/patch-content-type-mapi.js +3 -50
  34. package/build/tools/publish-variant-mapi.js +5 -8
  35. package/build/tools/search-variants-mapi.js +1 -27
  36. package/build/tools/unpublish-variant-mapi.js +5 -8
  37. package/build/tools/update-content-item-mapi.js +4 -4
  38. package/build/tools/upsert-language-variant-mapi.js +5 -8
  39. package/package.json +2 -1
package/README.md CHANGED
@@ -95,7 +95,7 @@ npx @kontent-ai/mcp-server@latest shttp
95
95
  * **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
96
96
  * **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
97
97
  * **delete-language-variant-mapi** – Delete Kontent.ai language variant from Management API
98
- * **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
98
+ * **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
99
99
  * **search-variants-mapi** – AI-powered semantic search for finding content by meaning and concepts in a specific language variant. Use for: conceptual searches when you don't know exact keywords. Limited filtering options (variant ID only)
100
100
 
101
101
  ### Asset Management
package/build/bin.js CHANGED
@@ -130,6 +130,13 @@ async function startStreamableHTTP() {
130
130
  id: null,
131
131
  }));
132
132
  });
133
+ app.get("/health", (_, res) => {
134
+ res.json({
135
+ status: "ok",
136
+ timestamp: new Date().toISOString(),
137
+ currentVersion: version,
138
+ });
139
+ });
133
140
  app.use((err, _req, _res, next) => {
134
141
  trackException(err, "Express Error Handler");
135
142
  next(err);
@@ -1,11 +1,9 @@
1
1
  import { z } from "zod";
2
- // Define a reusable reference object schema
3
- export const referenceObjectSchema = z
4
- .object({
2
+ // Reference by id or codename (id preferred)
3
+ export const referenceObjectSchema = z.object({
5
4
  id: z.string().optional(),
6
5
  codename: z.string().optional(),
7
- })
8
- .describe("An object with an id or codename property referencing another item. Using id is preferred for better performance.");
6
+ });
9
7
  // Common property schemas
10
8
  const baseElementSchema = {
11
9
  codename: z.string().optional(),
@@ -13,9 +11,7 @@ const baseElementSchema = {
13
11
  };
14
12
  // Content group schema for content type elements only
15
13
  const contentGroupElementSchema = {
16
- content_group: referenceObjectSchema
17
- .describe("An object with an id or codename property referencing a content group.")
18
- .optional(),
14
+ content_group: referenceObjectSchema.optional(),
19
15
  };
20
16
  const namedElementSchema = {
21
17
  ...baseElementSchema,
@@ -92,17 +88,14 @@ const assetElementSchema = {
92
88
  maximum_file_size: z.number().optional(),
93
89
  allowed_file_types: z.enum(["adjustable", "any"]).optional(),
94
90
  ...imageLimitSchema,
95
- default: arrayDefaultSchema.describe("Default value of the asset element. Reference an existing asset by its id or codename."),
91
+ default: arrayDefaultSchema,
96
92
  };
97
93
  const customElementSchema = {
98
94
  type: z.literal("custom"),
99
95
  ...namedElementSchema,
100
96
  source_url: z.string(),
101
97
  json_parameters: z.string().optional(),
102
- allowed_elements: z
103
- .array(referenceObjectSchema.describe("An object with an id or codename property referencing an element."))
104
- .optional()
105
- .describe("Specifies which elements from the content type can be used within this custom element."),
98
+ allowed_elements: z.array(referenceObjectSchema).optional(),
106
99
  };
107
100
  const dateTimeElementSchema = {
108
101
  type: z.literal("date_time"),
@@ -112,18 +105,14 @@ const dateTimeElementSchema = {
112
105
  const guidelinesElementSchema = {
113
106
  type: z.literal("guidelines"),
114
107
  ...baseElementSchema,
115
- guidelines: z
116
- .string()
117
- .describe("Value of the guidelines element. This is rich text and can include HTML formatting. Check the documentation here https://kontent.ai/learn/docs/apis/openapi/management-api-v2/#section/HTML5-elements-allowed-in-rich-text for the supported format, but keep in mind that content items and components are not supported in guidelines. Use empty `<p>` tag for empty guidelines."),
108
+ guidelines: z.string(),
118
109
  };
119
110
  const modularContentElementSchema = {
120
111
  type: z.literal("modular_content"),
121
112
  ...namedElementSchema,
122
- allowed_content_types: z
123
- .array(referenceObjectSchema.describe("An object with an id or codename property referencing a content type. Use an empty array to allow all content types."))
124
- .optional(),
113
+ allowed_content_types: z.array(referenceObjectSchema).optional(),
125
114
  item_count_limit: countLimitSchema,
126
- default: arrayDefaultSchema.describe("Default value of the modular content element. Reference an existing content item by its id or codename."),
115
+ default: arrayDefaultSchema,
127
116
  };
128
117
  const subpagesElementSchema = {
129
118
  type: z.literal("subpages"),
@@ -143,7 +132,7 @@ const multipleChoiceElementSchema = {
143
132
  ...namedElementSchema,
144
133
  mode: z.enum(["single", "multiple"]),
145
134
  options: z.array(optionSchema),
146
- default: arrayDefaultSchema.describe("Default value of the multiple choice element. Reference one of the options by its codename."),
135
+ default: arrayDefaultSchema,
147
136
  };
148
137
  const numberElementSchema = {
149
138
  type: z.literal("number"),
@@ -200,38 +189,14 @@ export const allowedTableTextBlockSchema = z.enum([
200
189
  const richTextElementSchema = {
201
190
  type: z.literal("rich_text"),
202
191
  ...namedElementSchema,
203
- allowed_blocks: z
204
- .array(allowedBlockSchema)
205
- .optional()
206
- .describe("Specifies allowed blocks. Use an empty array to allow all options."),
207
- allowed_formatting: z
208
- .array(allowedFormattingSchema)
209
- .optional()
210
- .describe("Specifies allowed formatting options. Use an empty array to allow all options."),
211
- allowed_text_blocks: z
212
- .array(allowedTextBlockSchema)
213
- .optional()
214
- .describe("Specifies allowed text blocks. Use an empty array to allow all options."),
215
- allowed_table_blocks: z
216
- .array(allowedTableBlockSchema)
217
- .optional()
218
- .describe("Specifies allowed table blocks. Use an empty array to allow all options."),
219
- allowed_table_formatting: z
220
- .array(allowedTableFormattingSchema)
221
- .optional()
222
- .describe("Specifies allowed table formatting options. Use an empty array to allow all options."),
223
- allowed_table_text_blocks: z
224
- .array(allowedTableTextBlockSchema)
225
- .optional()
226
- .describe("Specifies allowed table text blocks. Use an empty array to allow all options."),
227
- allowed_content_types: z
228
- .array(referenceObjectSchema.describe("An object with an id or codename property referencing a content type."))
229
- .optional()
230
- .describe("Specifies allowed content types. Use an empty array to allow all content types."),
231
- allowed_item_link_types: z
232
- .array(referenceObjectSchema.describe("An object with an id or codename property referencing a content type."))
233
- .optional()
234
- .describe("Specifies allowed item link types. Use an empty array to allow all link types."),
192
+ allowed_blocks: z.array(allowedBlockSchema).optional(),
193
+ allowed_formatting: z.array(allowedFormattingSchema).optional(),
194
+ allowed_text_blocks: z.array(allowedTextBlockSchema).optional(),
195
+ allowed_table_blocks: z.array(allowedTableBlockSchema).optional(),
196
+ allowed_table_formatting: z.array(allowedTableFormattingSchema).optional(),
197
+ allowed_table_text_blocks: z.array(allowedTableTextBlockSchema).optional(),
198
+ allowed_content_types: z.array(referenceObjectSchema).optional(),
199
+ allowed_item_link_types: z.array(referenceObjectSchema).optional(),
235
200
  ...imageLimitSchema,
236
201
  allowed_image_types: z.enum(["adjustable", "any"]).optional(),
237
202
  maximum_image_size: z.number().optional(),
@@ -239,15 +204,15 @@ const richTextElementSchema = {
239
204
  };
240
205
  const snippetElement = {
241
206
  type: z.literal("snippet"),
242
- snippet: referenceObjectSchema.describe("An object with an id or codename property referencing a snippet."),
207
+ snippet: referenceObjectSchema,
243
208
  ...baseElementSchema,
244
209
  };
245
210
  const taxonomyElementSchema = {
246
211
  type: z.literal("taxonomy"),
247
- taxonomy_group: referenceObjectSchema.describe("An object with an id or codename property referencing a taxonomy group."),
212
+ taxonomy_group: referenceObjectSchema,
248
213
  ...namedElementSchema,
249
214
  term_count_limit: countLimitSchema,
250
- default: arrayDefaultSchema.describe("Default value of the taxonomy element. Reference one of the terms from the specified taxonomy group by its codename."),
215
+ default: arrayDefaultSchema,
251
216
  };
252
217
  const textElementSchema = {
253
218
  type: z.literal("text"),
@@ -257,15 +222,13 @@ const textElementSchema = {
257
222
  default: stringDefaultSchema,
258
223
  };
259
224
  export const dependsOnSchema = z.object({
260
- element: referenceObjectSchema.describe("An object with an id or codename property referencing an element."),
261
- snippet: referenceObjectSchema
262
- .describe("An object with an id or codename property referencing a content type snippet.")
263
- .optional(),
225
+ element: referenceObjectSchema,
226
+ snippet: referenceObjectSchema.optional(),
264
227
  });
265
228
  const urlSlugElementSchema = {
266
229
  type: z.literal("url_slug"),
267
230
  ...namedElementSchema,
268
- depends_on: dependsOnSchema.describe("The element the URL slug depends on. If this element is within a snippet, the snippet must also be specified."),
231
+ depends_on: dependsOnSchema,
269
232
  validation_regex: regexValidationSchema,
270
233
  };
271
234
  // Define a union type of all possible element types for content types
@@ -1,4 +1,5 @@
1
1
  import { z } from "zod";
2
+ import { continuationTokenField } from "./listSchemas.js";
2
3
  import { referenceObjectSchema } from "./referenceObjectSchema.js";
3
4
  // UserReferenceDataContract is a union type - either id or email, but not both
4
5
  const userReferenceSchema = z
@@ -79,4 +80,5 @@ export const filterVariantsSchema = z.object({
79
80
  .boolean()
80
81
  .optional()
81
82
  .describe("Whether to include the full content of language variants in the response"),
83
+ continuation_token: continuationTokenField,
82
84
  });
@@ -0,0 +1,26 @@
1
+ import { z } from "zod";
2
+ // Reusable continuation token schema for paginated list operations
3
+ export const continuationTokenField = z
4
+ .string()
5
+ .optional()
6
+ .describe("Continuation token from a previous response to fetch the next page of results. Omit this parameter (or set to null) to fetch the first page. The response will include a continuation_token field - use this value in the next request to fetch the next page. When continuation_token in the response is null, there are no more pages available.");
7
+ // Schema for listing taxonomy groups
8
+ export const listTaxonomyGroupsSchema = z.object({
9
+ continuation_token: continuationTokenField,
10
+ });
11
+ // Schema for listing languages
12
+ export const listLanguagesSchema = z.object({
13
+ continuation_token: continuationTokenField,
14
+ });
15
+ // Schema for listing content types
16
+ export const listContentTypesSchema = z.object({
17
+ continuation_token: continuationTokenField,
18
+ });
19
+ // Schema for listing content type snippets
20
+ export const listContentTypeSnippetsSchema = z.object({
21
+ continuation_token: continuationTokenField,
22
+ });
23
+ // Schema for listing assets
24
+ export const listAssetsSchema = z.object({
25
+ continuation_token: continuationTokenField,
26
+ });
@@ -5,37 +5,15 @@ import { allowedBlockSchema, allowedFormattingSchema, allowedTableBlockSchema, a
5
5
  // Move operation - Move elements within content type
6
6
  const moveOperationSchema = z.object({
7
7
  op: z.literal("move"),
8
- path: z
9
- .string()
10
- .describe(`Identifies the object you want to move using a path reference. The path reference should be in format 'id:{uuid}'. Examples:
11
- • '/elements/id:123e4567-e89b-12d3-a456-426614174000' - Move an element
12
- • '/elements/id:123e4567-e89b-12d3-a456-426614174000/options/id:987fcdeb-51a2-43d1-9f4e-123456789abc' - Move a multiple choice option (first reference is element, second is option)
13
- • '/content_groups/id:456e7890-a12b-34c5-d678-901234567def' - Move a content group`),
14
- before: referenceObjectSchema
15
- .describe("A reference to the object before which you want to move the object. For example, to move an element before an existing element with id:uuid 'text', set before to {codename: 'text'}. The before and after properties are mutually exclusive.")
16
- .optional(),
17
- after: referenceObjectSchema
18
- .describe("A reference to the object after which you want to move the object. For example, to move an element after an existing element with id:uuid 'Text', set after to {codename: 'text'}. The before and after properties are mutually exclusive.")
19
- .optional(),
8
+ path: z.string().describe("Path to object (format: id:{uuid})"),
9
+ before: referenceObjectSchema.optional(),
10
+ after: referenceObjectSchema.optional(),
20
11
  });
21
12
  // AddInto operation - Add new elements to content type
22
13
  const addIntoOperationSchema = z.object({
23
14
  op: z.literal("addInto"),
24
- path: z
25
- .string()
26
- .describe(`JSON Pointer path where to add the item. The path reference should be in format 'id:{uuid}'. Examples:
27
- • '/elements' - Add a new element to the content type
28
- • '/content_groups' - Add a new content group
29
- • '/elements/id:123e4567-e89b-12d3-a456-426614174000/allowed_content_types' - Add allowed content type to rich text or linked items element
30
- • '/elements/id:123e4567-e89b-12d3-a456-426614174000/allowed_elements' - Add allowed element to custom element
31
- • '/elements/id:123e4567-e89b-12d3-a456-426614174000/options' - Add multiple choice option
32
- • '/elements/id:123e4567-e89b-12d3-a456-426614174000/allowed_blocks' - Add block for rich text element
33
- • '/elements/id:123e4567-e89b-12d3-a456-426614174000/allowed_formatting' - Add formatting option for rich text element
34
- (Replace with actual element UUID)
35
-
36
- - CRITICAL: When adding a url slug that references a snippet element, first add the snippet element to the content type if it’s not already included.`),
37
- value: z
38
- .union([
15
+ path: z.string().describe("Path where to add item (format: id:{uuid})"),
16
+ value: z.union([
39
17
  elementSchema,
40
18
  optionSchema,
41
19
  contentGroupSchema,
@@ -51,44 +29,18 @@ const addIntoOperationSchema = z.object({
51
29
  z.boolean(),
52
30
  z.null(),
53
31
  z.any(),
54
- ])
55
- .describe("The item to add (element, content group, option, etc.)"),
32
+ ]),
56
33
  });
57
34
  // Remove operation - Remove elements from content type
58
35
  const removeOperationSchema = z.object({
59
36
  op: z.literal("remove"),
60
- path: z
61
- .string()
62
- .describe(`JSON Pointer path to the item being removed. The path reference should be in format 'id:{uuid}'. Examples:
63
- • '/elements/id:123e4567-e89b-12d3-a456-426614174000' - Remove an element
64
- • '/elements/id:123e4567-e89b-12d3-a456-426614174000/allowed_content_types/id:987fcdeb-51a2-43d1-9f4e-123456789abc' - Remove allowed content type from rich text/linked items element
65
- • '/elements/id:123e4567-e89b-12d3-a456-426614174000/allowed_element/id:456e7890-a12b-34c5-d678-901234567def' - Remove allowed element from custom element
66
- • '/elements/id:123e4567-e89b-12d3-a456-426614174000/options/id:321dcba9-87f6-54e3-21b0-fedcba987654' - Remove multiple choice option
67
- • '/content_groups/id:456e7890-a12b-34c5-d678-901234567def' - Remove content group (removes all elements within the group)
68
- • '/elements/id:123e4567-e89b-12d3-a456-426614174000/allowed_blocks/images' - Remove rich-text element limitation (where {block} is the limitation type)
69
- (Replace with actual UUIDs)`),
37
+ path: z.string().describe("Path to item to remove (format: id:{uuid})"),
70
38
  });
71
39
  // Replace operation - Replace/update existing elements in content type
72
40
  const replaceOperationSchema = z.object({
73
41
  op: z.literal("replace"),
74
- path: z
75
- .string()
76
- .describe(`JSON Pointer path to the item or property being replaced. The path reference should be in format 'id:{uuid}' Examples:
77
- • '/name' - Change the content type's name
78
- • '/codename' - Change the content type's codename
79
- • '/content_groups/id:456e7890-a12b-34c5-d678-901234567def/name' - Change the name of a content group
80
- • '/elements/id:123e4567-e89b-12d3-a456-426614174000/name' - Change an element property (property depends on element type)
81
- • '/elements/id:123e4567-e89b-12d3-a456-426614174000/options/id:321dcba9-87f6-54e3-21b0-fedcba987654/name' - Change multiple choice option property (name or codename)
82
- (Replace with actual element/group UUIDs)
83
-
84
- REPLACE OPERATION RULES:
85
- • CAN modify: Most element properties based on element type (name, guidelines, validation, etc.)
86
- • CANNOT modify: external_id, id, or type of elements
87
- • CANNOT replace individual object values - must replace the entire object at once
88
- • FOR rich text elements: CANNOT replace individual items in allowed_blocks, allowed_formatting, allowed_text_blocks, allowed_table_blocks, allowed_table_formatting, allowed_table_text_blocks, allowed_content_types, allowed_item_link_types arrays - use addInto/remove operations instead for individual items
89
- • FOR multiple choice options: Can modify name and codename properties`),
90
- value: z
91
- .union([
42
+ path: z.string().describe("Path to property to replace (format: id:{uuid})"),
43
+ value: z.union([
92
44
  dependsOnSchema,
93
45
  regexValidationSchema,
94
46
  textLengthLimitSchema,
@@ -100,9 +52,8 @@ const replaceOperationSchema = z.object({
100
52
  z.number(),
101
53
  z.boolean(),
102
54
  z.null(),
103
- z.any(), // in Union zod tries to match from top to down. any if there is something missing so agent dont fail on validation.
104
- ])
105
- .describe("The new value to replace the existing one"),
55
+ z.any(),
56
+ ]),
106
57
  });
107
58
  // Union type for all patch operations
108
59
  export const patchOperationSchema = z.discriminatedUnion("op", [
@@ -112,7 +63,4 @@ export const patchOperationSchema = z.discriminatedUnion("op", [
112
63
  replaceOperationSchema,
113
64
  ]);
114
65
  // Schema for array of patch operations
115
- export const patchOperationsSchema = z
116
- .array(patchOperationSchema)
117
- .min(1)
118
- .describe("Array of patch operations to apply to the content type. Must contain at least one operation.");
66
+ export const patchOperationsSchema = z.array(patchOperationSchema).min(1);
@@ -1,9 +1,7 @@
1
1
  import z from "zod";
2
- // Define a reusable reference object schema
3
- export const referenceObjectSchema = z
4
- .object({
2
+ // Reference by id, codename, or external_id (id preferred)
3
+ export const referenceObjectSchema = z.object({
5
4
  id: z.string().optional(),
6
5
  codename: z.string().optional(),
7
6
  external_id: z.string().optional(),
8
- })
9
- .describe("An object with an id, codename, or external_id property referencing another item. Using id is preferred for better performance.");
7
+ });
@@ -8,16 +8,11 @@ const taxonomyTermSchema = z.object({
8
8
  });
9
9
  // Schema for a taxonomy group
10
10
  export const taxonomyGroupSchemas = {
11
- name: z.string().describe("Display name of the taxonomy group"),
11
+ name: z.string().describe("Taxonomy group name"),
12
12
  codename: z
13
13
  .string()
14
14
  .optional()
15
- .describe("Codename of the taxonomy group (optional, will be generated if not provided)"),
16
- external_id: z
17
- .string()
18
- .optional()
19
- .describe("External ID of the taxonomy group (optional)"),
20
- terms: z
21
- .array(taxonomyTermSchema)
22
- .describe("Hierarchical structure of taxonomy terms"),
15
+ .describe("Codename (auto-generated if omitted)"),
16
+ external_id: z.string().optional().describe("External ID"),
17
+ terms: z.array(taxonomyTermSchema).describe("Taxonomy terms hierarchy"),
23
18
  };
@@ -3,27 +3,20 @@ 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("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.", {
7
- name: z
8
- .string()
9
- .min(1)
10
- .max(200)
11
- .describe("Display name of the content item (1-200 characters)"),
6
+ server.tool("add-content-item-mapi", "Add new Kontent.ai content item (creates structure only, use upsert-language-variant-mapi for content)", {
7
+ name: z.string().min(1).max(200).describe("Item name (1-200 chars)"),
12
8
  type: z
13
9
  .object({
14
10
  id: z.string().optional(),
15
11
  codename: z.string().optional(),
16
12
  external_id: z.string().optional(),
17
13
  })
18
- .describe("Reference to the content type by id, codename, or external_id. At least one property must be specified."),
14
+ .describe("Content type reference"),
19
15
  codename: z
20
16
  .string()
21
17
  .optional()
22
- .describe("Codename of the content item (optional, will be generated from name if not provided)"),
23
- external_id: z
24
- .string()
25
- .optional()
26
- .describe("External ID for the content item (optional, useful for external system integration)"),
18
+ .describe("Codename (auto-generated if omitted)"),
19
+ external_id: z.string().optional().describe("External ID"),
27
20
  collection: z
28
21
  .object({
29
22
  id: z.string().optional(),
@@ -31,7 +24,7 @@ export const registerTool = (server) => {
31
24
  external_id: z.string().optional(),
32
25
  })
33
26
  .optional()
34
- .describe("Reference to a collection by id, codename, or external_id (optional)"),
27
+ .describe("Collection reference"),
35
28
  }, async ({ name, type, codename, external_id, collection }, { authInfo: { token, clientId } = {} }) => {
36
29
  const client = createMapiClient(clientId, token);
37
30
  try {
@@ -4,23 +4,18 @@ import { contentGroupSchema, elementSchema, } from "../schemas/contentTypeSchema
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("add-content-type-mapi", "Add new Kontent.ai content type via Management API", {
8
- name: z.string().describe("Display name of the content type"),
7
+ server.tool("add-content-type-mapi", "Add new Kontent.ai content type", {
8
+ name: z.string().describe("Content type name"),
9
9
  codename: z
10
10
  .string()
11
11
  .optional()
12
- .describe("Codename of the content type (optional, will be generated if not provided)"),
13
- external_id: z
14
- .string()
15
- .optional()
16
- .describe("External ID of the content type (optional)"),
17
- elements: z
18
- .array(elementSchema)
19
- .describe("Array of elements that define the structure of the content type"),
12
+ .describe("Codename (auto-generated if omitted)"),
13
+ external_id: z.string().optional().describe("External ID"),
14
+ elements: z.array(elementSchema).describe("Elements defining structure"),
20
15
  content_groups: z
21
16
  .array(contentGroupSchema)
22
17
  .optional()
23
- .describe("Array of content groups (optional)"),
18
+ .describe("Content groups"),
24
19
  }, async ({ name, codename, external_id, elements, content_groups }, { authInfo: { token, clientId } = {} }) => {
25
20
  const client = createMapiClient(clientId, token);
26
21
  try {
@@ -4,19 +4,16 @@ import { snippetElementSchema } from "../schemas/contentTypeSchemas.js";
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("add-content-type-snippet-mapi", "Add new Kontent.ai content type snippet via Management API", {
8
- name: z.string().describe("Display name of the content type snippet"),
7
+ server.tool("add-content-type-snippet-mapi", "Add new Kontent.ai content type snippet", {
8
+ name: z.string().describe("Snippet name"),
9
9
  codename: z
10
10
  .string()
11
11
  .optional()
12
- .describe("Codename of the content type snippet (optional, will be generated if not provided)"),
13
- external_id: z
14
- .string()
15
- .optional()
16
- .describe("External ID of the content type snippet (optional)"),
12
+ .describe("Codename (auto-generated if omitted)"),
13
+ external_id: z.string().optional().describe("External ID"),
17
14
  elements: z
18
15
  .array(snippetElementSchema)
19
- .describe("Array of elements that define the structure of the content type snippet"),
16
+ .describe("Elements defining structure"),
20
17
  }, async ({ name, codename, external_id, elements }, { authInfo: { token, clientId } = {} }) => {
21
18
  const client = createMapiClient(clientId, token);
22
19
  try {
@@ -3,7 +3,7 @@ import { taxonomyGroupSchemas } from "../schemas/taxonomySchemas.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("add-taxonomy-group-mapi", "Add new Kontent.ai taxonomy group via Management API", taxonomyGroupSchemas, async (taxonomyGroup, { authInfo: { token, clientId } = {} }) => {
6
+ server.tool("add-taxonomy-group-mapi", "Add new Kontent.ai taxonomy group", taxonomyGroupSchemas, async (taxonomyGroup, { authInfo: { token, clientId } = {} }) => {
7
7
  const client = createMapiClient(clientId, token);
8
8
  try {
9
9
  const response = await client
@@ -3,23 +3,14 @@ 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("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"),
6
+ server.tool("change-variant-workflow-step-mapi", "Change Kontent.ai variant workflow step", {
7
+ itemId: z.string().uuid().describe("Content item UUID"),
11
8
  languageId: z
12
9
  .string()
13
10
  .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"),
11
+ .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"),
23
14
  }, async ({ itemId, languageId, workflowId, workflowStepId }, { authInfo: { token, clientId } = {} }) => {
24
15
  const client = createMapiClient(clientId, token);
25
16
  try {
@@ -0,0 +1,58 @@
1
+ export const patchOperationsGuide = `
2
+ # Content Type Patch Operations Guide
3
+
4
+ ## Overview
5
+ Modify content types using RFC 6902 JSON Patch operations: move, addInto, remove, replace.
6
+
7
+ ## Critical Requirements
8
+ - **Always call get-type-mapi first** to get current schema
9
+ - **Use addInto/remove for arrays** (elements, allowed_content_types, allowed_blocks, etc.)
10
+ - **Never use replace for array properties** - use addInto/remove instead
11
+ - **Use replace for primitives/objects** (name, maximum_text_length, validation_regex)
12
+ - **external_id and type cannot be modified** after creation
13
+ - **When adding allowed_formatting/allowed_table_formatting**, 'unstyled' must be first
14
+
15
+ ## Operation Types
16
+
17
+ ### move
18
+ Reorganize elements, options, or content groups using 'before' or 'after' reference.
19
+
20
+ ### addInto
21
+ Add new items to arrays. Use for all array operations (elements, options, allowed_blocks, etc.).
22
+
23
+ ### remove
24
+ Delete items from arrays or remove elements/groups.
25
+
26
+ ### replace
27
+ Update primitives and objects. Cannot modify external_id, id, or type.
28
+
29
+ ## Path Formats
30
+ Use JSON Pointer with id:{uuid} format:
31
+ - Element: /elements/id:{uuid}
32
+ - Element property: /elements/id:{uuid}/name
33
+ - Option: /elements/id:{uuid}/options/id:{uuid}
34
+ - Content group: /content_groups/id:{uuid}
35
+ - Array item: /elements/id:{uuid}/allowed_content_types/id:{uuid}
36
+
37
+ ## Rich Text Properties
38
+ - **allowed_content_types**: Content types for components/linked items (empty=all)
39
+ - **allowed_item_link_types**: Content types for text links (empty=all)
40
+ - **allowed_blocks**: "images", "text", "tables", "components-and-items" (empty=all)
41
+ - **allowed_image_types**: "adjustable" or "any"
42
+ - **allowed_text_blocks**: "paragraph", "heading-one" through "heading-six", "ordered-list", "unordered-list" (empty=all)
43
+ - **allowed_formatting**: "unstyled", "bold", "italic", "code", "link", "subscript", "superscript" (empty=all, unstyled must be first)
44
+ - **allowed_table_blocks**: "images", "text" (empty=both)
45
+ - **allowed_table_text_blocks**: Same as allowed_text_blocks (empty=all)
46
+ - **allowed_table_formatting**: Same as allowed_formatting (empty=all, unstyled must be first)
47
+
48
+ ## Special Techniques
49
+ - **Remove content groups**: Set ALL elements' content_group to null AND remove ALL groups in one request
50
+ - **URL Slug with snippet**: Add snippet element first, then URL slug with depends_on reference
51
+
52
+ ## Best Practices
53
+ - Use descriptive codenames
54
+ - Group related operations in single request
55
+ - Use id:{uuid} format in paths
56
+ - Validate dependencies before adding URL slugs
57
+ - Consider element ordering for move operations
58
+ `;
@@ -3,15 +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("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.", {
7
- itemId: z
8
- .string()
9
- .uuid()
10
- .describe("Internal ID (UUID) of the content item whose language variant you want to create a new version of"),
6
+ server.tool("create-variant-version-mapi", "Create new version of Kontent.ai variant", {
7
+ itemId: z.string().uuid().describe("Content item UUID"),
11
8
  languageId: z
12
9
  .string()
13
10
  .uuid()
14
- .describe("Internal ID (UUID) of the language variant to create a new version of. Use '00000000-0000-0000-0000-000000000000' for the default language"),
11
+ .describe("Language variant UUID (default: 00000000-0000-0000-0000-000000000000)"),
15
12
  }, async ({ itemId, languageId }, { authInfo: { token, clientId } = {} }) => {
16
13
  const client = createMapiClient(clientId, token);
17
14
  try {
@@ -3,8 +3,8 @@ 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("delete-content-item-mapi", "Delete Kontent.ai content item by internal ID from Management API", {
7
- id: z.string().describe("Internal ID of the content item to delete"),
6
+ server.tool("delete-content-item-mapi", "Delete Kontent.ai content item", {
7
+ id: z.string().describe("Item ID"),
8
8
  }, async ({ id }, { authInfo: { token, clientId } = {} }) => {
9
9
  const client = createMapiClient(clientId, token);
10
10
  try {
@@ -3,8 +3,8 @@ 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("delete-content-type-mapi", "Delete a content type by codename from Management API", {
7
- codename: z.string().describe("Codename of the content type to delete"),
6
+ server.tool("delete-content-type-mapi", "Delete Kontent.ai content type by codename", {
7
+ codename: z.string().describe("Content type codename"),
8
8
  }, async ({ codename }, { authInfo: { token, clientId } = {} }) => {
9
9
  const client = createMapiClient(clientId, token);
10
10
  try {
@@ -3,11 +3,9 @@ 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("delete-language-variant-mapi", "Delete Kontent.ai language variant from Management API", {
7
- itemId: z.string().describe("Internal ID of the content item"),
8
- languageId: z
9
- .string()
10
- .describe("Internal ID of the language variant to delete"),
6
+ server.tool("delete-language-variant-mapi", "Delete Kontent.ai variant", {
7
+ itemId: z.string().describe("Item ID"),
8
+ languageId: z.string().describe("Language variant ID"),
11
9
  }, async ({ itemId, languageId }, { authInfo: { token, clientId } = {} }) => {
12
10
  const client = createMapiClient(clientId, token);
13
11
  try {
@@ -4,28 +4,14 @@ import { handleMcpToolError } from "../utils/errorHandler.js";
4
4
  import { createMcpToolSuccessResponse } from "../utils/responseHelper.js";
5
5
  import { throwError } from "../utils/throwError.js";
6
6
  export const registerTool = (server) => {
7
- server.tool("filter-variants-mapi", `Filter Kontent.ai language variants of content items using Management API.
8
-
9
- USE FOR:
10
- - EXACT keyword matching: finding specific words, phrases, names, codes, or IDs anywhere in content, regardless of overall topic
11
- ✓ Example: 'find items containing rabbit' → search 'rabbit'
12
- - Brand guideline compliance: detecting prohibited terms across all content
13
- ✓ Example: Search "hunt beast prey" to find content containing ANY of these terms (natural OR operator)
14
- - CAN expand concepts to keywords when using filter (e.g., "neurology-related" → "neurology neurological brain nervous system")
15
- - Advanced filtering by content type, contributors, workflow steps, taxonomies etc
16
- - Also use as fallback when AI search is unavailable
17
- - Optionally includes full content of variants with include_content parameter
18
-
19
- BEST FOR: Finding needles in haystacks - specific words in otherwise unrelated content. Multiple search terms use OR logic.`, filterVariantsSchema.shape, async ({ search_phrase, content_types, contributors, has_no_contributors, completion_statuses, language, workflow_steps, taxonomy_groups, order_by, order_direction, include_content, }, { authInfo: { token, clientId } = {} }) => {
7
+ server.tool("filter-variants-mapi", "Filter Kontent.ai variants. For EXACT keyword matching and compliance (terms use OR). Use search-variants-mapi for semantic/topic search.", filterVariantsSchema.shape, async ({ search_phrase, content_types, contributors, has_no_contributors, completion_statuses, language, workflow_steps, taxonomy_groups, order_by, order_direction, include_content, continuation_token, }, { authInfo: { token, clientId } = {} }) => {
20
8
  try {
21
9
  const environmentId = clientId ?? process.env.KONTENT_ENVIRONMENT_ID;
22
10
  if (!environmentId) {
23
11
  throwError("Missing required environment ID");
24
12
  }
25
13
  const client = createMapiClient(environmentId, token);
26
- const response = await client.earlyAccess
27
- .filterLanguageVariants()
28
- .withData({
14
+ const query = client.earlyAccess.filterLanguageVariants().withData({
29
15
  filters: {
30
16
  search_phrase,
31
17
  content_types,
@@ -43,9 +29,16 @@ export const registerTool = (server) => {
43
29
  }
44
30
  : undefined,
45
31
  include_content: include_content ?? false,
46
- })
47
- .toAllPromise();
48
- return createMcpToolSuccessResponse(response.responses.flatMap((r) => r.rawData.data));
32
+ });
33
+ const response = await (continuation_token
34
+ ? query.xContinuationToken(continuation_token)
35
+ : query).toPromise();
36
+ return createMcpToolSuccessResponse({
37
+ data: response.rawData.data,
38
+ pagination: {
39
+ continuation_token: response.data.pagination.continuationToken,
40
+ },
41
+ });
49
42
  }
50
43
  catch (error) {
51
44
  return handleMcpToolError(error, "Variant Filter");
@@ -3,8 +3,8 @@ 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 a specific Kontent.ai asset by internal ID from Management API", {
7
- assetId: z.string().describe("Internal ID of the asset to retrieve"),
6
+ server.tool("get-asset-mapi", "Get Kontent.ai asset by ID", {
7
+ assetId: z.string().describe("Asset ID"),
8
8
  }, async ({ assetId }, { authInfo: { token, clientId } = {} }) => {
9
9
  const client = createMapiClient(clientId, token);
10
10
  try {
@@ -1,9 +1,10 @@
1
1
  import { createMcpToolSuccessResponse } from "../utils/responseHelper.js";
2
2
  import { initialContext } from "./context/initial-context.js";
3
+ import { patchOperationsGuide } from "./context/patch-operations-guide.js";
3
4
  export const registerTool = (server) => {
4
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 () => {
5
6
  try {
6
- return createMcpToolSuccessResponse(initialContext);
7
+ return createMcpToolSuccessResponse(`${initialContext}\n\n${patchOperationsGuide}`);
7
8
  }
8
9
  catch (error) {
9
10
  throw new Error(`Failed to read initial context: ${error instanceof Error ? error.message : "Unknown error"}`);
@@ -3,8 +3,8 @@ 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 internal ID from Management API", {
7
- id: z.string().describe("Internal ID of the item to get"),
6
+ server.tool("get-item-mapi", "Get Kontent.ai item by ID", {
7
+ id: z.string().describe("Item ID"),
8
8
  }, async ({ id }, { authInfo: { token, clientId } = {} }) => {
9
9
  const client = createMapiClient(clientId, token);
10
10
  try {
@@ -3,8 +3,8 @@ 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 internal ID from Management API", {
7
- id: z.string().describe("Internal ID of the taxonomy group to get"),
6
+ server.tool("get-taxonomy-group-mapi", "Get Kontent.ai taxonomy group by ID", {
7
+ id: z.string().describe("Taxonomy group ID"),
8
8
  }, async ({ id }, { authInfo: { token, clientId } = {} }) => {
9
9
  const client = createMapiClient(clientId, token);
10
10
  try {
@@ -3,8 +3,8 @@ 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 internal ID from Management API", {
7
- id: z.string().describe("Internal ID of the content type to get"),
6
+ server.tool("get-type-mapi", "Get Kontent.ai content type by ID", {
7
+ id: z.string().describe("Content type ID"),
8
8
  }, async ({ id }, { authInfo: { token, clientId } = {} }) => {
9
9
  const client = createMapiClient(clientId, token);
10
10
  try {
@@ -3,8 +3,8 @@ 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 internal ID from Management API", {
7
- id: z.string().describe("Internal ID of the content type snippet to get"),
6
+ server.tool("get-type-snippet-mapi", "Get Kontent.ai content type snippet by ID", {
7
+ id: z.string().describe("Snippet ID"),
8
8
  }, async ({ id }, { authInfo: { token, clientId } = {} }) => {
9
9
  const client = createMapiClient(clientId, token);
10
10
  try {
@@ -3,11 +3,9 @@ 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-variant-mapi", "Get Kontent.ai language variant of content item from Management API", {
7
- itemId: z.string().describe("Internal ID of the content item"),
8
- languageId: z
9
- .string()
10
- .describe("Internal ID of the language variant to get"),
6
+ server.tool("get-variant-mapi", "Get Kontent.ai variant", {
7
+ itemId: z.string().describe("Item ID"),
8
+ languageId: z.string().describe("Language variant ID"),
11
9
  }, async ({ itemId, languageId }, { authInfo: { token, clientId } = {} }) => {
12
10
  const client = createMapiClient(clientId, token);
13
11
  try {
@@ -1,13 +1,21 @@
1
1
  import { createMapiClient } from "../clients/kontentClients.js";
2
+ import { listAssetsSchema } from "../schemas/listSchemas.js";
2
3
  import { handleMcpToolError } from "../utils/errorHandler.js";
3
4
  import { createMcpToolSuccessResponse } from "../utils/responseHelper.js";
4
5
  export const registerTool = (server) => {
5
- server.tool("list-assets-mapi", "Get all Kontent.ai assets from Management API", {}, async (_, { authInfo: { token, clientId } = {} }) => {
6
+ server.tool("list-assets-mapi", "Get all Kontent.ai assets (paginated)", listAssetsSchema.shape, async ({ continuation_token }, { authInfo: { token, clientId } = {} }) => {
6
7
  const client = createMapiClient(clientId, token);
7
8
  try {
8
- const response = await client.listAssets().toAllPromise();
9
- const rawData = response.responses.flatMap((r) => r.rawData.assets);
10
- return createMcpToolSuccessResponse(rawData);
9
+ const query = client.listAssets();
10
+ const response = await (continuation_token
11
+ ? query.xContinuationToken(continuation_token)
12
+ : query).toPromise();
13
+ return createMcpToolSuccessResponse({
14
+ data: response.rawData.assets,
15
+ pagination: {
16
+ continuation_token: response.data.pagination.continuationToken,
17
+ },
18
+ });
11
19
  }
12
20
  catch (error) {
13
21
  return handleMcpToolError(error, "Assets Listing");
@@ -1,13 +1,21 @@
1
1
  import { createMapiClient } from "../clients/kontentClients.js";
2
+ import { listContentTypeSnippetsSchema } from "../schemas/listSchemas.js";
2
3
  import { handleMcpToolError } from "../utils/errorHandler.js";
3
4
  import { createMcpToolSuccessResponse } from "../utils/responseHelper.js";
4
5
  export const registerTool = (server) => {
5
- server.tool("list-content-type-snippets-mapi", "Get all Kontent.ai content type snippets from Management API", {}, async (_, { authInfo: { token, clientId } = {} }) => {
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
7
  const client = createMapiClient(clientId, token);
7
8
  try {
8
- const response = await client.listContentTypeSnippets().toAllPromise();
9
- const rawData = response.responses.flatMap((r) => r.rawData.snippets);
10
- return createMcpToolSuccessResponse(rawData);
9
+ const query = client.listContentTypeSnippets();
10
+ const response = await (continuation_token
11
+ ? query.xContinuationToken(continuation_token)
12
+ : query).toPromise();
13
+ return createMcpToolSuccessResponse({
14
+ data: response.rawData.snippets,
15
+ pagination: {
16
+ continuation_token: response.data.pagination.continuationToken,
17
+ },
18
+ });
11
19
  }
12
20
  catch (error) {
13
21
  return handleMcpToolError(error, "Content Type Snippets Listing");
@@ -1,13 +1,21 @@
1
1
  import { createMapiClient } from "../clients/kontentClients.js";
2
+ import { listContentTypesSchema } from "../schemas/listSchemas.js";
2
3
  import { handleMcpToolError } from "../utils/errorHandler.js";
3
4
  import { createMcpToolSuccessResponse } from "../utils/responseHelper.js";
4
5
  export const registerTool = (server) => {
5
- server.tool("list-content-types-mapi", "Get all Kontent.ai content types from Management API", {}, async (_, { authInfo: { token, clientId } = {} }) => {
6
+ server.tool("list-content-types-mapi", "Get all Kontent.ai content types (paginated)", listContentTypesSchema.shape, async ({ continuation_token }, { authInfo: { token, clientId } = {} }) => {
6
7
  const client = createMapiClient(clientId, token);
7
8
  try {
8
- const response = await client.listContentTypes().toAllPromise();
9
- const rawData = response.responses.flatMap((r) => r.rawData.types);
10
- return createMcpToolSuccessResponse(rawData);
9
+ const query = client.listContentTypes();
10
+ const response = await (continuation_token
11
+ ? query.xContinuationToken(continuation_token)
12
+ : query).toPromise();
13
+ return createMcpToolSuccessResponse({
14
+ data: response.rawData.types,
15
+ pagination: {
16
+ continuation_token: response.data.pagination.continuationToken,
17
+ },
18
+ });
11
19
  }
12
20
  catch (error) {
13
21
  return handleMcpToolError(error, "Content Types Listing");
@@ -1,13 +1,21 @@
1
1
  import { createMapiClient } from "../clients/kontentClients.js";
2
+ import { listLanguagesSchema } from "../schemas/listSchemas.js";
2
3
  import { handleMcpToolError } from "../utils/errorHandler.js";
3
4
  import { createMcpToolSuccessResponse } from "../utils/responseHelper.js";
4
5
  export const registerTool = (server) => {
5
- server.tool("list-languages-mapi", "Get all Kontent.ai languages from Management API", {}, async (_, { authInfo: { token, clientId } = {} }) => {
6
+ server.tool("list-languages-mapi", "Get all Kontent.ai languages (paginated)", listLanguagesSchema.shape, async ({ continuation_token }, { authInfo: { token, clientId } = {} }) => {
6
7
  const client = createMapiClient(clientId, token);
7
8
  try {
8
- const response = await client.listLanguages().toAllPromise();
9
- const rawData = response.responses.flatMap((r) => r.rawData.languages);
10
- return createMcpToolSuccessResponse(rawData);
9
+ const query = client.listLanguages();
10
+ const response = await (continuation_token
11
+ ? query.xContinuationToken(continuation_token)
12
+ : query).toPromise();
13
+ return createMcpToolSuccessResponse({
14
+ data: response.rawData.languages,
15
+ pagination: {
16
+ continuation_token: response.data.pagination.continuationToken,
17
+ },
18
+ });
11
19
  }
12
20
  catch (error) {
13
21
  return handleMcpToolError(error, "Languages Listing");
@@ -1,13 +1,21 @@
1
1
  import { createMapiClient } from "../clients/kontentClients.js";
2
+ import { listTaxonomyGroupsSchema } from "../schemas/listSchemas.js";
2
3
  import { handleMcpToolError } from "../utils/errorHandler.js";
3
4
  import { createMcpToolSuccessResponse } from "../utils/responseHelper.js";
4
5
  export const registerTool = (server) => {
5
- server.tool("list-taxonomy-groups-mapi", "Get all Kontent.ai taxonomy groups from Management API", {}, async (_, { authInfo: { token, clientId } = {} }) => {
6
+ server.tool("list-taxonomy-groups-mapi", "Get all Kontent.ai taxonomy groups (paginated)", listTaxonomyGroupsSchema.shape, async ({ continuation_token }, { authInfo: { token, clientId } = {} }) => {
6
7
  const client = createMapiClient(clientId, token);
7
8
  try {
8
- const response = await client.listTaxonomies().toAllPromise();
9
- const rawData = response.responses.flatMap((r) => r.rawData.taxonomies);
10
- return createMcpToolSuccessResponse(rawData);
9
+ const query = client.listTaxonomies();
10
+ const response = await (continuation_token
11
+ ? query.xContinuationToken(continuation_token)
12
+ : query).toPromise();
13
+ return createMcpToolSuccessResponse({
14
+ data: response.rawData.taxonomies,
15
+ pagination: {
16
+ continuation_token: response.data.pagination.continuationToken,
17
+ },
18
+ });
11
19
  }
12
20
  catch (error) {
13
21
  return handleMcpToolError(error, "Taxonomy Groups Listing");
@@ -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 from Management API. Workflows define the content lifecycle stages and transitions between them.", {}, async (_, { authInfo: { token, clientId } = {} }) => {
5
+ server.tool("list-workflows-mapi", "Get all Kontent.ai workflows", {}, async (_, { authInfo: { token, clientId } = {} }) => {
6
6
  const client = createMapiClient(clientId, token);
7
7
  try {
8
8
  const response = await client.listWorkflows().toPromise();
@@ -4,56 +4,9 @@ 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 an existing Kontent.ai content type by codename via Management API. Supports move, addInto, remove, and replace operations following RFC 6902 JSON Patch specification.", {
8
- codename: z.string().describe("Codename of the content type to update"),
9
- operations: patchOperationsSchema.describe(`Array of patch operations to apply. Supports: 'move' (reorganize elements), 'addInto' (add new elements), 'remove' (delete elements), 'replace' (update existing elements/properties).
10
-
11
- CRITICAL REQUIREMENTS:
12
- - ALWAYS call get-type-mapi tool before patching to get the latest content type schema
13
- - Use addInto/remove for array operations (adding/removing items from arrays like elements and element's array properties - allowed_content_types, allowed_item_link_types, allowed_blocks, allowed_text_blocks, allowed_formatting, allowed_table_blocks, allowed_table_text_blocks, allowed_table_formatting)
14
- - Never ever use replace for element's array properties - use addInto/remove instead
15
- - Use replace for element's primitive data types and object properties (maximum_text_length, validation_regex, etc.)
16
- - External_id and type cannot be modified after creation
17
- - Patch operations with URL Slug elements referencing snippet elements MUST ensure snippet is present first
18
- - When adding to allowed_formatting or allowed_table_formatting, 'unstyled' must be the first item in the array. If it is not present, then add it as first operation.
19
-
20
- RICH TEXT ELEMENT PROPERTIES:
21
- - allowed_content_types: Array of content type references. Specifies allowed content types for components and linked items. Empty array allows all.
22
- - allowed_item_link_types: Array of content type references. Specifies content types allowed in text links (applies to text and tables). Empty array allows all.
23
- - allowed_blocks: Available options: "images", "text", "tables", "components-and-items". Empty array allows all blocks.
24
- - allowed_image_types: Available options: "adjustable" (only transformable images), "any" (all image files).
25
- - allowed_text_blocks: Available options: "paragraph", "heading-one", "heading-two", "heading-three", "heading-four", "heading-five", "heading-six", "ordered-list", "unordered-list". Empty array allows all.
26
- - allowed_formatting: Available options: "unstyled", "bold", "italic", "code", "link", "subscript", "superscript". "unstyled" must be first if used. Empty array allows all.
27
- - allowed_table_blocks: Available options: "images", "text". Use ["text"] for text-only or empty array for both text and images.
28
- - allowed_table_text_blocks: Available options: "paragraph", "heading-one", "heading-two", "heading-three", "heading-four", "heading-five", "heading-six", "ordered-list", "unordered-list". Empty array allows all.
29
- - allowed_table_formatting: Available options: "unstyled", "bold", "italic", "code", "link", "subscript", "superscript". "unstyled" must be first if used. Empty array allows all.
30
-
31
- OPERATION TYPES:
32
- 1. move: Reorganize elements, options, or content groups. Uses 'before' or 'after' reference.
33
- 2. addInto: Add new elements, options, content groups, or array items. Use for all array operations.
34
- 3. remove: Remove elements, options, content groups, or array items. Use for all array removals.
35
- 4. replace: Update existing properties of primitive data types and objects. Cannot modify external_id, id, or type.
36
-
37
- PATH FORMATS:
38
- - Use JSON Pointer paths with id:{uuid} format for referencing objects
39
- - Element: /elements/id:123e4567-e89b-12d3-a456-426614174000
40
- - Element property: /elements/id:123e4567-e89b-12d3-a456-426614174000/name
41
- - Multiple choice option: /elements/id:123e4567-e89b-12d3-a456-426614174000/options/id:987fcdeb-51a2-43d1-9f4e-123456789abc
42
- - Content group: /content_groups/id:456e7890-a12b-34c5-d678-901234567def
43
- - Rich text array property: /elements/id:123e4567-e89b-12d3-a456-426614174000/allowed_content_types/id:987fcdeb-51a2-43d1-9f4e-123456789abc
44
-
45
- SPECIAL TECHNIQUES:
46
- - To remove content groups while keeping elements: Set ALL elements' content_group to null AND remove ALL content groups in ONE request (atomic operation)
47
- - URL Slug with snippet dependency: First add snippet element, then add URL slug with depends_on reference
48
-
49
- BEST PRACTICES:
50
- - Use descriptive codenames following naming conventions
51
- - Group related operations in a single patch request when possible
52
- - Use proper reference formats (id:{uuid}) in paths
53
- - Validate element dependencies before adding URL slug elements
54
- - Consider element ordering when using move operations
55
- - Use atomic operations for complex changes like removing content groups
56
- - When adding to allowed_formatting or allowed_table_formatting, always ensure 'unstyled' is the first item in the array`),
7
+ server.tool("patch-content-type-mapi", "Update Kontent.ai content type using JSON Patch (move, addInto, remove, replace)", {
8
+ codename: z.string().describe("Content type codename"),
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."),
57
10
  }, async ({ codename, operations }, { authInfo: { token, clientId } = {} }) => {
58
11
  const client = createMapiClient(clientId, token);
59
12
  try {
@@ -3,24 +3,21 @@ 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 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."),
6
+ server.tool("publish-variant-mapi", "Publish or schedule Kontent.ai variant", {
7
+ itemId: z.string().uuid().describe("Content item UUID"),
11
8
  languageId: z
12
9
  .string()
13
10
  .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."),
11
+ .describe("Language variant UUID (default: 00000000-0000-0000-0000-000000000000)"),
15
12
  scheduledTo: z
16
13
  .string()
17
14
  .datetime({ offset: true })
18
15
  .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."),
16
+ .describe("ISO 8601 datetime for scheduled publish (omit for immediate)"),
20
17
  displayTimezone: z
21
18
  .string()
22
19
  .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."),
20
+ .describe("Timezone for UI display (e.g., America/New_York, UTC)"),
24
21
  }, async ({ itemId, languageId, scheduledTo, displayTimezone }, { authInfo: { token, clientId } = {} }) => {
25
22
  const client = createMapiClient(clientId, token);
26
23
  try {
@@ -11,33 +11,7 @@ class OperationResultIncompleteError extends Error {
11
11
  }
12
12
  }
13
13
  export const registerTool = (server) => {
14
- server.tool("search-variants-mapi", `AI-powered semantic search for finding Kontent.ai content by meaning, concepts, themes, and content similarity in a specific language variant. This tool uses vector database and AI to enable searching by meaning and similarity rather than exact keyword matching.
15
-
16
- CRITICAL REQUIREMENTS:
17
- - The AI search feature may not be available for all Kontent.ai environments
18
- - If you receive an "unavailable" status response, DO NOT attempt to use this tool again in the same session
19
- - Use filter-variants-mapi for exact text matching when semantic search is unavailable
20
- - Requires language variant filter parameter (e.g., default language '00000000-0000-0000-0000-000000000000')
21
- - The tool always RETURNS ONLY TOP 50 most relevant items at max
22
- - Limited filtering options (only by variant ID) - use filter-variants-mapi for advanced filtering by content types, workflow steps, taxonomies, etc.
23
-
24
- USE FOR:
25
- - Finding content WHERE THE OVERALL TOPIC/THEME matches a concept
26
- ✓ Example: "fairy tales" → finds articles primarily about fairy tales
27
- ✓ Example: "beverage temperature control" → finds content focused on keeping drinks cold/hot
28
- - Content similarity: Finding articles with similar overall themes
29
- - Thematic content discovery when the main subject matter is what you're looking for
30
-
31
- DO NOT USE FOR:
32
- - Finding specific words scattered in otherwise unrelated content
33
- ✗ Example: Finding "challenge" term in articles about various topics (use filter-variants-mapi)
34
- - Brand guideline violations or prohibited term detection (use filter-variants-mapi)
35
- - Compliance audits looking for specific keywords (use filter-variants-mapi)
36
- - Finding exhaustive and exact number of results (use filter-variants-mapi)
37
-
38
- CRITICAL: This is SEMANTIC search for topic matching. Pass natural language concepts AS-IS. DO NOT generate keyword lists or concatenate multiple keywords.
39
- ✓ CORRECT: "animal predators" or "articles about temperature control"
40
- ✗ WRONG: "animal beast creature wild hunt predator prey attack"`, searchOperationSchema.shape, async ({ searchPhrase, filter }, { authInfo: { token, clientId } = {} }) => {
14
+ server.tool("search-variants-mapi", "AI semantic search for Kontent.ai content by topic/theme (max 50 results). Use filter-variants-mapi for exact keywords. May be unavailable.", searchOperationSchema.shape, async ({ searchPhrase, filter }, { authInfo: { token, clientId } = {} }) => {
41
15
  try {
42
16
  const environmentId = clientId ?? process.env.KONTENT_ENVIRONMENT_ID;
43
17
  if (!environmentId) {
@@ -3,24 +3,21 @@ 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 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."),
6
+ server.tool("unpublish-variant-mapi", "Unpublish or schedule unpublishing of Kontent.ai variant", {
7
+ itemId: z.string().uuid().describe("Content item UUID"),
11
8
  languageId: z
12
9
  .string()
13
10
  .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."),
11
+ .describe("Language variant UUID (default: 00000000-0000-0000-0000-000000000000)"),
15
12
  scheduledTo: z
16
13
  .string()
17
14
  .datetime({ offset: true })
18
15
  .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."),
16
+ .describe("ISO 8601 datetime for scheduled unpublish (omit for immediate)"),
20
17
  displayTimezone: z
21
18
  .string()
22
19
  .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."),
20
+ .describe("Timezone for UI display (e.g., America/New_York, UTC)"),
24
21
  }, async ({ itemId, languageId, scheduledTo, displayTimezone }, { authInfo: { token, clientId } = {} }) => {
25
22
  const client = createMapiClient(clientId, token);
26
23
  try {
@@ -3,14 +3,14 @@ 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("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.", {
7
- id: z.string().describe("Internal ID of the content item to update"),
6
+ server.tool("update-content-item-mapi", "Update Kontent.ai content item", {
7
+ id: z.string().describe("Content item ID"),
8
8
  name: z
9
9
  .string()
10
10
  .min(1)
11
11
  .max(200)
12
12
  .optional()
13
- .describe("New display name of the content item (1-200 characters, optional)"),
13
+ .describe("New name (1-200 chars)"),
14
14
  collection: z
15
15
  .object({
16
16
  id: z.string().optional(),
@@ -18,7 +18,7 @@ export const registerTool = (server) => {
18
18
  external_id: z.string().optional(),
19
19
  })
20
20
  .optional()
21
- .describe("Reference to a collection by id, codename, or external_id (optional)"),
21
+ .describe("Collection reference"),
22
22
  }, async ({ id, name, collection }, { authInfo: { token, clientId } = {} }) => {
23
23
  const client = createMapiClient(clientId, token);
24
24
  try {
@@ -4,18 +4,15 @@ import { languageVariantElementSchema } from "../schemas/contentItemSchemas.js";
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("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.", {
8
- itemId: z.string().describe("Internal ID of the content item"),
7
+ server.tool("upsert-language-variant-mapi", "Create or update Kontent.ai variant", {
8
+ itemId: z.string().describe("Content item ID"),
9
9
  languageId: z
10
10
  .string()
11
- .describe("Internal ID of the language variant (e.g., '00000000-0000-0000-0000-000000000000' for default language)"),
11
+ .describe("Language variant ID (default: 00000000-0000-0000-0000-000000000000)"),
12
12
  elements: z
13
13
  .array(languageVariantElementSchema)
14
- .describe("Array of content elements, each with 'element' (reference object with id/codename/external_id) and 'value' properties. Additional properties may be required depending on element type (e.g., 'mode' for URL slugs)."),
15
- workflow_step_id: z
16
- .string()
17
- .optional()
18
- .describe("Internal ID of the workflow step (optional)"),
14
+ .describe("Content elements array"),
15
+ workflow_step_id: z.string().optional().describe("Workflow step ID"),
19
16
  }, async ({ itemId, languageId, elements, workflow_step_id }, { authInfo: { token, clientId } = {} }) => {
20
17
  const client = createMapiClient(clientId, token);
21
18
  const data = {
package/package.json CHANGED
@@ -1,7 +1,8 @@
1
1
  {
2
2
  "name": "@kontent-ai/mcp-server",
3
- "version": "0.21.8",
3
+ "version": "0.21.10",
4
4
  "type": "module",
5
+ "mcpName": "io.github.kontent-ai/mcp-server",
5
6
  "scripts": {
6
7
  "build": "rimraf build && tsc --project scripts/tsconfig.json && tsc",
7
8
  "start:stdio": "node build/bin.js stdio",