@kontent-ai/mcp-server 0.8.2 → 0.10.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 +6 -0
- package/build/schemas/contentItemSchemas.js +1 -7
- package/build/schemas/filterVariantSchemas.js +54 -0
- package/build/schemas/referenceObjectSchema.js +9 -0
- package/build/schemas/workflowSchemas.js +106 -0
- package/build/server.js +6 -0
- package/build/tools/change-variant-workflow-step-mapi.js +48 -0
- package/build/tools/context/initial-context.js +18 -0
- package/build/tools/filter-variants-mapi.js +66 -0
- package/build/tools/list-workflows-mapi.js +15 -0
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -88,6 +88,7 @@ npx @kontent-ai/mcp-server@latest sse
|
|
|
88
88
|
* **delete-content-item-mapi** – Delete a content item by internal ID
|
|
89
89
|
* **upsert-language-variant-mapi** – Create or update a language variant with content using internal IDs
|
|
90
90
|
* **delete-language-variant-mapi** – Delete a language variant of a content item by internal IDs
|
|
91
|
+
* **filter-variants-mapi** – Search and filter language variants using filters and search phrases
|
|
91
92
|
|
|
92
93
|
### Asset Management
|
|
93
94
|
|
|
@@ -98,6 +99,11 @@ npx @kontent-ai/mcp-server@latest sse
|
|
|
98
99
|
|
|
99
100
|
* **list-languages-mapi** – List all languages configured in the environment
|
|
100
101
|
|
|
102
|
+
### Workflow Management
|
|
103
|
+
|
|
104
|
+
* **list-workflows-mapi** – List all workflows in the environment with their lifecycle stages and transitions
|
|
105
|
+
* **change-variant-workflow-step-mapi** – Change the workflow step of a language variant (move content through workflow stages)
|
|
106
|
+
|
|
101
107
|
## ⚙️ Configuration
|
|
102
108
|
|
|
103
109
|
The server requires the following environment variables:
|
|
@@ -1,11 +1,5 @@
|
|
|
1
1
|
import { z } from "zod";
|
|
2
|
-
|
|
3
|
-
.object({
|
|
4
|
-
id: z.string().optional(),
|
|
5
|
-
codename: z.string().optional(),
|
|
6
|
-
external_id: z.string().optional(),
|
|
7
|
-
})
|
|
8
|
-
.describe("An object with an id, codename, or external_id property referencing another item. Using id is preferred for better performance.");
|
|
2
|
+
import { referenceObjectSchema } from "./referenceObjectSchema.js";
|
|
9
3
|
const languageVariantElementBaseSchema = z.object({
|
|
10
4
|
element: referenceObjectSchema,
|
|
11
5
|
value: z.any(),
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
import { z } from "zod";
|
|
2
|
+
import { referenceObjectSchema } from "./referenceObjectSchema.js";
|
|
3
|
+
const userReferenceSchema = z.object({
|
|
4
|
+
id: z.string().optional().describe("User identifier"),
|
|
5
|
+
email: z.string().email().optional().describe("User email address"),
|
|
6
|
+
});
|
|
7
|
+
// Search variants tool input schema
|
|
8
|
+
export const filterVariantsSchema = z.object({
|
|
9
|
+
search_phrase: z
|
|
10
|
+
.string()
|
|
11
|
+
.optional()
|
|
12
|
+
.describe("Search phrase to look for in content"),
|
|
13
|
+
content_types: z
|
|
14
|
+
.array(referenceObjectSchema)
|
|
15
|
+
.min(1)
|
|
16
|
+
.optional()
|
|
17
|
+
.describe("Array of references to content types by their id, codename, or external id"),
|
|
18
|
+
contributors: z
|
|
19
|
+
.array(userReferenceSchema)
|
|
20
|
+
.min(1)
|
|
21
|
+
.optional()
|
|
22
|
+
.describe("Array of references to users by their id or email"),
|
|
23
|
+
completion_statuses: z
|
|
24
|
+
.array(z.enum(["unfinished", "completed", "not_translated", "all_done"]))
|
|
25
|
+
.min(1)
|
|
26
|
+
.optional()
|
|
27
|
+
.describe("Array of completion statuses to filter by. It is not the same thing as workflow steps, it reflects e.g. not filled in required elements"),
|
|
28
|
+
language: referenceObjectSchema
|
|
29
|
+
.optional()
|
|
30
|
+
.describe("Reference to a language by its id, codename, or external id (defaults to default language)"),
|
|
31
|
+
workflow_steps: z
|
|
32
|
+
.array(z.object({
|
|
33
|
+
workflow_identifier: referenceObjectSchema.describe("Reference to a workflow by its id, codename, or external id"),
|
|
34
|
+
step_identifiers: z
|
|
35
|
+
.array(referenceObjectSchema)
|
|
36
|
+
.min(1)
|
|
37
|
+
.describe("Array of references to workflow steps by their id, codename, or external id"),
|
|
38
|
+
}))
|
|
39
|
+
.min(1)
|
|
40
|
+
.optional()
|
|
41
|
+
.describe("Array of workflows with workflow steps"),
|
|
42
|
+
order_by: z
|
|
43
|
+
.enum(["name", "due", "last_modified"])
|
|
44
|
+
.optional()
|
|
45
|
+
.describe("Field to order by"),
|
|
46
|
+
order_direction: z
|
|
47
|
+
.enum(["asc", "desc"])
|
|
48
|
+
.optional()
|
|
49
|
+
.describe("Order direction"),
|
|
50
|
+
continuation_token: z
|
|
51
|
+
.string()
|
|
52
|
+
.optional()
|
|
53
|
+
.describe("Continuation token for pagination"),
|
|
54
|
+
});
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import z from "zod";
|
|
2
|
+
// Define a reusable reference object schema
|
|
3
|
+
export const referenceObjectSchema = z
|
|
4
|
+
.object({
|
|
5
|
+
id: z.string().optional(),
|
|
6
|
+
codename: z.string().optional(),
|
|
7
|
+
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.");
|
|
@@ -0,0 +1,106 @@
|
|
|
1
|
+
import { z } from "zod";
|
|
2
|
+
// Schema for a workflow step
|
|
3
|
+
const workflowStepSchema = z.object({
|
|
4
|
+
id: z
|
|
5
|
+
.string()
|
|
6
|
+
.uuid()
|
|
7
|
+
.describe("The unique identifier of the workflow step in UUID format"),
|
|
8
|
+
name: z.string().describe("The human-readable name of the workflow step"),
|
|
9
|
+
codename: z
|
|
10
|
+
.string()
|
|
11
|
+
.describe("The codename of the workflow step used for API operations"),
|
|
12
|
+
transitions_to: z
|
|
13
|
+
.array(z.string().uuid())
|
|
14
|
+
.describe("Array of workflow step IDs that this step can transition to")
|
|
15
|
+
.optional(),
|
|
16
|
+
role_ids: z
|
|
17
|
+
.array(z.string().uuid())
|
|
18
|
+
.describe("Array of role IDs that have permissions for this workflow step")
|
|
19
|
+
.optional(),
|
|
20
|
+
});
|
|
21
|
+
// Schema for the published step
|
|
22
|
+
const publishedStepSchema = z.object({
|
|
23
|
+
id: z
|
|
24
|
+
.string()
|
|
25
|
+
.uuid()
|
|
26
|
+
.describe("The unique identifier of the published step in UUID format"),
|
|
27
|
+
name: z
|
|
28
|
+
.string()
|
|
29
|
+
.describe("The name of the published step - typically 'Published'"),
|
|
30
|
+
codename: z
|
|
31
|
+
.string()
|
|
32
|
+
.describe("The codename of the published step - typically 'published'"),
|
|
33
|
+
unpublish_role_ids: z
|
|
34
|
+
.array(z.string().uuid())
|
|
35
|
+
.describe("Array of role IDs that can unpublish content from this step")
|
|
36
|
+
.optional(),
|
|
37
|
+
create_new_version_role_ids: z
|
|
38
|
+
.array(z.string().uuid())
|
|
39
|
+
.describe("Array of role IDs that can create new versions of content in this step")
|
|
40
|
+
.optional(),
|
|
41
|
+
});
|
|
42
|
+
// Schema for the scheduled step
|
|
43
|
+
const scheduledStepSchema = z.object({
|
|
44
|
+
id: z
|
|
45
|
+
.string()
|
|
46
|
+
.uuid()
|
|
47
|
+
.describe("The unique identifier of the scheduled step in UUID format"),
|
|
48
|
+
name: z
|
|
49
|
+
.string()
|
|
50
|
+
.describe("The name of the scheduled step - typically 'Scheduled'"),
|
|
51
|
+
codename: z
|
|
52
|
+
.string()
|
|
53
|
+
.describe("The codename of the scheduled step - typically 'scheduled'"),
|
|
54
|
+
});
|
|
55
|
+
// Schema for the archived step
|
|
56
|
+
const archivedStepSchema = z.object({
|
|
57
|
+
id: z
|
|
58
|
+
.string()
|
|
59
|
+
.uuid()
|
|
60
|
+
.describe("The unique identifier of the archived step in UUID format"),
|
|
61
|
+
name: z
|
|
62
|
+
.string()
|
|
63
|
+
.describe("The name of the archived step - typically 'Archived'"),
|
|
64
|
+
codename: z
|
|
65
|
+
.string()
|
|
66
|
+
.describe("The codename of the archived step - typically 'archived'"),
|
|
67
|
+
role_ids: z
|
|
68
|
+
.array(z.string().uuid())
|
|
69
|
+
.describe("Array of role IDs that can unarchive content from this step")
|
|
70
|
+
.optional(),
|
|
71
|
+
});
|
|
72
|
+
// Schema for workflow scope
|
|
73
|
+
const workflowScopeSchema = z.object({
|
|
74
|
+
content_types: z
|
|
75
|
+
.array(z.object({
|
|
76
|
+
id: z
|
|
77
|
+
.string()
|
|
78
|
+
.uuid()
|
|
79
|
+
.describe("The unique identifier of the content type in UUID format"),
|
|
80
|
+
}))
|
|
81
|
+
.describe("Array of content types that this workflow applies to"),
|
|
82
|
+
});
|
|
83
|
+
// Main workflow schema
|
|
84
|
+
export const workflowSchema = z.object({
|
|
85
|
+
id: z
|
|
86
|
+
.string()
|
|
87
|
+
.uuid()
|
|
88
|
+
.describe("The unique identifier of the workflow in UUID format"),
|
|
89
|
+
name: z.string().describe("The human-readable name of the workflow"),
|
|
90
|
+
codename: z
|
|
91
|
+
.string()
|
|
92
|
+
.describe("The codename of the workflow used for API operations"),
|
|
93
|
+
scopes: z
|
|
94
|
+
.array(workflowScopeSchema)
|
|
95
|
+
.describe("Array of scopes defining which content types use this workflow"),
|
|
96
|
+
steps: z
|
|
97
|
+
.array(workflowStepSchema)
|
|
98
|
+
.describe("Array of custom workflow steps between draft and published states"),
|
|
99
|
+
published_step: publishedStepSchema.describe("The published step configuration of the workflow"),
|
|
100
|
+
scheduled_step: scheduledStepSchema.describe("The scheduled step configuration of the workflow"),
|
|
101
|
+
archived_step: archivedStepSchema.describe("The archived step configuration of the workflow"),
|
|
102
|
+
});
|
|
103
|
+
// Schema for the list workflows response
|
|
104
|
+
export const listWorkflowsResponseSchema = z
|
|
105
|
+
.array(workflowSchema)
|
|
106
|
+
.describe("Array of workflows in the project");
|
package/build/server.js
CHANGED
|
@@ -4,8 +4,10 @@ import { registerTool as registerAddContentItemMapi } from "./tools/add-content-
|
|
|
4
4
|
import { registerTool as registerAddContentTypeMapi } from "./tools/add-content-type-mapi.js";
|
|
5
5
|
import { registerTool as registerAddContentTypeSnippetMapi } from "./tools/add-content-type-snippet-mapi.js";
|
|
6
6
|
import { registerTool as registerAddTaxonomyGroupMapi } from "./tools/add-taxonomy-group-mapi.js";
|
|
7
|
+
import { registerTool as registerChangeVariantWorkflowStepMapi } from "./tools/change-variant-workflow-step-mapi.js";
|
|
7
8
|
import { registerTool as registerDeleteContentItemMapi } from "./tools/delete-content-item-mapi.js";
|
|
8
9
|
import { registerTool as registerDeleteLanguageVariantMapi } from "./tools/delete-language-variant-mapi.js";
|
|
10
|
+
import { registerTool as registerFilterVariantsMapi } from "./tools/filter-variants-mapi.js";
|
|
9
11
|
import { registerTool as registerGetAssetMapi } from "./tools/get-asset-mapi.js";
|
|
10
12
|
import { registerTool as registerGetInitialContext } from "./tools/get-initial-context.js";
|
|
11
13
|
import { registerTool as registerGetItemDapi } from "./tools/get-item-dapi.js";
|
|
@@ -19,6 +21,7 @@ import { registerTool as registerListContentTypeSnippetsMapi } from "./tools/lis
|
|
|
19
21
|
import { registerTool as registerListContentTypesMapi } from "./tools/list-content-types-mapi.js";
|
|
20
22
|
import { registerTool as registerListLanguagesMapi } from "./tools/list-languages-mapi.js";
|
|
21
23
|
import { registerTool as registerListTaxonomyGroupsMapi } from "./tools/list-taxonomy-groups-mapi.js";
|
|
24
|
+
import { registerTool as registerListWorkflowsMapi } from "./tools/list-workflows-mapi.js";
|
|
22
25
|
import { registerTool as registerUpdateContentItemMapi } from "./tools/update-content-item-mapi.js";
|
|
23
26
|
import { registerTool as registerUpsertLanguageVariantMapi } from "./tools/upsert-language-variant-mapi.js";
|
|
24
27
|
// Create server instance
|
|
@@ -53,5 +56,8 @@ export const createServer = () => {
|
|
|
53
56
|
registerDeleteContentItemMapi(server);
|
|
54
57
|
registerUpsertLanguageVariantMapi(server);
|
|
55
58
|
registerDeleteLanguageVariantMapi(server);
|
|
59
|
+
registerListWorkflowsMapi(server);
|
|
60
|
+
registerChangeVariantWorkflowStepMapi(server);
|
|
61
|
+
registerFilterVariantsMapi(server);
|
|
56
62
|
return { server };
|
|
57
63
|
};
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
import { z } from "zod";
|
|
2
|
+
import { createMapiClient } from "../clients/kontentClients.js";
|
|
3
|
+
import { handleMcpToolError } from "../utils/errorHandler.js";
|
|
4
|
+
import { createMcpToolSuccessResponse } from "../utils/responseHelper.js";
|
|
5
|
+
export const registerTool = (server) => {
|
|
6
|
+
server.tool("change-variant-workflow-step-mapi", "Change the workflow step of a language variant in Kontent.ai. This operation moves a language variant to a different step in the workflow, enabling content lifecycle management such as moving content from draft to review, review to published, etc.", {
|
|
7
|
+
itemId: z
|
|
8
|
+
.string()
|
|
9
|
+
.uuid()
|
|
10
|
+
.describe("Internal ID (UUID) of the content item whose language variant workflow step you want to change"),
|
|
11
|
+
languageId: z
|
|
12
|
+
.string()
|
|
13
|
+
.uuid()
|
|
14
|
+
.describe("Internal ID (UUID) of the language variant. Use '00000000-0000-0000-0000-000000000000' for the default language"),
|
|
15
|
+
workflowId: z
|
|
16
|
+
.string()
|
|
17
|
+
.uuid()
|
|
18
|
+
.describe("Internal ID (UUID) of the workflow. This is the workflow that contains the target step"),
|
|
19
|
+
workflowStepId: z
|
|
20
|
+
.string()
|
|
21
|
+
.uuid()
|
|
22
|
+
.describe("Internal ID (UUID) of the target workflow step. This must be a valid step ID from the specified workflow. Common steps include Draft, Review, Published, and Archived, but the actual IDs depend on your specific workflow configuration"),
|
|
23
|
+
}, async ({ itemId, languageId, workflowId, workflowStepId }) => {
|
|
24
|
+
const client = createMapiClient();
|
|
25
|
+
try {
|
|
26
|
+
const response = await client
|
|
27
|
+
.changeWorkflowOfLanguageVariant()
|
|
28
|
+
.byItemId(itemId)
|
|
29
|
+
.byLanguageId(languageId)
|
|
30
|
+
.withData({
|
|
31
|
+
workflow_identifier: {
|
|
32
|
+
id: workflowId,
|
|
33
|
+
},
|
|
34
|
+
step_identifier: {
|
|
35
|
+
id: workflowStepId,
|
|
36
|
+
},
|
|
37
|
+
})
|
|
38
|
+
.toPromise();
|
|
39
|
+
return createMcpToolSuccessResponse({
|
|
40
|
+
message: `Successfully changed workflow step of language variant '${languageId}' for content item '${itemId}' to workflow step '${workflowStepId}'`,
|
|
41
|
+
result: response.data,
|
|
42
|
+
});
|
|
43
|
+
}
|
|
44
|
+
catch (error) {
|
|
45
|
+
return handleMcpToolError(error, "Workflow Step Change");
|
|
46
|
+
}
|
|
47
|
+
});
|
|
48
|
+
};
|
|
@@ -80,6 +80,24 @@ ALL FOUR elements must be included in language variant using their internal IDs:
|
|
|
80
80
|
- Create new language variants when expanding to additional languages
|
|
81
81
|
- Organize with taxonomies for better content categorization
|
|
82
82
|
|
|
83
|
+
### Workflow Step Management
|
|
84
|
+
|
|
85
|
+
**CRITICAL**: When changing workflow steps of language variants, you must:
|
|
86
|
+
|
|
87
|
+
1. **Provide the workflowId parameter** - This is mandatory for all workflow step changes
|
|
88
|
+
2. **Use internal IDs** for itemId, languageId, and workflowStepId parameters
|
|
89
|
+
3. **Ensure the target workflow step exists** in the specified workflow
|
|
90
|
+
|
|
91
|
+
**WORKFLOW ID REQUIREMENT**: The workflowId parameter identifies which workflow contains the target step. Different content types may use different workflows, so always specify the correct workflow ID when changing workflow steps.
|
|
92
|
+
|
|
93
|
+
**Example**: To move a language variant to a "review" step:
|
|
94
|
+
- itemId: "content_item_internal_id"
|
|
95
|
+
- languageId: "language_internal_id"
|
|
96
|
+
- workflowStepId: "review_step_internal_id"
|
|
97
|
+
- workflowId: "workflow_internal_id" (MANDATORY)
|
|
98
|
+
|
|
99
|
+
**Failure to provide the workflowId parameter will result in workflow step change failures.**
|
|
100
|
+
|
|
83
101
|
## Essential Concepts
|
|
84
102
|
|
|
85
103
|
**Taxonomies** provide hierarchical content categorization, allowing you to organize and tag content systematically.
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
import { filterVariantsSchema } from "../schemas/filterVariantSchemas.js";
|
|
2
|
+
import { handleMcpToolError } from "../utils/errorHandler.js";
|
|
3
|
+
import { createMcpToolSuccessResponse } from "../utils/responseHelper.js";
|
|
4
|
+
import { throwError } from "../utils/throwError.js";
|
|
5
|
+
export const registerTool = (server) => {
|
|
6
|
+
server.tool("filter-variants-mapi", "Search and filter language variants of content items using the Management API.", filterVariantsSchema.shape, async ({ search_phrase, content_types, contributors, completion_statuses, language, workflow_steps, order_by, order_direction, continuation_token, }) => {
|
|
7
|
+
try {
|
|
8
|
+
const requestPayload = {
|
|
9
|
+
filters: {
|
|
10
|
+
search_phrase,
|
|
11
|
+
content_types,
|
|
12
|
+
contributors,
|
|
13
|
+
completion_statuses,
|
|
14
|
+
language,
|
|
15
|
+
workflow_steps,
|
|
16
|
+
},
|
|
17
|
+
order: order_by
|
|
18
|
+
? {
|
|
19
|
+
by: order_by,
|
|
20
|
+
direction: order_direction === "desc" ? "Descending" : "Ascending",
|
|
21
|
+
}
|
|
22
|
+
: null,
|
|
23
|
+
};
|
|
24
|
+
const environmentId = process.env.KONTENT_ENVIRONMENT_ID;
|
|
25
|
+
const apiKey = process.env.KONTENT_API_KEY;
|
|
26
|
+
if (!environmentId || !apiKey) {
|
|
27
|
+
throwError("Missing required environment variables");
|
|
28
|
+
}
|
|
29
|
+
const url = `https://manage.kontent.ai/v2/projects/${environmentId}/early-access/variants/filter`;
|
|
30
|
+
const headers = {
|
|
31
|
+
Authorization: `Bearer ${apiKey}`,
|
|
32
|
+
"Content-Type": "application/json",
|
|
33
|
+
};
|
|
34
|
+
if (continuation_token) {
|
|
35
|
+
headers["X-Continuation"] = continuation_token;
|
|
36
|
+
}
|
|
37
|
+
const response = await fetch(url, {
|
|
38
|
+
method: "POST",
|
|
39
|
+
headers: headers,
|
|
40
|
+
body: JSON.stringify(requestPayload),
|
|
41
|
+
});
|
|
42
|
+
if (!response.ok) {
|
|
43
|
+
const responseText = await response.text();
|
|
44
|
+
let responseData;
|
|
45
|
+
try {
|
|
46
|
+
responseData = JSON.parse(responseText);
|
|
47
|
+
}
|
|
48
|
+
catch {
|
|
49
|
+
responseData = responseText;
|
|
50
|
+
}
|
|
51
|
+
const error = new Error(`HTTP error! status: ${response.status}`);
|
|
52
|
+
error.response = {
|
|
53
|
+
status: response.status,
|
|
54
|
+
statusText: response.statusText,
|
|
55
|
+
data: responseData,
|
|
56
|
+
};
|
|
57
|
+
throw error;
|
|
58
|
+
}
|
|
59
|
+
const responseData = await response.json();
|
|
60
|
+
return createMcpToolSuccessResponse(responseData);
|
|
61
|
+
}
|
|
62
|
+
catch (error) {
|
|
63
|
+
return handleMcpToolError(error, "Variant Search");
|
|
64
|
+
}
|
|
65
|
+
});
|
|
66
|
+
};
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import { createMapiClient } from "../clients/kontentClients.js";
|
|
2
|
+
import { handleMcpToolError } from "../utils/errorHandler.js";
|
|
3
|
+
import { createMcpToolSuccessResponse } from "../utils/responseHelper.js";
|
|
4
|
+
export const registerTool = (server) => {
|
|
5
|
+
server.tool("list-workflows-mapi", "Get all workflows from Management API. Workflows define the content lifecycle stages and transitions between them.", {}, async () => {
|
|
6
|
+
const client = createMapiClient();
|
|
7
|
+
try {
|
|
8
|
+
const response = await client.listWorkflows().toPromise();
|
|
9
|
+
return createMcpToolSuccessResponse(response.data);
|
|
10
|
+
}
|
|
11
|
+
catch (error) {
|
|
12
|
+
return handleMcpToolError(error, "Workflows Listing");
|
|
13
|
+
}
|
|
14
|
+
});
|
|
15
|
+
};
|