@kontent-ai/mcp-server 0.26.0 → 0.27.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -105,7 +105,8 @@ npx @kontent-ai/mcp-server@latest shttp
105
105
  * **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
106
106
  * **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
107
107
  * **delete-language-variant-mapi** – Delete Kontent.ai language variant from Management API
108
- * **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
108
+ * **filter-variants-mapi** – Filter Kontent.ai items with variants returning references (item ID + language ID). Use for exact keyword matching and finding specific terms in content. Supports full filtering capabilities (content types, workflow steps, taxonomies, spaces, collections, publishing states, etc.). Returns paginated results with continuation token for fetching subsequent pages. Use bulk-get-items-variants-mapi to retrieve full content for matched items
109
+ * **bulk-get-items-variants-mapi** – Bulk get Kontent.ai content items with their language variants by item and language reference pairs. Use after filter-variants-mapi to retrieve full content data for specific item+language pairs. Items without a variant in the requested language return the item without the variant property. Returns paginated results with continuation token
109
110
  * **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)
110
111
 
111
112
  ### Asset Management
@@ -0,0 +1,14 @@
1
+ import { z } from "zod";
2
+ import { continuationTokenField } from "./listSchemas.js";
3
+ import { referenceObjectSchema } from "./referenceObjectSchema.js";
4
+ export const bulkGetItemsWithVariantsSchema = z.object({
5
+ variants: z
6
+ .array(z.object({
7
+ item: referenceObjectSchema.describe("Reference to a content item by its id, codename, or external id"),
8
+ language: referenceObjectSchema.describe("Reference to a language by its id, codename, or external id"),
9
+ }))
10
+ .min(1)
11
+ .max(100)
12
+ .describe("Array of item and language reference pairs to retrieve (max 100). Use filter-variants-mapi to get item and language references first."),
13
+ continuation_token: continuationTokenField,
14
+ });
@@ -1,67 +1,128 @@
1
1
  import { z } from "zod";
2
2
  import { referenceObjectSchema } from "./referenceObjectSchema.js";
3
- // Recursive schema for rich text elements within components
4
- const richTextElementInComponentSchema = z.lazy(() => z.object({
3
+ // Element schemas with descriptions based on Kontent.ai Management API documentation
4
+ const assetInVariantElementSchema = z
5
+ .object({
5
6
  element: referenceObjectSchema,
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({
11
- id: z.string(),
12
- type: referenceObjectSchema,
13
- elements: z.array(richTextElementInComponentSchema),
14
- }));
15
- const assetInVariantElementSchema = z.object({
7
+ value: z
8
+ .array(referenceObjectSchema)
9
+ .nullable()
10
+ .describe("Array of Reference objects, each representing a single asset. Every asset can be referenced only once."),
11
+ })
12
+ .describe("Asset element - references to assets (images, documents). Supports renditions for image-specific editions.");
13
+ const customElementInVariantElementSchema = z
14
+ .object({
16
15
  element: referenceObjectSchema,
17
- value: z.array(referenceObjectSchema).nullable(),
18
- });
19
- const customElementInVariantElementSchema = z.object({
16
+ value: z
17
+ .string()
18
+ .max(200000)
19
+ .nullable()
20
+ .describe("Custom-formatted data depending on the specific custom element implementation."),
21
+ searchable_value: z
22
+ .string()
23
+ .max(200000)
24
+ .nullable()
25
+ .describe("Plain text for search functionality in content item lists."),
26
+ })
27
+ .describe("Custom element - stores custom-formatted data with optional searchable plain text representation.");
28
+ const dateTimeInVariantElementSchema = z
29
+ .object({
20
30
  element: referenceObjectSchema,
21
- value: z.string().nullable(),
22
- searchable_value: z.string().nullable(),
23
- });
24
- const dateTimeInVariantElementSchema = z.object({
31
+ value: z.iso.datetime().nullable(),
32
+ display_timezone: z
33
+ .string()
34
+ .nullable()
35
+ .describe("IANA time zone name affecting UI display without modifying stored value. Defaults to null (local time zone)."),
36
+ })
37
+ .describe("Date & time element - stores date and time values in UTC with optional display timezone.");
38
+ const linkedItemsInVariantElementSchema = z
39
+ .object({
25
40
  element: referenceObjectSchema,
26
- value: z.string().nullable(),
27
- display_timezone: z.string().nullable(),
28
- });
29
- const linkedItemsInVariantElementSchema = z.object({
41
+ value: z
42
+ .array(referenceObjectSchema)
43
+ .nullable()
44
+ .describe("Array of Reference objects. Each reference represents a single content item (each item referenced only once)."),
45
+ })
46
+ .describe("Linked items element - references to other content items for modular content.");
47
+ const multipleChoiceInVariantElementSchema = z
48
+ .object({
30
49
  element: referenceObjectSchema,
31
- value: z.array(referenceObjectSchema).nullable(),
32
- });
33
- const multipleChoiceInVariantElementSchema = z.object({
50
+ value: z
51
+ .array(referenceObjectSchema)
52
+ .nullable()
53
+ .describe("Array of Reference objects. Each reference represents one of the multiple choice options defined in content type."),
54
+ })
55
+ .describe("Multiple choice element - references to selected choice options. Single-option mode requires exactly one reference.");
56
+ const numberInVariantElementSchema = z
57
+ .object({
34
58
  element: referenceObjectSchema,
35
- value: z.array(referenceObjectSchema).nullable(),
36
- });
37
- const numberInVariantElementSchema = z.object({
59
+ value: z.number().nullable().describe("Floating-point number."),
60
+ })
61
+ .describe("Number element - stores numeric floating-point values.");
62
+ const taxonomyInVariantElementSchema = z
63
+ .object({
38
64
  element: referenceObjectSchema,
39
- value: z.number().nullable(),
40
- });
41
- const richTextInVariantElementSchema = z.object({
65
+ value: z
66
+ .array(referenceObjectSchema)
67
+ .nullable()
68
+ .describe("Array of Reference objects. Each reference represents a taxonomy term (each term referenced only once)."),
69
+ })
70
+ .describe("Taxonomy element - references to taxonomy terms from a taxonomy group.");
71
+ const textInVariantElementSchema = z
72
+ .object({
42
73
  element: referenceObjectSchema,
43
- value: z.string().nullable(),
44
- components: z.array(richTextComponentSchema).nullable(),
45
- });
46
- const taxonomyInVariantElementSchema = z.object({
74
+ value: z.string().max(100000).nullable().describe("Plain text content."),
75
+ })
76
+ .describe("Text element - stores plain text content.");
77
+ const urlSlugInVariantElementSchema = z
78
+ .object({
47
79
  element: referenceObjectSchema,
48
- value: z.array(referenceObjectSchema).nullable(),
49
- });
50
- const textInVariantElementSchema = z.object({
80
+ mode: z
81
+ .enum(["autogenerated", "custom"])
82
+ .describe("'autogenerated' (system-generated based on dependent text element) or 'custom' (manual). Switches to custom when value is directly modified."),
83
+ value: z.string().nullable().describe("URL-friendly slug value for SEO."),
84
+ })
85
+ .describe("URL slug element - URL-friendly slug for SEO. Can be auto-generated from dependent text element or custom.");
86
+ // Rich text component schema - uses lazy to handle circular reference
87
+ const richTextComponentSchema = z.lazy(() => z
88
+ .object({
89
+ id: z
90
+ .string()
91
+ .describe("Unique identifier of the component within the rich text element."),
92
+ type: referenceObjectSchema.describe("Reference to the content type defining the component structure."),
93
+ elements: z
94
+ .array(elementInComponentSchema)
95
+ .nullable()
96
+ .describe("Array of element values within the component (supports up to 6 levels deep nesting)."),
97
+ })
98
+ .describe("Component embedded in rich text - a reusable content block defined by a content type."));
99
+ // Rich text element schema - references components which can contain any element type
100
+ const richTextInVariantElementSchema = z
101
+ .object({
51
102
  element: referenceObjectSchema,
52
- value: z.string().nullable(),
53
- });
54
- const urlSlugInVariantElementSchema = z.object({
55
- element: referenceObjectSchema,
56
- mode: z.enum(["autogenerated", "custom"]),
57
- value: z.string().nullable(),
58
- });
103
+ value: z
104
+ .string()
105
+ .max(100000)
106
+ .nullable()
107
+ .describe("Valid HTML5 fragment. Defaults to '<p><br/></p>' if empty."),
108
+ components: z
109
+ .array(richTextComponentSchema)
110
+ .nullable()
111
+ .describe("Array of nested component objects that can be embedded in the rich text content."),
112
+ })
113
+ .describe("Rich text element - formatted HTML content that can include embedded assets, components, content items, and links.");
114
+ // Union schema for elements within components
115
+ // Uses z.lazy() to handle circular reference with richTextComponentSchema
116
+ const elementInComponentSchema = z
117
+ .lazy(() => languageVariantElementSchema)
118
+ .describe("Element value within a component - same structure as top-level variant elements.");
119
+ // Top-level language variant element schema - discriminated by unique fields
59
120
  export const languageVariantElementSchema = z.union([
60
121
  // Most specific schemas first (with unique distinguishing fields)
61
122
  urlSlugInVariantElementSchema, // has unique required 'mode' field
62
- richTextInVariantElementSchema, // has unique optional 'components' field
123
+ richTextInVariantElementSchema, // has unique 'components' field
63
124
  dateTimeInVariantElementSchema, // has unique nullable 'display_timezone' field
64
- customElementInVariantElementSchema, // has unique optional 'searchable_value' field
125
+ customElementInVariantElementSchema, // has unique 'searchable_value' field
65
126
  numberInVariantElementSchema, // has unique value type (number)
66
127
  // Medium specificity (array value types)
67
128
  assetInVariantElementSchema, // value: array of references
@@ -68,6 +68,21 @@ export const filterVariantsSchema = z.object({
68
68
  .min(1)
69
69
  .optional()
70
70
  .describe("Array of taxonomy groups with taxonomy terms"),
71
+ spaces: z
72
+ .array(referenceObjectSchema)
73
+ .min(1)
74
+ .optional()
75
+ .describe("Array of references to spaces by their id or codename (external_id is not supported for spaces)"),
76
+ collections: z
77
+ .array(referenceObjectSchema)
78
+ .min(1)
79
+ .optional()
80
+ .describe("Array of references to collections by their id, codename, or external id"),
81
+ publishing_states: z
82
+ .array(z.enum(["published", "unpublished", "not_published_yet"]))
83
+ .min(1)
84
+ .optional()
85
+ .describe("Array of publishing states to filter by. 'published' - variant is currently published, 'unpublished' - variant was published but is now unpublished, 'not_published_yet' - variant has never been published"),
71
86
  order_by: z
72
87
  .enum(["name", "due_date", "last_modified"])
73
88
  .optional()
@@ -76,9 +91,5 @@ export const filterVariantsSchema = z.object({
76
91
  .enum(["asc", "desc"])
77
92
  .optional()
78
93
  .describe("Order direction"),
79
- include_content: z
80
- .boolean()
81
- .optional()
82
- .describe("Whether to include the full content of language variants in the response"),
83
94
  continuation_token: continuationTokenField,
84
95
  });
package/build/server.js CHANGED
@@ -7,6 +7,7 @@ import { registerTool as registerAddLanguageMapi } from "./tools/add-language-ma
7
7
  import { registerTool as registerAddSpaceMapi } from "./tools/add-space-mapi.js";
8
8
  import { registerTool as registerAddTaxonomyGroupMapi } from "./tools/add-taxonomy-group-mapi.js";
9
9
  import { registerTool as registerAddWorkflowMapi } from "./tools/add-workflow-mapi.js";
10
+ import { registerTool as registerBulkGetItemsVariantsMapi } from "./tools/bulk-get-items-variants-mapi.js";
10
11
  import { registerTool as registerChangeVariantWorkflowStepMapi } from "./tools/change-variant-workflow-step-mapi.js";
11
12
  import { registerTool as registerCreateVariantVersionMapi } from "./tools/create-variant-version-mapi.js";
12
13
  import { registerTool as registerDeleteContentItemMapi } from "./tools/delete-content-item-mapi.js";
@@ -112,6 +113,7 @@ export const createServer = () => {
112
113
  registerDeleteWorkflowMapi(server);
113
114
  registerChangeVariantWorkflowStepMapi(server);
114
115
  registerFilterVariantsMapi(server);
116
+ registerBulkGetItemsVariantsMapi(server);
115
117
  registerSearchVariantsMapi(server);
116
118
  registerPublishVariantMapi(server);
117
119
  registerUnpublishVariantMapi(server);
@@ -0,0 +1,31 @@
1
+ import { createMapiClient } from "../clients/kontentClients.js";
2
+ import { bulkGetItemsWithVariantsSchema } from "../schemas/bulkGetItemsWithVariantsSchemas.js";
3
+ import { handleMcpToolError } from "../utils/errorHandler.js";
4
+ import { createVariantMcpToolSuccessResponse } from "../utils/responseHelper.js";
5
+ import { throwError } from "../utils/throwError.js";
6
+ export const registerTool = (server) => {
7
+ server.tool("bulk-get-items-variants-mapi", "Bulk get Kontent.ai content items with their language variants by item and language reference pairs. Items without a variant in the requested language return the item without the variant property.", bulkGetItemsWithVariantsSchema.shape, async ({ variants, continuation_token }, { authInfo: { token, clientId } = {} }) => {
8
+ try {
9
+ const environmentId = clientId ?? process.env.KONTENT_ENVIRONMENT_ID;
10
+ if (!environmentId) {
11
+ throwError("Missing required environment ID");
12
+ }
13
+ const client = createMapiClient(environmentId, token);
14
+ const query = client.bulkGetItemsWithVariants().withData({
15
+ variants,
16
+ });
17
+ const response = await (continuation_token
18
+ ? query.xContinuationToken(continuation_token)
19
+ : query).toPromise();
20
+ return createVariantMcpToolSuccessResponse({
21
+ data: response.rawData.data,
22
+ pagination: {
23
+ continuation_token: response.data.pagination.continuationToken,
24
+ },
25
+ });
26
+ }
27
+ catch (error) {
28
+ return handleMcpToolError(error, "Bulk Get Items With Variants");
29
+ }
30
+ });
31
+ };
@@ -1,17 +1,17 @@
1
1
  import { createMapiClient } from "../clients/kontentClients.js";
2
2
  import { filterVariantsSchema } from "../schemas/filterVariantSchemas.js";
3
3
  import { handleMcpToolError } from "../utils/errorHandler.js";
4
- import { createVariantMcpToolSuccessResponse } from "../utils/responseHelper.js";
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 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 } = {} }) => {
7
+ server.tool("filter-variants-mapi", "Filter Kontent.ai items with variants returning references (item ID + language ID). For EXACT keyword matching and compliance (terms use OR). Use bulk-get-items-variants-mapi to retrieve full content for matched items. 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, spaces, collections, publishing_states, order_by, order_direction, continuation_token, }, { authInfo: { token, clientId } = {} }) => {
8
8
  try {
9
9
  const environmentId = clientId ?? process.env.KONTENT_ENVIRONMENT_ID;
10
10
  if (!environmentId) {
11
11
  throwError("Missing required environment ID");
12
12
  }
13
13
  const client = createMapiClient(environmentId, token);
14
- const query = client.earlyAccess.filterLanguageVariants().withData({
14
+ const query = client.filterItemsWithVariants().withData({
15
15
  filters: {
16
16
  search_phrase,
17
17
  content_types,
@@ -21,6 +21,9 @@ export const registerTool = (server) => {
21
21
  language,
22
22
  workflow_steps,
23
23
  taxonomy_groups,
24
+ spaces,
25
+ collections,
26
+ publishing_states,
24
27
  },
25
28
  order: order_by
26
29
  ? {
@@ -28,13 +31,12 @@ export const registerTool = (server) => {
28
31
  direction: order_direction || "asc",
29
32
  }
30
33
  : undefined,
31
- include_content: include_content ?? false,
32
34
  });
33
35
  const response = await (continuation_token
34
36
  ? query.xContinuationToken(continuation_token)
35
37
  : query).toPromise();
36
- return createVariantMcpToolSuccessResponse({
37
- data: response.rawData.data,
38
+ return createMcpToolSuccessResponse({
39
+ variants: response.rawData.variants,
38
40
  pagination: {
39
41
  continuation_token: response.data.pagination.continuationToken,
40
42
  },
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@kontent-ai/mcp-server",
3
- "version": "0.26.0",
3
+ "version": "0.27.0",
4
4
  "type": "module",
5
5
  "mcpName": "io.github.kontent-ai/mcp-server",
6
6
  "repository": {
@@ -29,7 +29,7 @@
29
29
  "author": "Jiri Lojda",
30
30
  "license": "MIT",
31
31
  "dependencies": {
32
- "@kontent-ai/management-sdk": "^8.1.0",
32
+ "@kontent-ai/management-sdk": "^8.3.0",
33
33
  "@modelcontextprotocol/sdk": "^1.25.2",
34
34
  "applicationinsights": "^2.9.8",
35
35
  "dotenv": "^17.2.3",