@kontent-ai/mcp-server 0.23.2 → 0.24.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 +14 -2
- package/build/bin.js +1 -1
- package/build/schemas/assetFolderSchemas.js +44 -0
- package/build/schemas/assetSchemas.js +23 -0
- package/build/schemas/{contentTypeSchemas.js → contentTypeAndSnippetSchemas.js} +9 -10
- package/build/schemas/patchSchemas/baseTypeAndSnippetPatchSchemas.js +41 -0
- package/build/schemas/patchSchemas/contentTypePatchSchemas.js +4 -46
- package/build/schemas/patchSchemas/snippetPatchSchemas.js +22 -0
- package/build/schemas/patchSchemas/taxonomyPatchSchemas.js +67 -0
- package/build/schemas/spaceSchemas.js +30 -0
- package/build/server.js +20 -0
- package/build/telemetry/applicationInsights.js +1 -1
- package/build/tools/add-content-type-mapi.js +1 -1
- package/build/tools/add-content-type-snippet-mapi.js +1 -1
- package/build/tools/add-space-mapi.js +23 -0
- package/build/tools/context/patch-guide-path-based.js +53 -0
- package/build/tools/context/patch-guide-property-based.js +42 -0
- package/build/tools/context/patch-guide-reference-based.js +86 -0
- package/build/tools/delete-space-mapi.js +20 -0
- package/build/tools/delete-taxonomy-group-mapi.js +20 -0
- package/build/tools/delete-type-snippet-mapi.js +23 -0
- package/build/tools/get-patch-guide.js +31 -3
- package/build/tools/get-taxonomy-group-mapi.js +2 -2
- package/build/tools/list-asset-folders-mapi.js +15 -0
- package/build/tools/list-spaces-mapi.js +1 -1
- package/build/tools/list-taxonomy-groups-mapi.js +1 -1
- package/build/tools/patch-asset-folders-mapi.js +25 -0
- package/build/tools/patch-content-type-mapi.js +10 -6
- package/build/tools/patch-space-mapi.js +28 -0
- package/build/tools/patch-taxonomy-group-mapi.js +28 -0
- package/build/tools/patch-type-snippet-mapi.js +30 -0
- package/build/tools/update-asset-mapi.js +24 -0
- package/package.json +1 -1
- package/build/tools/context/patch-operations-guide.js +0 -58
package/README.md
CHANGED
|
@@ -63,7 +63,7 @@ npx @kontent-ai/mcp-server@latest shttp
|
|
|
63
63
|
|
|
64
64
|
### Patch Operations Guide
|
|
65
65
|
|
|
66
|
-
* **get-patch-guide** – 🚨 **REQUIRED before any patch operation**. Get
|
|
66
|
+
* **get-patch-guide** – 🚨 **REQUIRED before any patch operation**. Get patch operations guide for Kontent.ai Management API by entity type
|
|
67
67
|
|
|
68
68
|
### Content Type Management
|
|
69
69
|
|
|
@@ -78,17 +78,20 @@ npx @kontent-ai/mcp-server@latest shttp
|
|
|
78
78
|
* **get-type-snippet-mapi** – Get Kontent.ai content type snippet by internal ID from Management API
|
|
79
79
|
* **list-content-type-snippets-mapi** – Get all Kontent.ai content type snippets from Management API
|
|
80
80
|
* **add-content-type-snippet-mapi** – Add new Kontent.ai content type snippet via Management API
|
|
81
|
+
* **patch-type-snippet-mapi** – Update an existing Kontent.ai content type snippet by internal ID using patch operations (move, addInto, remove, replace)
|
|
82
|
+
* **delete-type-snippet-mapi** – Delete a Kontent.ai content type snippet by codename
|
|
81
83
|
|
|
82
84
|
### Taxonomy Management
|
|
83
85
|
|
|
84
86
|
* **get-taxonomy-group-mapi** – Get Kontent.ai taxonomy group by internal ID from Management API
|
|
85
87
|
* **list-taxonomy-groups-mapi** – Get all Kontent.ai taxonomy groups from Management API
|
|
86
88
|
* **add-taxonomy-group-mapi** – Add new Kontent.ai taxonomy group via Management API
|
|
89
|
+
* **patch-taxonomy-group-mapi** – Update Kontent.ai taxonomy group using patch operations (addInto, move, remove, replace) via Management API
|
|
90
|
+
* **delete-taxonomy-group-mapi** – Delete Kontent.ai taxonomy group by internal ID
|
|
87
91
|
|
|
88
92
|
### Content Item Management
|
|
89
93
|
|
|
90
94
|
* **get-item-mapi** – Get Kontent.ai item by internal ID from Management API
|
|
91
|
-
* **get-item-dapi** – Get Kontent.ai item by codename from Delivery API
|
|
92
95
|
* **get-latest-variant-mapi** – Get latest version of Kontent.ai language variant from Management API
|
|
93
96
|
* **get-published-variant-mapi** – Get published version of Kontent.ai language variant from Management API
|
|
94
97
|
* **list-variants-item-mapi** – List all Kontent.ai language variants of a content item from Management API
|
|
@@ -109,6 +112,12 @@ npx @kontent-ai/mcp-server@latest shttp
|
|
|
109
112
|
|
|
110
113
|
* **get-asset-mapi** – Get a specific Kontent.ai asset by internal ID from Management API
|
|
111
114
|
* **list-assets-mapi** – Get all Kontent.ai assets from Management API
|
|
115
|
+
* **update-asset-mapi** – Update Kontent.ai asset by internal ID
|
|
116
|
+
|
|
117
|
+
### Asset Folder Management
|
|
118
|
+
|
|
119
|
+
* **list-asset-folders-mapi** – List all Kontent.ai asset folders
|
|
120
|
+
* **patch-asset-folders-mapi** – Modify Kontent.ai asset folders using patch operations (addInto to add new folders, rename to change names, remove to delete folders)
|
|
112
121
|
|
|
113
122
|
### Language Management
|
|
114
123
|
|
|
@@ -124,6 +133,9 @@ npx @kontent-ai/mcp-server@latest shttp
|
|
|
124
133
|
### Space Management
|
|
125
134
|
|
|
126
135
|
* **list-spaces-mapi** – Get all Kontent.ai spaces from Management API
|
|
136
|
+
* **add-space-mapi** – Add Kontent.ai space to environment
|
|
137
|
+
* **patch-space-mapi** – Patch Kontent.ai space using replace operations
|
|
138
|
+
* **delete-space-mapi** – Delete Kontent.ai space
|
|
127
139
|
|
|
128
140
|
### Workflow Management
|
|
129
141
|
|
package/build/bin.js
CHANGED
|
@@ -152,7 +152,7 @@ Available endpoints:
|
|
|
152
152
|
async function startStdio() {
|
|
153
153
|
const { server } = createServer();
|
|
154
154
|
const transport = new StdioServerTransport();
|
|
155
|
-
console.
|
|
155
|
+
console.error(`Kontent.ai MCP Server v${version} (stdio) starting`);
|
|
156
156
|
await server.connect(transport);
|
|
157
157
|
}
|
|
158
158
|
async function main() {
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
import { z } from "zod";
|
|
2
|
+
import { referenceObjectSchema } from "./referenceObjectSchema.js";
|
|
3
|
+
const assetFolderValueSchema = z.object({
|
|
4
|
+
name: z.string(),
|
|
5
|
+
codename: z.string().optional(),
|
|
6
|
+
external_id: z.string().optional(),
|
|
7
|
+
folders: z.lazy(() => z.array(assetFolderValueSchema)).optional(),
|
|
8
|
+
});
|
|
9
|
+
const addIntoBaseSchema = {
|
|
10
|
+
op: z.literal("addInto"),
|
|
11
|
+
reference: referenceObjectSchema
|
|
12
|
+
.optional()
|
|
13
|
+
.describe("Parent folder reference. Omit to add at root level."),
|
|
14
|
+
value: assetFolderValueSchema,
|
|
15
|
+
};
|
|
16
|
+
const addIntoBeforeOperationSchema = z.object({
|
|
17
|
+
...addIntoBaseSchema,
|
|
18
|
+
before: referenceObjectSchema,
|
|
19
|
+
});
|
|
20
|
+
const addIntoAfterOperationSchema = z.object({
|
|
21
|
+
...addIntoBaseSchema,
|
|
22
|
+
after: referenceObjectSchema,
|
|
23
|
+
});
|
|
24
|
+
const addIntoDefaultOperationSchema = z.object(addIntoBaseSchema);
|
|
25
|
+
const renameOperationSchema = z.object({
|
|
26
|
+
op: z.literal("rename"),
|
|
27
|
+
reference: referenceObjectSchema,
|
|
28
|
+
value: z.string(),
|
|
29
|
+
});
|
|
30
|
+
const removeOperationSchema = z.object({
|
|
31
|
+
op: z.literal("remove"),
|
|
32
|
+
reference: referenceObjectSchema,
|
|
33
|
+
});
|
|
34
|
+
const assetFolderPatchOperationSchema = z.union([
|
|
35
|
+
addIntoBeforeOperationSchema,
|
|
36
|
+
addIntoAfterOperationSchema,
|
|
37
|
+
addIntoDefaultOperationSchema,
|
|
38
|
+
renameOperationSchema,
|
|
39
|
+
removeOperationSchema,
|
|
40
|
+
]);
|
|
41
|
+
export const assetFolderPatchOperationsSchema = z
|
|
42
|
+
.array(assetFolderPatchOperationSchema)
|
|
43
|
+
.min(1)
|
|
44
|
+
.describe("Patch operations array. Use addInto to add new folders (with optional reference for parent, before/after for positioning), rename to change folder names, remove to delete folders.");
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import { z } from "zod";
|
|
2
|
+
import { referenceObjectSchema } from "./referenceObjectSchema.js";
|
|
3
|
+
const assetDescriptionSchema = z.object({
|
|
4
|
+
language: referenceObjectSchema,
|
|
5
|
+
description: z.string(),
|
|
6
|
+
});
|
|
7
|
+
const assetTaxonomyElementSchema = z.object({
|
|
8
|
+
element: referenceObjectSchema,
|
|
9
|
+
value: z.array(referenceObjectSchema),
|
|
10
|
+
});
|
|
11
|
+
const assetCollectionReferenceSchema = z.object({
|
|
12
|
+
reference: referenceObjectSchema,
|
|
13
|
+
});
|
|
14
|
+
export const updateAssetDataSchema = z
|
|
15
|
+
.object({
|
|
16
|
+
title: z.string().optional(),
|
|
17
|
+
codename: z.string().optional(),
|
|
18
|
+
collection: assetCollectionReferenceSchema.optional(),
|
|
19
|
+
folder: referenceObjectSchema.optional(),
|
|
20
|
+
descriptions: z.array(assetDescriptionSchema).optional(),
|
|
21
|
+
elements: z.array(assetTaxonomyElementSchema).optional(),
|
|
22
|
+
})
|
|
23
|
+
.describe("Only include properties you want to update; omitted fields retain existing values.");
|
|
@@ -1,9 +1,5 @@
|
|
|
1
1
|
import { z } from "zod";
|
|
2
|
-
|
|
3
|
-
export const referenceObjectSchema = z.object({
|
|
4
|
-
id: z.string().optional(),
|
|
5
|
-
codename: z.string().optional(),
|
|
6
|
-
});
|
|
2
|
+
import { referenceObjectSchema } from "./referenceObjectSchema.js";
|
|
7
3
|
// Common property schemas
|
|
8
4
|
const baseElementSchema = {
|
|
9
5
|
codename: z.string().optional(),
|
|
@@ -16,7 +12,10 @@ const contentGroupElementSchema = {
|
|
|
16
12
|
const namedElementSchema = {
|
|
17
13
|
...baseElementSchema,
|
|
18
14
|
name: z.string(),
|
|
19
|
-
guidelines: z
|
|
15
|
+
guidelines: z
|
|
16
|
+
.string()
|
|
17
|
+
.optional()
|
|
18
|
+
.describe("Plain text guidelines shown to editors"),
|
|
20
19
|
is_required: z.boolean().optional(),
|
|
21
20
|
is_non_localizable: z.boolean().optional(),
|
|
22
21
|
};
|
|
@@ -93,7 +92,7 @@ const assetElementSchema = {
|
|
|
93
92
|
const customElementSchema = {
|
|
94
93
|
type: z.literal("custom"),
|
|
95
94
|
...namedElementSchema,
|
|
96
|
-
source_url: z.
|
|
95
|
+
source_url: z.url(),
|
|
97
96
|
json_parameters: z.string().optional(),
|
|
98
97
|
allowed_elements: z.array(referenceObjectSchema).optional(),
|
|
99
98
|
};
|
|
@@ -105,7 +104,9 @@ const dateTimeElementSchema = {
|
|
|
105
104
|
const guidelinesElementSchema = {
|
|
106
105
|
type: z.literal("guidelines"),
|
|
107
106
|
...baseElementSchema,
|
|
108
|
-
guidelines: z
|
|
107
|
+
guidelines: z
|
|
108
|
+
.string()
|
|
109
|
+
.describe("HTML content for guidelines element. Must be valid HTML."),
|
|
109
110
|
};
|
|
110
111
|
const modularContentElementSchema = {
|
|
111
112
|
type: z.literal("modular_content"),
|
|
@@ -292,14 +293,12 @@ export const contentGroupSchema = z.object({
|
|
|
292
293
|
external_id: z.string().optional(),
|
|
293
294
|
codename: z.string().optional(),
|
|
294
295
|
});
|
|
295
|
-
// Define a union type for snippet elements (excluding url_slug and snippet elements)
|
|
296
296
|
export const snippetElementSchema = z.discriminatedUnion("type", [
|
|
297
297
|
z.object(assetElementSchema),
|
|
298
298
|
z.object(customElementSchema),
|
|
299
299
|
z.object(dateTimeElementSchema),
|
|
300
300
|
z.object(guidelinesElementSchema),
|
|
301
301
|
z.object(modularContentElementSchema),
|
|
302
|
-
z.object(subpagesElementSchema),
|
|
303
302
|
z.object(multipleChoiceElementSchema),
|
|
304
303
|
z.object(numberElementSchema),
|
|
305
304
|
z.object(richTextElementSchema),
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
import { z } from "zod";
|
|
2
|
+
import { allowedBlockSchema, allowedFormattingSchema, allowedTableBlockSchema, allowedTableFormattingSchema, allowedTableTextBlockSchema, allowedTextBlockSchema, arrayDefaultSchema, countLimitSchema, numberDefaultSchema, optionSchema, regexValidationSchema, stringDefaultSchema, textLengthLimitSchema, } from "../contentTypeAndSnippetSchemas.js";
|
|
3
|
+
import { referenceObjectSchema } from "../referenceObjectSchema.js";
|
|
4
|
+
export const moveOperationSchema = z.object({
|
|
5
|
+
op: z.literal("move"),
|
|
6
|
+
path: z.string().describe("Path to object (format: id:{uuid})"),
|
|
7
|
+
before: referenceObjectSchema.optional(),
|
|
8
|
+
after: referenceObjectSchema.optional(),
|
|
9
|
+
});
|
|
10
|
+
export const removeOperationSchema = z.object({
|
|
11
|
+
op: z.literal("remove"),
|
|
12
|
+
path: z.string().describe("Path to item to remove (format: id:{uuid})"),
|
|
13
|
+
});
|
|
14
|
+
export const sharedAddIntoValueSchemas = [
|
|
15
|
+
optionSchema,
|
|
16
|
+
referenceObjectSchema,
|
|
17
|
+
allowedBlockSchema,
|
|
18
|
+
allowedFormattingSchema,
|
|
19
|
+
allowedTextBlockSchema,
|
|
20
|
+
allowedTableBlockSchema,
|
|
21
|
+
allowedTableFormattingSchema,
|
|
22
|
+
allowedTableTextBlockSchema,
|
|
23
|
+
z.string(),
|
|
24
|
+
z.number(),
|
|
25
|
+
z.boolean(),
|
|
26
|
+
z.null(),
|
|
27
|
+
z.any(),
|
|
28
|
+
];
|
|
29
|
+
export const sharedReplaceValueSchemas = [
|
|
30
|
+
regexValidationSchema,
|
|
31
|
+
textLengthLimitSchema,
|
|
32
|
+
countLimitSchema,
|
|
33
|
+
arrayDefaultSchema,
|
|
34
|
+
stringDefaultSchema,
|
|
35
|
+
numberDefaultSchema,
|
|
36
|
+
z.string(),
|
|
37
|
+
z.number(),
|
|
38
|
+
z.boolean(),
|
|
39
|
+
z.null(),
|
|
40
|
+
z.any(),
|
|
41
|
+
];
|
|
@@ -1,66 +1,24 @@
|
|
|
1
|
-
// Patch operation schemas for content type modifications
|
|
2
|
-
// Based on: https://kontent.ai/learn/docs/apis/openapi/management-api-v2/#operation/modify-a-content-type
|
|
3
1
|
import { z } from "zod";
|
|
4
|
-
import {
|
|
5
|
-
|
|
6
|
-
const moveOperationSchema = z.object({
|
|
7
|
-
op: z.literal("move"),
|
|
8
|
-
path: z.string().describe("Path to object (format: id:{uuid})"),
|
|
9
|
-
before: referenceObjectSchema.optional(),
|
|
10
|
-
after: referenceObjectSchema.optional(),
|
|
11
|
-
});
|
|
12
|
-
// AddInto operation - Add new elements to content type
|
|
2
|
+
import { contentGroupSchema, dependsOnSchema, elementSchema, } from "../contentTypeAndSnippetSchemas.js";
|
|
3
|
+
import { moveOperationSchema, removeOperationSchema, sharedAddIntoValueSchemas, sharedReplaceValueSchemas, } from "./baseTypeAndSnippetPatchSchemas.js";
|
|
13
4
|
const addIntoOperationSchema = z.object({
|
|
14
5
|
op: z.literal("addInto"),
|
|
15
6
|
path: z.string().describe("Path where to add item (format: id:{uuid})"),
|
|
16
7
|
value: z.union([
|
|
17
8
|
elementSchema,
|
|
18
|
-
optionSchema,
|
|
19
9
|
contentGroupSchema,
|
|
20
|
-
|
|
21
|
-
allowedBlockSchema,
|
|
22
|
-
allowedFormattingSchema,
|
|
23
|
-
allowedTextBlockSchema,
|
|
24
|
-
allowedTableBlockSchema,
|
|
25
|
-
allowedTableFormattingSchema,
|
|
26
|
-
allowedTableTextBlockSchema,
|
|
27
|
-
z.string(),
|
|
28
|
-
z.number(),
|
|
29
|
-
z.boolean(),
|
|
30
|
-
z.null(),
|
|
31
|
-
z.any(),
|
|
10
|
+
...sharedAddIntoValueSchemas,
|
|
32
11
|
]),
|
|
33
12
|
});
|
|
34
|
-
// Remove operation - Remove elements from content type
|
|
35
|
-
const removeOperationSchema = z.object({
|
|
36
|
-
op: z.literal("remove"),
|
|
37
|
-
path: z.string().describe("Path to item to remove (format: id:{uuid})"),
|
|
38
|
-
});
|
|
39
|
-
// Replace operation - Replace/update existing elements in content type
|
|
40
13
|
const replaceOperationSchema = z.object({
|
|
41
14
|
op: z.literal("replace"),
|
|
42
15
|
path: z.string().describe("Path to property to replace (format: id:{uuid})"),
|
|
43
|
-
value: z.union([
|
|
44
|
-
dependsOnSchema,
|
|
45
|
-
regexValidationSchema,
|
|
46
|
-
textLengthLimitSchema,
|
|
47
|
-
countLimitSchema,
|
|
48
|
-
arrayDefaultSchema,
|
|
49
|
-
stringDefaultSchema,
|
|
50
|
-
numberDefaultSchema,
|
|
51
|
-
z.string(),
|
|
52
|
-
z.number(),
|
|
53
|
-
z.boolean(),
|
|
54
|
-
z.null(),
|
|
55
|
-
z.any(),
|
|
56
|
-
]),
|
|
16
|
+
value: z.union([dependsOnSchema, ...sharedReplaceValueSchemas]),
|
|
57
17
|
});
|
|
58
|
-
// Union type for all patch operations
|
|
59
18
|
export const patchOperationSchema = z.discriminatedUnion("op", [
|
|
60
19
|
moveOperationSchema,
|
|
61
20
|
addIntoOperationSchema,
|
|
62
21
|
removeOperationSchema,
|
|
63
22
|
replaceOperationSchema,
|
|
64
23
|
]);
|
|
65
|
-
// Schema for array of patch operations
|
|
66
24
|
export const patchOperationsSchema = z.array(patchOperationSchema).min(1);
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import { z } from "zod";
|
|
2
|
+
import { snippetElementSchema } from "../contentTypeAndSnippetSchemas.js";
|
|
3
|
+
import { moveOperationSchema, removeOperationSchema, sharedAddIntoValueSchemas, sharedReplaceValueSchemas, } from "./baseTypeAndSnippetPatchSchemas.js";
|
|
4
|
+
const addIntoOperationSchema = z.object({
|
|
5
|
+
op: z.literal("addInto"),
|
|
6
|
+
path: z.string().describe("Path where to add item (format: id:{uuid})"),
|
|
7
|
+
value: z.union([snippetElementSchema, ...sharedAddIntoValueSchemas]),
|
|
8
|
+
});
|
|
9
|
+
const replaceOperationSchema = z.object({
|
|
10
|
+
op: z.literal("replace"),
|
|
11
|
+
path: z.string().describe("Path to property to replace (format: id:{uuid})"),
|
|
12
|
+
value: z.union(sharedReplaceValueSchemas),
|
|
13
|
+
});
|
|
14
|
+
export const snippetPatchOperationSchema = z.discriminatedUnion("op", [
|
|
15
|
+
moveOperationSchema,
|
|
16
|
+
addIntoOperationSchema,
|
|
17
|
+
removeOperationSchema,
|
|
18
|
+
replaceOperationSchema,
|
|
19
|
+
]);
|
|
20
|
+
export const snippetPatchOperationsSchema = z
|
|
21
|
+
.array(snippetPatchOperationSchema)
|
|
22
|
+
.min(1);
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
import { z } from "zod";
|
|
2
|
+
import { referenceObjectSchema } from "../referenceObjectSchema.js";
|
|
3
|
+
const taxonomyTermValueSchema = z.object({
|
|
4
|
+
name: z.string(),
|
|
5
|
+
codename: z.string().optional(),
|
|
6
|
+
external_id: z.string().optional(),
|
|
7
|
+
terms: z.lazy(() => z.array(taxonomyTermValueSchema)).optional(),
|
|
8
|
+
});
|
|
9
|
+
const addIntoBaseSchema = {
|
|
10
|
+
op: z.literal("addInto"),
|
|
11
|
+
reference: referenceObjectSchema
|
|
12
|
+
.optional()
|
|
13
|
+
.describe("Parent term reference. Omit to add at root level of taxonomy group."),
|
|
14
|
+
value: taxonomyTermValueSchema,
|
|
15
|
+
};
|
|
16
|
+
const addIntoBeforeOperationSchema = z.object({
|
|
17
|
+
...addIntoBaseSchema,
|
|
18
|
+
before: referenceObjectSchema,
|
|
19
|
+
});
|
|
20
|
+
const addIntoAfterOperationSchema = z.object({
|
|
21
|
+
...addIntoBaseSchema,
|
|
22
|
+
after: referenceObjectSchema,
|
|
23
|
+
});
|
|
24
|
+
const addIntoDefaultOperationSchema = z.object(addIntoBaseSchema);
|
|
25
|
+
const moveBaseSchema = {
|
|
26
|
+
op: z.literal("move"),
|
|
27
|
+
reference: referenceObjectSchema,
|
|
28
|
+
};
|
|
29
|
+
const moveBeforeOperationSchema = z.object({
|
|
30
|
+
...moveBaseSchema,
|
|
31
|
+
before: referenceObjectSchema,
|
|
32
|
+
});
|
|
33
|
+
const moveAfterOperationSchema = z.object({
|
|
34
|
+
...moveBaseSchema,
|
|
35
|
+
after: referenceObjectSchema,
|
|
36
|
+
});
|
|
37
|
+
const moveUnderOperationSchema = z.object({
|
|
38
|
+
...moveBaseSchema,
|
|
39
|
+
under: referenceObjectSchema.describe("Move as child of this term (tree nesting)"),
|
|
40
|
+
});
|
|
41
|
+
const removeOperationSchema = z.object({
|
|
42
|
+
op: z.literal("remove"),
|
|
43
|
+
reference: referenceObjectSchema,
|
|
44
|
+
});
|
|
45
|
+
const replaceOperationSchema = z.object({
|
|
46
|
+
op: z.literal("replace"),
|
|
47
|
+
reference: referenceObjectSchema
|
|
48
|
+
.optional()
|
|
49
|
+
.describe("Term reference. Omit when modifying group-level properties (name, codename). Required when modifying specific term."),
|
|
50
|
+
property_name: z.enum(["name", "codename", "terms"]),
|
|
51
|
+
value: z
|
|
52
|
+
.union([z.string(), z.array(taxonomyTermValueSchema)])
|
|
53
|
+
.describe("New value. String for name/codename, array for terms."),
|
|
54
|
+
});
|
|
55
|
+
export const taxonomyPatchOperationSchema = z.union([
|
|
56
|
+
addIntoBeforeOperationSchema,
|
|
57
|
+
addIntoAfterOperationSchema,
|
|
58
|
+
addIntoDefaultOperationSchema,
|
|
59
|
+
moveBeforeOperationSchema,
|
|
60
|
+
moveAfterOperationSchema,
|
|
61
|
+
moveUnderOperationSchema,
|
|
62
|
+
removeOperationSchema,
|
|
63
|
+
replaceOperationSchema,
|
|
64
|
+
]);
|
|
65
|
+
export const taxonomyPatchOperationsSchema = z
|
|
66
|
+
.array(taxonomyPatchOperationSchema)
|
|
67
|
+
.min(1);
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import { z } from "zod";
|
|
2
|
+
import { referenceObjectSchema } from "./referenceObjectSchema.js";
|
|
3
|
+
const nameReplaceOperationSchema = z.object({
|
|
4
|
+
op: z.literal("replace"),
|
|
5
|
+
property_name: z.literal("name"),
|
|
6
|
+
value: z.string(),
|
|
7
|
+
});
|
|
8
|
+
const codenameReplaceOperationSchema = z.object({
|
|
9
|
+
op: z.literal("replace"),
|
|
10
|
+
property_name: z.literal("codename"),
|
|
11
|
+
value: z.string(),
|
|
12
|
+
});
|
|
13
|
+
const collectionsReplaceOperationSchema = z.object({
|
|
14
|
+
op: z.literal("replace"),
|
|
15
|
+
property_name: z.literal("collections"),
|
|
16
|
+
value: z.array(referenceObjectSchema),
|
|
17
|
+
});
|
|
18
|
+
export const spacePatchOperationSchema = z.discriminatedUnion("property_name", [
|
|
19
|
+
nameReplaceOperationSchema,
|
|
20
|
+
codenameReplaceOperationSchema,
|
|
21
|
+
collectionsReplaceOperationSchema,
|
|
22
|
+
]);
|
|
23
|
+
export const spacePatchOperationsSchema = z
|
|
24
|
+
.array(spacePatchOperationSchema)
|
|
25
|
+
.min(1);
|
|
26
|
+
export const addSpaceSchema = z.object({
|
|
27
|
+
name: z.string(),
|
|
28
|
+
codename: z.string().optional(),
|
|
29
|
+
collections: z.array(referenceObjectSchema).optional(),
|
|
30
|
+
});
|
package/build/server.js
CHANGED
|
@@ -4,12 +4,16 @@ 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 registerAddLanguageMapi } from "./tools/add-language-mapi.js";
|
|
7
|
+
import { registerTool as registerAddSpaceMapi } from "./tools/add-space-mapi.js";
|
|
7
8
|
import { registerTool as registerAddTaxonomyGroupMapi } from "./tools/add-taxonomy-group-mapi.js";
|
|
8
9
|
import { registerTool as registerChangeVariantWorkflowStepMapi } from "./tools/change-variant-workflow-step-mapi.js";
|
|
9
10
|
import { registerTool as registerCreateVariantVersionMapi } from "./tools/create-variant-version-mapi.js";
|
|
10
11
|
import { registerTool as registerDeleteContentItemMapi } from "./tools/delete-content-item-mapi.js";
|
|
11
12
|
import { registerTool as registerDeleteContentTypeMapi } from "./tools/delete-content-type-mapi.js";
|
|
12
13
|
import { registerTool as registerDeleteLanguageVariantMapi } from "./tools/delete-language-variant-mapi.js";
|
|
14
|
+
import { registerTool as registerDeleteSpaceMapi } from "./tools/delete-space-mapi.js";
|
|
15
|
+
import { registerTool as registerDeleteTaxonomyGroupMapi } from "./tools/delete-taxonomy-group-mapi.js";
|
|
16
|
+
import { registerTool as registerDeleteTypeSnippetMapi } from "./tools/delete-type-snippet-mapi.js";
|
|
13
17
|
import { registerTool as registerFilterVariantsMapi } from "./tools/filter-variants-mapi.js";
|
|
14
18
|
import { registerTool as registerGetAssetMapi } from "./tools/get-asset-mapi.js";
|
|
15
19
|
import { registerTool as registerGetItemMapi } from "./tools/get-item-mapi.js";
|
|
@@ -19,6 +23,7 @@ import { registerTool as registerGetPublishedVariantMapi } from "./tools/get-pub
|
|
|
19
23
|
import { registerTool as registerGetTaxonomyGroupMapi } from "./tools/get-taxonomy-group-mapi.js";
|
|
20
24
|
import { registerTool as registerGetTypeMapi } from "./tools/get-type-mapi.js";
|
|
21
25
|
import { registerTool as registerGetTypeSnippetMapi } from "./tools/get-type-snippet-mapi.js";
|
|
26
|
+
import { registerTool as registerListAssetFoldersMapi } from "./tools/list-asset-folders-mapi.js";
|
|
22
27
|
import { registerTool as registerListAssetsMapi } from "./tools/list-assets-mapi.js";
|
|
23
28
|
import { registerTool as registerListCollectionsMapi } from "./tools/list-collections-mapi.js";
|
|
24
29
|
import { registerTool as registerListContentTypeSnippetsMapi } from "./tools/list-content-type-snippets-mapi.js";
|
|
@@ -32,12 +37,17 @@ import { registerTool as registerListVariantsItemMapi } from "./tools/list-varia
|
|
|
32
37
|
import { registerTool as registerListVariantsSpaceMapi } from "./tools/list-variants-space-mapi.js";
|
|
33
38
|
import { registerTool as registerListVariantsTypeMapi } from "./tools/list-variants-type-mapi.js";
|
|
34
39
|
import { registerTool as registerListWorkflowsMapi } from "./tools/list-workflows-mapi.js";
|
|
40
|
+
import { registerTool as registerPatchAssetFoldersMapi } from "./tools/patch-asset-folders-mapi.js";
|
|
35
41
|
import { registerTool as registerPatchCollectionsMapi } from "./tools/patch-collections-mapi.js";
|
|
36
42
|
import { registerTool as registerPatchContentTypeMapi } from "./tools/patch-content-type-mapi.js";
|
|
37
43
|
import { registerTool as registerPatchLanguageMapi } from "./tools/patch-language-mapi.js";
|
|
44
|
+
import { registerTool as registerPatchSpaceMapi } from "./tools/patch-space-mapi.js";
|
|
45
|
+
import { registerTool as registerPatchTaxonomyGroupMapi } from "./tools/patch-taxonomy-group-mapi.js";
|
|
46
|
+
import { registerTool as registerPatchTypeSnippetMapi } from "./tools/patch-type-snippet-mapi.js";
|
|
38
47
|
import { registerTool as registerPublishVariantMapi } from "./tools/publish-variant-mapi.js";
|
|
39
48
|
import { registerTool as registerSearchVariantsMapi } from "./tools/search-variants-mapi.js";
|
|
40
49
|
import { registerTool as registerUnpublishVariantMapi } from "./tools/unpublish-variant-mapi.js";
|
|
50
|
+
import { registerTool as registerUpdateAssetMapi } from "./tools/update-asset-mapi.js";
|
|
41
51
|
import { registerTool as registerUpdateContentItemMapi } from "./tools/update-content-item-mapi.js";
|
|
42
52
|
import { registerTool as registerUpsertLanguageVariantMapi } from "./tools/upsert-language-variant-mapi.js";
|
|
43
53
|
// Create server instance
|
|
@@ -65,16 +75,26 @@ export const createServer = () => {
|
|
|
65
75
|
registerListCollectionsMapi(server);
|
|
66
76
|
registerPatchCollectionsMapi(server);
|
|
67
77
|
registerListSpacesMapi(server);
|
|
78
|
+
registerAddSpaceMapi(server);
|
|
79
|
+
registerPatchSpaceMapi(server);
|
|
80
|
+
registerDeleteSpaceMapi(server);
|
|
68
81
|
registerGetAssetMapi(server);
|
|
69
82
|
registerListAssetsMapi(server);
|
|
83
|
+
registerUpdateAssetMapi(server);
|
|
84
|
+
registerListAssetFoldersMapi(server);
|
|
85
|
+
registerPatchAssetFoldersMapi(server);
|
|
70
86
|
registerAddContentTypeMapi(server);
|
|
71
87
|
registerPatchContentTypeMapi(server);
|
|
72
88
|
registerAddContentTypeSnippetMapi(server);
|
|
73
89
|
registerGetTypeSnippetMapi(server);
|
|
74
90
|
registerListContentTypeSnippetsMapi(server);
|
|
91
|
+
registerPatchTypeSnippetMapi(server);
|
|
92
|
+
registerDeleteTypeSnippetMapi(server);
|
|
75
93
|
registerAddTaxonomyGroupMapi(server);
|
|
76
94
|
registerListTaxonomyGroupsMapi(server);
|
|
77
95
|
registerGetTaxonomyGroupMapi(server);
|
|
96
|
+
registerPatchTaxonomyGroupMapi(server);
|
|
97
|
+
registerDeleteTaxonomyGroupMapi(server);
|
|
78
98
|
registerAddContentItemMapi(server);
|
|
79
99
|
registerUpdateContentItemMapi(server);
|
|
80
100
|
registerDeleteContentItemMapi(server);
|
|
@@ -156,6 +156,6 @@ export function initializeApplicationInsights() {
|
|
|
156
156
|
appInsights.defaultClient.addTelemetryProcessor(createTelemetryProcessor());
|
|
157
157
|
}
|
|
158
158
|
catch (error) {
|
|
159
|
-
console.
|
|
159
|
+
console.error("Failed to initialize Application Insights:", error);
|
|
160
160
|
}
|
|
161
161
|
}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { z } from "zod";
|
|
2
2
|
import { createMapiClient } from "../clients/kontentClients.js";
|
|
3
|
-
import { contentGroupSchema, elementSchema, } from "../schemas/
|
|
3
|
+
import { contentGroupSchema, elementSchema, } from "../schemas/contentTypeAndSnippetSchemas.js";
|
|
4
4
|
import { handleMcpToolError } from "../utils/errorHandler.js";
|
|
5
5
|
import { createMcpToolSuccessResponse } from "../utils/responseHelper.js";
|
|
6
6
|
export const registerTool = (server) => {
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { z } from "zod";
|
|
2
2
|
import { createMapiClient } from "../clients/kontentClients.js";
|
|
3
|
-
import { snippetElementSchema } from "../schemas/
|
|
3
|
+
import { snippetElementSchema } from "../schemas/contentTypeAndSnippetSchemas.js";
|
|
4
4
|
import { handleMcpToolError } from "../utils/errorHandler.js";
|
|
5
5
|
import { createMcpToolSuccessResponse } from "../utils/responseHelper.js";
|
|
6
6
|
export const registerTool = (server) => {
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import { createMapiClient } from "../clients/kontentClients.js";
|
|
2
|
+
import { addSpaceSchema } from "../schemas/spaceSchemas.js";
|
|
3
|
+
import { handleMcpToolError } from "../utils/errorHandler.js";
|
|
4
|
+
import { createMcpToolSuccessResponse } from "../utils/responseHelper.js";
|
|
5
|
+
export const registerTool = (server) => {
|
|
6
|
+
server.tool("add-space-mapi", "Add Kontent.ai space", addSpaceSchema.shape, async ({ name, codename, collections }, { authInfo: { token, clientId } = {} }) => {
|
|
7
|
+
const client = createMapiClient(clientId, token);
|
|
8
|
+
try {
|
|
9
|
+
const response = await client
|
|
10
|
+
.addSpace()
|
|
11
|
+
.withData({
|
|
12
|
+
name,
|
|
13
|
+
codename,
|
|
14
|
+
collections,
|
|
15
|
+
})
|
|
16
|
+
.toPromise();
|
|
17
|
+
return createMcpToolSuccessResponse(response.rawData);
|
|
18
|
+
}
|
|
19
|
+
catch (error) {
|
|
20
|
+
return handleMcpToolError(error, "Space Creation");
|
|
21
|
+
}
|
|
22
|
+
});
|
|
23
|
+
};
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
export const pathBasedPatchGuide = `
|
|
2
|
+
# Patch Guide: Content Types & Snippets
|
|
3
|
+
|
|
4
|
+
## Addressing
|
|
5
|
+
Use JSON Pointer 'path' property with id:{uuid} format.
|
|
6
|
+
|
|
7
|
+
## Path Formats
|
|
8
|
+
- Element: /elements/id:{uuid}
|
|
9
|
+
- Element property: /elements/id:{uuid}/name
|
|
10
|
+
- Option: /elements/id:{uuid}/options/id:{uuid}
|
|
11
|
+
- Content group: /content_groups/id:{uuid}
|
|
12
|
+
- Array item: /elements/id:{uuid}/allowed_content_types/id:{uuid}
|
|
13
|
+
|
|
14
|
+
## Operations
|
|
15
|
+
|
|
16
|
+
**move** - Reorder elements/options/groups
|
|
17
|
+
\`\`\`json
|
|
18
|
+
{ "op": "move", "path": "/elements/id:{uuid}", "before": { "id": "{uuid}" } }
|
|
19
|
+
{ "op": "move", "path": "/elements/id:{uuid}", "after": { "id": "{uuid}" } }
|
|
20
|
+
\`\`\`
|
|
21
|
+
|
|
22
|
+
**addInto** - Add to arrays (elements, options, allowed_blocks, etc.)
|
|
23
|
+
\`\`\`json
|
|
24
|
+
{ "op": "addInto", "path": "/elements", "value": { "name": "Title", "type": "text", ... } }
|
|
25
|
+
{ "op": "addInto", "path": "/elements/id:{uuid}/allowed_content_types", "value": { "id": "{uuid}" } }
|
|
26
|
+
\`\`\`
|
|
27
|
+
|
|
28
|
+
**remove** - Delete items
|
|
29
|
+
\`\`\`json
|
|
30
|
+
{ "op": "remove", "path": "/elements/id:{uuid}" }
|
|
31
|
+
{ "op": "remove", "path": "/elements/id:{uuid}/allowed_content_types/id:{uuid}" }
|
|
32
|
+
\`\`\`
|
|
33
|
+
|
|
34
|
+
**replace** - Update primitives/objects
|
|
35
|
+
\`\`\`json
|
|
36
|
+
{ "op": "replace", "path": "/elements/id:{uuid}/name", "value": "New Name" }
|
|
37
|
+
{ "op": "replace", "path": "/elements/id:{uuid}/maximum_text_length", "value": { "value": 100, "applies_to": "characters" } }
|
|
38
|
+
\`\`\`
|
|
39
|
+
|
|
40
|
+
## Rules
|
|
41
|
+
- Use addInto/remove for arrays, replace for primitives/objects
|
|
42
|
+
- external_id and type cannot be modified after creation
|
|
43
|
+
- Empty arrays enable all options (allowed_blocks, allowed_formatting, etc.)
|
|
44
|
+
- 'unstyled' must be first when adding allowed_formatting
|
|
45
|
+
|
|
46
|
+
## Content Type Specifics
|
|
47
|
+
- Only one url_slug element allowed per content type
|
|
48
|
+
- To remove content groups: set ALL elements' content_group to null AND remove ALL groups in one request
|
|
49
|
+
- URL slug with snippet: add snippet element first, then url_slug with depends_on reference
|
|
50
|
+
|
|
51
|
+
## Snippet Specifics
|
|
52
|
+
- Cannot contain: content_groups, snippet, or url_slug elements
|
|
53
|
+
`;
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
export const propertyBasedPatchGuide = `
|
|
2
|
+
# Patch Guide: Spaces & Languages
|
|
3
|
+
|
|
4
|
+
## Addressing
|
|
5
|
+
Use 'property_name' to specify which property to update. Only 'replace' operation is supported.
|
|
6
|
+
|
|
7
|
+
## Space Operations
|
|
8
|
+
|
|
9
|
+
**replace** - Update space properties
|
|
10
|
+
\`\`\`json
|
|
11
|
+
{ "op": "replace", "property_name": "name", "value": "New Space Name" }
|
|
12
|
+
{ "op": "replace", "property_name": "codename", "value": "new_codename" }
|
|
13
|
+
{ "op": "replace", "property_name": "collections", "value": [{ "id": "{uuid}" }, { "codename": "collection" }] }
|
|
14
|
+
\`\`\`
|
|
15
|
+
|
|
16
|
+
Available properties: name, codename, collections
|
|
17
|
+
|
|
18
|
+
## Language Operations
|
|
19
|
+
|
|
20
|
+
**replace** - Update language properties
|
|
21
|
+
\`\`\`json
|
|
22
|
+
{ "op": "replace", "property_name": "name", "value": "New Language Name" }
|
|
23
|
+
{ "op": "replace", "property_name": "codename", "value": "new_codename" }
|
|
24
|
+
{ "op": "replace", "property_name": "is_active", "value": true }
|
|
25
|
+
{ "op": "replace", "property_name": "fallback_language", "value": { "codename": "en-US" } }
|
|
26
|
+
\`\`\`
|
|
27
|
+
|
|
28
|
+
Available properties: name, codename, is_active, fallback_language
|
|
29
|
+
|
|
30
|
+
## Critical Rule for Languages
|
|
31
|
+
If a language is deactivated, you must activate it first before making other changes:
|
|
32
|
+
\`\`\`json
|
|
33
|
+
[
|
|
34
|
+
{ "op": "replace", "property_name": "is_active", "value": true },
|
|
35
|
+
{ "op": "replace", "property_name": "name", "value": "New Name" }
|
|
36
|
+
]
|
|
37
|
+
\`\`\`
|
|
38
|
+
|
|
39
|
+
## General Rules
|
|
40
|
+
- external_id cannot be modified after creation
|
|
41
|
+
- Operations are applied in order
|
|
42
|
+
`;
|
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
export const referenceBasedPatchGuide = `
|
|
2
|
+
# Patch Guide: Taxonomy, Collections & Asset Folders
|
|
3
|
+
|
|
4
|
+
## Addressing
|
|
5
|
+
Use 'reference' objects: { id: "{uuid}" } or { codename: "codename" }
|
|
6
|
+
|
|
7
|
+
## Taxonomy Group Operations
|
|
8
|
+
|
|
9
|
+
**addInto** - Add terms
|
|
10
|
+
\`\`\`json
|
|
11
|
+
{ "op": "addInto", "value": { "name": "Term" } }
|
|
12
|
+
{ "op": "addInto", "reference": { "id": "{parent-uuid}" }, "value": { "name": "Child Term" } }
|
|
13
|
+
{ "op": "addInto", "value": { "name": "Term" }, "before": { "id": "{uuid}" } }
|
|
14
|
+
{ "op": "addInto", "value": { "name": "Term" }, "after": { "id": "{uuid}" } }
|
|
15
|
+
\`\`\`
|
|
16
|
+
|
|
17
|
+
**move** - Reorder or nest terms (before/after/under are mutually exclusive)
|
|
18
|
+
\`\`\`json
|
|
19
|
+
{ "op": "move", "reference": { "id": "{uuid}" }, "before": { "id": "{uuid}" } }
|
|
20
|
+
{ "op": "move", "reference": { "id": "{uuid}" }, "after": { "id": "{uuid}" } }
|
|
21
|
+
{ "op": "move", "reference": { "id": "{uuid}" }, "under": { "id": "{parent-uuid}" } }
|
|
22
|
+
\`\`\`
|
|
23
|
+
|
|
24
|
+
**remove** - Delete terms
|
|
25
|
+
\`\`\`json
|
|
26
|
+
{ "op": "remove", "reference": { "id": "{uuid}" } }
|
|
27
|
+
\`\`\`
|
|
28
|
+
|
|
29
|
+
**replace** - Update properties
|
|
30
|
+
\`\`\`json
|
|
31
|
+
{ "op": "replace", "property_name": "name", "value": "New Group Name" }
|
|
32
|
+
{ "op": "replace", "reference": { "id": "{uuid}" }, "property_name": "name", "value": "New Term Name" }
|
|
33
|
+
{ "op": "replace", "reference": { "id": "{uuid}" }, "property_name": "terms", "value": [] }
|
|
34
|
+
\`\`\`
|
|
35
|
+
|
|
36
|
+
## Collection Operations
|
|
37
|
+
|
|
38
|
+
**addInto** - Add collections
|
|
39
|
+
\`\`\`json
|
|
40
|
+
{ "op": "addInto", "value": { "name": "Collection", "codename": "collection" } }
|
|
41
|
+
{ "op": "addInto", "value": { "name": "Collection" }, "before": { "id": "{uuid}" } }
|
|
42
|
+
{ "op": "addInto", "value": { "name": "Collection" }, "after": { "id": "{uuid}" } }
|
|
43
|
+
\`\`\`
|
|
44
|
+
|
|
45
|
+
**move** - Reorder collections
|
|
46
|
+
\`\`\`json
|
|
47
|
+
{ "op": "move", "reference": { "id": "{uuid}" }, "before": { "id": "{uuid}" } }
|
|
48
|
+
{ "op": "move", "reference": { "id": "{uuid}" }, "after": { "id": "{uuid}" } }
|
|
49
|
+
\`\`\`
|
|
50
|
+
|
|
51
|
+
**remove** - Delete empty collections only
|
|
52
|
+
\`\`\`json
|
|
53
|
+
{ "op": "remove", "reference": { "id": "{uuid}" } }
|
|
54
|
+
\`\`\`
|
|
55
|
+
|
|
56
|
+
**replace** - Rename collections
|
|
57
|
+
\`\`\`json
|
|
58
|
+
{ "op": "replace", "reference": { "id": "{uuid}" }, "property_name": "name", "value": "New Name" }
|
|
59
|
+
\`\`\`
|
|
60
|
+
|
|
61
|
+
## Asset Folder Operations
|
|
62
|
+
|
|
63
|
+
**addInto** - Add folders
|
|
64
|
+
\`\`\`json
|
|
65
|
+
{ "op": "addInto", "value": { "name": "Folder" } }
|
|
66
|
+
{ "op": "addInto", "reference": { "id": "{parent-uuid}" }, "value": { "name": "Subfolder" } }
|
|
67
|
+
{ "op": "addInto", "value": { "name": "Folder" }, "before": { "id": "{uuid}" } }
|
|
68
|
+
\`\`\`
|
|
69
|
+
|
|
70
|
+
**rename** - Rename folders (not 'replace')
|
|
71
|
+
\`\`\`json
|
|
72
|
+
{ "op": "rename", "reference": { "id": "{uuid}" }, "value": "New Folder Name" }
|
|
73
|
+
\`\`\`
|
|
74
|
+
|
|
75
|
+
**remove** - Delete folders
|
|
76
|
+
\`\`\`json
|
|
77
|
+
{ "op": "remove", "reference": { "id": "{uuid}" } }
|
|
78
|
+
\`\`\`
|
|
79
|
+
|
|
80
|
+
Note: Asset folders do not support 'move' operation.
|
|
81
|
+
|
|
82
|
+
## General Rules
|
|
83
|
+
- Always fetch the entity first to get current IDs
|
|
84
|
+
- external_id cannot be modified after creation
|
|
85
|
+
- Operations are applied in order
|
|
86
|
+
`;
|
|
@@ -0,0 +1,20 @@
|
|
|
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("delete-space-mapi", "Delete Kontent.ai space by ID", {
|
|
7
|
+
id: z.guid(),
|
|
8
|
+
}, async ({ id }, { authInfo: { token, clientId } = {} }) => {
|
|
9
|
+
const client = createMapiClient(clientId, token);
|
|
10
|
+
try {
|
|
11
|
+
await client.deleteSpace().bySpaceId(id).toPromise();
|
|
12
|
+
return createMcpToolSuccessResponse({
|
|
13
|
+
message: `Space '${id}' deleted successfully`,
|
|
14
|
+
});
|
|
15
|
+
}
|
|
16
|
+
catch (error) {
|
|
17
|
+
return handleMcpToolError(error, "Space Deletion");
|
|
18
|
+
}
|
|
19
|
+
});
|
|
20
|
+
};
|
|
@@ -0,0 +1,20 @@
|
|
|
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("delete-taxonomy-group-mapi", "Delete Kontent.ai taxonomy group by ID", {
|
|
7
|
+
id: z.guid(),
|
|
8
|
+
}, async ({ id }, { authInfo: { token, clientId } = {} }) => {
|
|
9
|
+
const client = createMapiClient(clientId, token);
|
|
10
|
+
try {
|
|
11
|
+
await client.deleteTaxonomy().byTaxonomyId(id).toPromise();
|
|
12
|
+
return createMcpToolSuccessResponse({
|
|
13
|
+
message: `Taxonomy group '${id}' deleted successfully`,
|
|
14
|
+
});
|
|
15
|
+
}
|
|
16
|
+
catch (error) {
|
|
17
|
+
return handleMcpToolError(error, "Taxonomy Group Deletion");
|
|
18
|
+
}
|
|
19
|
+
});
|
|
20
|
+
};
|
|
@@ -0,0 +1,23 @@
|
|
|
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("delete-type-snippet-mapi", "Delete Kontent.ai content type snippet by codename", {
|
|
7
|
+
codename: z.string(),
|
|
8
|
+
}, async ({ codename }, { authInfo: { token, clientId } = {} }) => {
|
|
9
|
+
const client = createMapiClient(clientId, token);
|
|
10
|
+
try {
|
|
11
|
+
await client
|
|
12
|
+
.deleteContentTypeSnippet()
|
|
13
|
+
.byTypeCodename(codename)
|
|
14
|
+
.toPromise();
|
|
15
|
+
return createMcpToolSuccessResponse({
|
|
16
|
+
message: `Content type snippet '${codename}' deleted successfully`,
|
|
17
|
+
});
|
|
18
|
+
}
|
|
19
|
+
catch (error) {
|
|
20
|
+
return handleMcpToolError(error, "Content Type Snippet Deletion");
|
|
21
|
+
}
|
|
22
|
+
});
|
|
23
|
+
};
|
|
@@ -1,9 +1,37 @@
|
|
|
1
|
+
import { z } from "zod";
|
|
1
2
|
import { createMcpToolSuccessResponse } from "../utils/responseHelper.js";
|
|
2
|
-
import {
|
|
3
|
+
import { pathBasedPatchGuide } from "./context/patch-guide-path-based.js";
|
|
4
|
+
import { propertyBasedPatchGuide } from "./context/patch-guide-property-based.js";
|
|
5
|
+
import { referenceBasedPatchGuide } from "./context/patch-guide-reference-based.js";
|
|
6
|
+
const entityTypeSchema = z.enum([
|
|
7
|
+
"content-type",
|
|
8
|
+
"snippet",
|
|
9
|
+
"taxonomy",
|
|
10
|
+
"collection",
|
|
11
|
+
"asset-folder",
|
|
12
|
+
"space",
|
|
13
|
+
"language",
|
|
14
|
+
]);
|
|
15
|
+
const getGuideForEntity = (entityType) => {
|
|
16
|
+
switch (entityType) {
|
|
17
|
+
case "content-type":
|
|
18
|
+
case "snippet":
|
|
19
|
+
return pathBasedPatchGuide;
|
|
20
|
+
case "taxonomy":
|
|
21
|
+
case "collection":
|
|
22
|
+
case "asset-folder":
|
|
23
|
+
return referenceBasedPatchGuide;
|
|
24
|
+
case "space":
|
|
25
|
+
case "language":
|
|
26
|
+
return propertyBasedPatchGuide;
|
|
27
|
+
}
|
|
28
|
+
};
|
|
3
29
|
export const registerTool = (server) => {
|
|
4
|
-
server.tool("get-patch-guide", "REQUIRED before any patch operation. Get
|
|
30
|
+
server.tool("get-patch-guide", "REQUIRED before any patch operation. Get patch operations guide for Kontent.ai Management API.", {
|
|
31
|
+
entityType: entityTypeSchema.describe("Entity type to get patch guide for: content-type, snippet, taxonomy, collection, asset-folder, space, language"),
|
|
32
|
+
}, async ({ entityType }) => {
|
|
5
33
|
try {
|
|
6
|
-
return createMcpToolSuccessResponse(
|
|
34
|
+
return createMcpToolSuccessResponse(getGuideForEntity(entityType));
|
|
7
35
|
}
|
|
8
36
|
catch (error) {
|
|
9
37
|
throw new Error(`Failed to read patch guide: ${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-taxonomy-group-mapi", "Get Kontent.ai taxonomy group.
|
|
7
|
-
id: z.
|
|
6
|
+
server.tool("get-taxonomy-group-mapi", "Get Kontent.ai taxonomy group. Taxonomy groups are hierarchical with tree-structured terms that can be nested to any depth for flexible content categorization.", {
|
|
7
|
+
id: z.guid(),
|
|
8
8
|
}, async ({ id }, { authInfo: { token, clientId } = {} }) => {
|
|
9
9
|
const client = createMapiClient(clientId, token);
|
|
10
10
|
try {
|
|
@@ -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-asset-folders-mapi", "List all Kontent.ai asset folders", {}, async (_params, { authInfo: { token, clientId } = {} }) => {
|
|
6
|
+
const client = createMapiClient(clientId, token);
|
|
7
|
+
try {
|
|
8
|
+
const response = await client.listAssetFolders().toPromise();
|
|
9
|
+
return createMcpToolSuccessResponse(response.rawData);
|
|
10
|
+
}
|
|
11
|
+
catch (error) {
|
|
12
|
+
return handleMcpToolError(error, "Asset Folders Listing");
|
|
13
|
+
}
|
|
14
|
+
});
|
|
15
|
+
};
|
|
@@ -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-spaces-mapi", "
|
|
5
|
+
server.tool("list-spaces-mapi", "List all Kontent.ai spaces. Spaces provide channel-specific context for managing multiple websites/channels. Each space has its own domain and preview URLs; collections connect to spaces to organize content per channel.", {}, async (_, { authInfo: { token, clientId } = {} }) => {
|
|
6
6
|
const client = createMapiClient(clientId, token);
|
|
7
7
|
try {
|
|
8
8
|
const response = await client.listSpaces().toPromise();
|
|
@@ -3,7 +3,7 @@ import { listTaxonomyGroupsSchema } from "../schemas/listSchemas.js";
|
|
|
3
3
|
import { handleMcpToolError } from "../utils/errorHandler.js";
|
|
4
4
|
import { createMcpToolSuccessResponse } from "../utils/responseHelper.js";
|
|
5
5
|
export const registerTool = (server) => {
|
|
6
|
-
server.tool("list-taxonomy-groups-mapi", "
|
|
6
|
+
server.tool("list-taxonomy-groups-mapi", "List all Kontent.ai taxonomy groups (paginated). Taxonomy groups are hierarchical with tree-structured terms that can be nested to any depth for flexible content categorization.", listTaxonomyGroupsSchema.shape, async ({ continuation_token }, { authInfo: { token, clientId } = {} }) => {
|
|
7
7
|
const client = createMapiClient(clientId, token);
|
|
8
8
|
try {
|
|
9
9
|
const query = client.listTaxonomies();
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import { createMapiClient } from "../clients/kontentClients.js";
|
|
2
|
+
import { assetFolderPatchOperationsSchema } from "../schemas/assetFolderSchemas.js";
|
|
3
|
+
import { handleMcpToolError } from "../utils/errorHandler.js";
|
|
4
|
+
import { createMcpToolSuccessResponse } from "../utils/responseHelper.js";
|
|
5
|
+
export const registerTool = (server) => {
|
|
6
|
+
server.tool("patch-asset-folders-mapi", "Modify Kontent.ai asset folders using patch operations (addInto, rename, remove). Call get-patch-guide first for operations reference.", {
|
|
7
|
+
operations: assetFolderPatchOperationsSchema,
|
|
8
|
+
}, async ({ operations }, { authInfo: { token, clientId } = {} }) => {
|
|
9
|
+
const client = createMapiClient(clientId, token);
|
|
10
|
+
try {
|
|
11
|
+
const response = await client
|
|
12
|
+
.modifyAssetFolders()
|
|
13
|
+
.withData(operations)
|
|
14
|
+
.toPromise();
|
|
15
|
+
return createMcpToolSuccessResponse({
|
|
16
|
+
message: `Asset folders modified with ${operations.length} operation(s)`,
|
|
17
|
+
folders: response.rawData,
|
|
18
|
+
appliedOperations: operations,
|
|
19
|
+
});
|
|
20
|
+
}
|
|
21
|
+
catch (error) {
|
|
22
|
+
return handleMcpToolError(error, "Asset Folders Modification");
|
|
23
|
+
}
|
|
24
|
+
});
|
|
25
|
+
};
|
|
@@ -4,20 +4,24 @@ import { patchOperationsSchema } from "../schemas/patchSchemas/contentTypePatchS
|
|
|
4
4
|
import { handleMcpToolError } from "../utils/errorHandler.js";
|
|
5
5
|
import { createMcpToolSuccessResponse } from "../utils/responseHelper.js";
|
|
6
6
|
export const registerTool = (server) => {
|
|
7
|
-
server.tool("patch-content-type-mapi", "Update Kontent.ai content type using
|
|
8
|
-
|
|
9
|
-
operations: patchOperationsSchema.describe(
|
|
10
|
-
|
|
7
|
+
server.tool("patch-content-type-mapi", "Update Kontent.ai content type using patch operations. Call get-patch-guide first for operations reference.", {
|
|
8
|
+
id: z.guid(),
|
|
9
|
+
operations: patchOperationsSchema.describe(`Patch operations array. CRITICAL: Always call get-type-mapi first.
|
|
10
|
+
- Use addInto/remove for arrays, replace for primitives/objects
|
|
11
|
+
- Only one url_slug element allowed per content type
|
|
12
|
+
- To remove content groups: set ALL elements' content_group to null AND remove ALL groups in one request
|
|
13
|
+
- URL slug with snippet: add snippet element first, then url_slug with depends_on reference`),
|
|
14
|
+
}, async ({ id, operations }, { authInfo: { token, clientId } = {} }) => {
|
|
11
15
|
const client = createMapiClient(clientId, token);
|
|
12
16
|
try {
|
|
13
17
|
// Apply patch operations using the modifyContentType method
|
|
14
18
|
const response = await client
|
|
15
19
|
.modifyContentType()
|
|
16
|
-
.
|
|
20
|
+
.byTypeId(id)
|
|
17
21
|
.withData(operations)
|
|
18
22
|
.toPromise();
|
|
19
23
|
return createMcpToolSuccessResponse({
|
|
20
|
-
message: `Content type
|
|
24
|
+
message: `Content type updated successfully with ${operations.length} operation(s)`,
|
|
21
25
|
contentType: response.rawData,
|
|
22
26
|
appliedOperations: operations,
|
|
23
27
|
});
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import { z } from "zod";
|
|
2
|
+
import { createMapiClient } from "../clients/kontentClients.js";
|
|
3
|
+
import { spacePatchOperationsSchema } from "../schemas/spaceSchemas.js";
|
|
4
|
+
import { handleMcpToolError } from "../utils/errorHandler.js";
|
|
5
|
+
import { createMcpToolSuccessResponse } from "../utils/responseHelper.js";
|
|
6
|
+
export const registerTool = (server) => {
|
|
7
|
+
server.tool("patch-space-mapi", "Patch Kontent.ai space using replace operations. Call get-patch-guide first for operations reference.", {
|
|
8
|
+
id: z.guid(),
|
|
9
|
+
operations: spacePatchOperationsSchema,
|
|
10
|
+
}, async ({ id, operations }, { authInfo: { token, clientId } = {} }) => {
|
|
11
|
+
const client = createMapiClient(clientId, token);
|
|
12
|
+
try {
|
|
13
|
+
const response = await client
|
|
14
|
+
.modifySpace()
|
|
15
|
+
.bySpaceId(id)
|
|
16
|
+
.withData(operations)
|
|
17
|
+
.toPromise();
|
|
18
|
+
return createMcpToolSuccessResponse({
|
|
19
|
+
message: `Space updated successfully with ${operations.length} operation(s)`,
|
|
20
|
+
space: response.rawData,
|
|
21
|
+
appliedOperations: operations,
|
|
22
|
+
});
|
|
23
|
+
}
|
|
24
|
+
catch (error) {
|
|
25
|
+
return handleMcpToolError(error, "Space Modification");
|
|
26
|
+
}
|
|
27
|
+
});
|
|
28
|
+
};
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import { z } from "zod";
|
|
2
|
+
import { createMapiClient } from "../clients/kontentClients.js";
|
|
3
|
+
import { taxonomyPatchOperationsSchema } from "../schemas/patchSchemas/taxonomyPatchSchemas.js";
|
|
4
|
+
import { handleMcpToolError } from "../utils/errorHandler.js";
|
|
5
|
+
import { createMcpToolSuccessResponse } from "../utils/responseHelper.js";
|
|
6
|
+
export const registerTool = (server) => {
|
|
7
|
+
server.tool("patch-taxonomy-group-mapi", "Update Kontent.ai taxonomy group using patch operations (addInto, move, remove, replace). Call get-patch-guide first for operations reference.", {
|
|
8
|
+
id: z.guid(),
|
|
9
|
+
operations: taxonomyPatchOperationsSchema.describe("Patch operations array. Call get-taxonomy-group-mapi first. Use addInto to add terms (with optional reference for parent), move to reorder/nest terms (before/after/under - mutually exclusive), remove to delete terms, replace for name/codename/terms."),
|
|
10
|
+
}, async ({ id, operations }, { authInfo: { token, clientId } = {} }) => {
|
|
11
|
+
const client = createMapiClient(clientId, token);
|
|
12
|
+
try {
|
|
13
|
+
const response = await client
|
|
14
|
+
.modifyTaxonomy()
|
|
15
|
+
.byTaxonomyId(id)
|
|
16
|
+
.withData(operations)
|
|
17
|
+
.toPromise();
|
|
18
|
+
return createMcpToolSuccessResponse({
|
|
19
|
+
message: `Taxonomy group updated with ${operations.length} operation(s)`,
|
|
20
|
+
taxonomyGroup: response.rawData,
|
|
21
|
+
appliedOperations: operations,
|
|
22
|
+
});
|
|
23
|
+
}
|
|
24
|
+
catch (error) {
|
|
25
|
+
return handleMcpToolError(error, "Taxonomy Group Patch");
|
|
26
|
+
}
|
|
27
|
+
});
|
|
28
|
+
};
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import { z } from "zod";
|
|
2
|
+
import { createMapiClient } from "../clients/kontentClients.js";
|
|
3
|
+
import { snippetPatchOperationsSchema } from "../schemas/patchSchemas/snippetPatchSchemas.js";
|
|
4
|
+
import { handleMcpToolError } from "../utils/errorHandler.js";
|
|
5
|
+
import { createMcpToolSuccessResponse } from "../utils/responseHelper.js";
|
|
6
|
+
export const registerTool = (server) => {
|
|
7
|
+
server.tool("patch-type-snippet-mapi", "Update Kontent.ai content type snippet using patch operations (move, addInto, remove, replace). Call get-patch-guide first for operations reference.", {
|
|
8
|
+
id: z.guid(),
|
|
9
|
+
operations: snippetPatchOperationsSchema.describe(`Patch operations array. CRITICAL: Always call get-type-snippet-mapi first.
|
|
10
|
+
- Use addInto/remove for arrays, replace for primitives/objects
|
|
11
|
+
- Snippets cannot contain: content_groups, snippet, or url_slug elements`),
|
|
12
|
+
}, async ({ id, operations }, { authInfo: { token, clientId } = {} }) => {
|
|
13
|
+
const client = createMapiClient(clientId, token);
|
|
14
|
+
try {
|
|
15
|
+
const response = await client
|
|
16
|
+
.modifyContentTypeSnippet()
|
|
17
|
+
.byTypeId(id)
|
|
18
|
+
.withData(operations)
|
|
19
|
+
.toPromise();
|
|
20
|
+
return createMcpToolSuccessResponse({
|
|
21
|
+
message: `Content type snippet updated successfully with ${operations.length} operation(s)`,
|
|
22
|
+
snippet: response.rawData,
|
|
23
|
+
appliedOperations: operations,
|
|
24
|
+
});
|
|
25
|
+
}
|
|
26
|
+
catch (error) {
|
|
27
|
+
return handleMcpToolError(error, "Content Type Snippet Patch");
|
|
28
|
+
}
|
|
29
|
+
});
|
|
30
|
+
};
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import { z } from "zod";
|
|
2
|
+
import { createMapiClient } from "../clients/kontentClients.js";
|
|
3
|
+
import { updateAssetDataSchema } from "../schemas/assetSchemas.js";
|
|
4
|
+
import { handleMcpToolError } from "../utils/errorHandler.js";
|
|
5
|
+
import { createMcpToolSuccessResponse } from "../utils/responseHelper.js";
|
|
6
|
+
export const registerTool = (server) => {
|
|
7
|
+
server.tool("update-asset-mapi", "Update Kontent.ai asset by ID", {
|
|
8
|
+
id: z.guid(),
|
|
9
|
+
data: updateAssetDataSchema,
|
|
10
|
+
}, async ({ id, data }, { authInfo: { token, clientId } = {} }) => {
|
|
11
|
+
const client = createMapiClient(clientId, token);
|
|
12
|
+
try {
|
|
13
|
+
const response = await client
|
|
14
|
+
.upsertAsset()
|
|
15
|
+
.byAssetId(id)
|
|
16
|
+
.withData(() => data)
|
|
17
|
+
.toPromise();
|
|
18
|
+
return createMcpToolSuccessResponse(response.rawData);
|
|
19
|
+
}
|
|
20
|
+
catch (error) {
|
|
21
|
+
return handleMcpToolError(error, "Asset Update");
|
|
22
|
+
}
|
|
23
|
+
});
|
|
24
|
+
};
|
package/package.json
CHANGED
|
@@ -1,58 +0,0 @@
|
|
|
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
|
-
`;
|