@kontent-ai/mcp-server 0.27.0 → 0.28.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +0 -29
- package/build/test/utils/responseHelper.spec.js +22 -556
- package/build/tools/bulk-get-items-variants-mapi.js +2 -2
- package/build/tools/delete-workflow-mapi.js +1 -1
- package/build/tools/get-latest-variant-mapi.js +2 -2
- package/build/tools/get-published-variant-mapi.js +2 -2
- package/build/tools/search-variants-mapi.js +2 -2
- package/build/tools/upsert-language-variant-mapi.js +2 -2
- package/build/utils/responseHelper.js +3 -107
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -23,7 +23,6 @@ Kontent.ai MCP Server implements the Model Context Protocol to connect your Kont
|
|
|
23
23
|
- [🔌 Quickstart](#-quickstart)
|
|
24
24
|
- [🛠️ Available Tools](#️-available-tools)
|
|
25
25
|
- [⚙️ Configuration](#️-configuration)
|
|
26
|
-
- [🔧 Response Optimization](#-response-optimization)
|
|
27
26
|
- [🚀 Transport Options](#-transport-options)
|
|
28
27
|
- [💻 Development](#-development)
|
|
29
28
|
- [🛠 Local Installation](#-local-installation)
|
|
@@ -177,34 +176,6 @@ For multi-tenant mode (Streamable HTTP only), the server accepts:
|
|
|
177
176
|
|
|
178
177
|
This mode allows a single server instance to handle requests for multiple Kontent.ai environments securely without requiring environment variables.
|
|
179
178
|
|
|
180
|
-
## 🔧 Response Optimization
|
|
181
|
-
|
|
182
|
-
The MCP server implements automatic token optimization to reduce AI model costs and improve performance:
|
|
183
|
-
|
|
184
|
-
### Token Reduction Strategy
|
|
185
|
-
|
|
186
|
-
The server automatically removes empty/default values from responses to reduce token usage. This includes:
|
|
187
|
-
|
|
188
|
-
- Null and undefined values
|
|
189
|
-
- Empty strings (`""`)
|
|
190
|
-
- Empty arrays (`[]`)
|
|
191
|
-
- Empty objects (`{}`)
|
|
192
|
-
- Rich text placeholders (`"<p><br/></p>"`)
|
|
193
|
-
- Elements with only an ID after empty value removal
|
|
194
|
-
|
|
195
|
-
### Impact on AI Agents
|
|
196
|
-
|
|
197
|
-
**Important for AI implementations**: When consuming responses from this MCP server:
|
|
198
|
-
|
|
199
|
-
1. **Missing properties indicate default values**, not missing data
|
|
200
|
-
2. Missing elements in variants have their type-specific defaults:
|
|
201
|
-
- Text elements: `""` (empty string)
|
|
202
|
-
- Rich text: `"<p><br/></p>"` (empty placeholder)
|
|
203
|
-
- Number/Date: `null`
|
|
204
|
-
- Custom elements: `null` (for value and searchable_value)
|
|
205
|
-
- Arrays (assets, taxonomy, etc.): `[]`
|
|
206
|
-
3. When creating/updating content, always send complete data
|
|
207
|
-
|
|
208
179
|
## 🚀 Transport Options
|
|
209
180
|
|
|
210
181
|
### 📟 STDIO Transport
|
|
@@ -1,320 +1,20 @@
|
|
|
1
1
|
import * as assert from "node:assert";
|
|
2
2
|
import { describe, it } from "mocha";
|
|
3
|
-
import { createMcpToolSuccessResponse
|
|
4
|
-
describe("isEmptyOrDefault", () => {
|
|
5
|
-
describe("should return true for empty/default values", () => {
|
|
6
|
-
it("returns true for null", () => {
|
|
7
|
-
assert.strictEqual(isEmptyOrDefault(null), true);
|
|
8
|
-
});
|
|
9
|
-
it("returns true for undefined", () => {
|
|
10
|
-
assert.strictEqual(isEmptyOrDefault(undefined), true);
|
|
11
|
-
});
|
|
12
|
-
it("returns true for empty string", () => {
|
|
13
|
-
assert.strictEqual(isEmptyOrDefault(""), true);
|
|
14
|
-
});
|
|
15
|
-
it("returns true for rich text empty paragraph", () => {
|
|
16
|
-
assert.strictEqual(isEmptyOrDefault("<p><br/></p>"), true);
|
|
17
|
-
});
|
|
18
|
-
it("returns true for empty array", () => {
|
|
19
|
-
assert.strictEqual(isEmptyOrDefault([]), true);
|
|
20
|
-
});
|
|
21
|
-
it("returns true for empty object", () => {
|
|
22
|
-
assert.strictEqual(isEmptyOrDefault({}), true);
|
|
23
|
-
});
|
|
24
|
-
});
|
|
25
|
-
describe("should return false for non-empty values", () => {
|
|
26
|
-
it("returns false for non-empty string", () => {
|
|
27
|
-
assert.strictEqual(isEmptyOrDefault("hello"), false);
|
|
28
|
-
});
|
|
29
|
-
it("returns false for number zero", () => {
|
|
30
|
-
assert.strictEqual(isEmptyOrDefault(0), false);
|
|
31
|
-
});
|
|
32
|
-
it("returns false for positive number", () => {
|
|
33
|
-
assert.strictEqual(isEmptyOrDefault(42), false);
|
|
34
|
-
});
|
|
35
|
-
it("returns false for boolean false", () => {
|
|
36
|
-
assert.strictEqual(isEmptyOrDefault(false), false);
|
|
37
|
-
});
|
|
38
|
-
it("returns false for boolean true", () => {
|
|
39
|
-
assert.strictEqual(isEmptyOrDefault(true), false);
|
|
40
|
-
});
|
|
41
|
-
it("returns false for non-empty array", () => {
|
|
42
|
-
assert.strictEqual(isEmptyOrDefault([1, 2, 3]), false);
|
|
43
|
-
});
|
|
44
|
-
it("returns false for non-empty object", () => {
|
|
45
|
-
assert.strictEqual(isEmptyOrDefault({ key: "value" }), false);
|
|
46
|
-
});
|
|
47
|
-
it("returns false for Date object", () => {
|
|
48
|
-
assert.strictEqual(isEmptyOrDefault(new Date()), false);
|
|
49
|
-
});
|
|
50
|
-
it("returns false for rich text with actual content", () => {
|
|
51
|
-
assert.strictEqual(isEmptyOrDefault("<p>Hello world</p>"), false);
|
|
52
|
-
});
|
|
53
|
-
});
|
|
54
|
-
});
|
|
55
|
-
describe("removeEmptyValues", () => {
|
|
56
|
-
describe("primitive values at root level - preserved as-is", () => {
|
|
57
|
-
it("returns null for null", () => {
|
|
58
|
-
assert.strictEqual(removeEmptyValues(null), null);
|
|
59
|
-
});
|
|
60
|
-
it("returns undefined for undefined", () => {
|
|
61
|
-
assert.strictEqual(removeEmptyValues(undefined), undefined);
|
|
62
|
-
});
|
|
63
|
-
it("preserves empty string at root level", () => {
|
|
64
|
-
assert.strictEqual(removeEmptyValues(""), "");
|
|
65
|
-
});
|
|
66
|
-
it("preserves rich text empty paragraph at root level", () => {
|
|
67
|
-
assert.strictEqual(removeEmptyValues("<p><br/></p>"), "<p><br/></p>");
|
|
68
|
-
});
|
|
69
|
-
it("preserves non-empty string", () => {
|
|
70
|
-
assert.strictEqual(removeEmptyValues("hello"), "hello");
|
|
71
|
-
});
|
|
72
|
-
it("preserves number zero", () => {
|
|
73
|
-
assert.strictEqual(removeEmptyValues(0), 0);
|
|
74
|
-
});
|
|
75
|
-
it("preserves boolean false", () => {
|
|
76
|
-
assert.strictEqual(removeEmptyValues(false), false);
|
|
77
|
-
});
|
|
78
|
-
});
|
|
79
|
-
describe("arrays", () => {
|
|
80
|
-
it("returns empty array for empty array at root level", () => {
|
|
81
|
-
assert.deepStrictEqual(removeEmptyValues([]), []);
|
|
82
|
-
});
|
|
83
|
-
it("removes empty values from array", () => {
|
|
84
|
-
assert.deepStrictEqual(removeEmptyValues([1, null, 2, "", 3]), [1, 2, 3]);
|
|
85
|
-
});
|
|
86
|
-
it("returns empty array when all array items are empty at root level", () => {
|
|
87
|
-
assert.deepStrictEqual(removeEmptyValues([null, "", [], {}]), []);
|
|
88
|
-
});
|
|
89
|
-
it("recursively cleans nested arrays", () => {
|
|
90
|
-
assert.deepStrictEqual(removeEmptyValues([1, [2, null, 3], [null, ""]]), [
|
|
91
|
-
1,
|
|
92
|
-
[2, 3],
|
|
93
|
-
]);
|
|
94
|
-
});
|
|
95
|
-
});
|
|
96
|
-
describe("objects", () => {
|
|
97
|
-
it("returns empty object for empty object at root level", () => {
|
|
98
|
-
assert.deepStrictEqual(removeEmptyValues({}), {});
|
|
99
|
-
});
|
|
100
|
-
it("removes null properties", () => {
|
|
101
|
-
assert.deepStrictEqual(removeEmptyValues({ a: 1, b: null }), { a: 1 });
|
|
102
|
-
});
|
|
103
|
-
it("removes undefined properties", () => {
|
|
104
|
-
assert.deepStrictEqual(removeEmptyValues({ a: 1, b: undefined }), {
|
|
105
|
-
a: 1,
|
|
106
|
-
});
|
|
107
|
-
});
|
|
108
|
-
it("removes empty string properties", () => {
|
|
109
|
-
assert.deepStrictEqual(removeEmptyValues({ a: 1, b: "" }), { a: 1 });
|
|
110
|
-
});
|
|
111
|
-
it("removes empty array properties", () => {
|
|
112
|
-
assert.deepStrictEqual(removeEmptyValues({ a: 1, b: [] }), { a: 1 });
|
|
113
|
-
});
|
|
114
|
-
it("removes empty object properties", () => {
|
|
115
|
-
assert.deepStrictEqual(removeEmptyValues({ a: 1, b: {} }), { a: 1 });
|
|
116
|
-
});
|
|
117
|
-
it("removes rich text empty paragraph properties", () => {
|
|
118
|
-
assert.deepStrictEqual(removeEmptyValues({ a: 1, b: "<p><br/></p>" }), {
|
|
119
|
-
a: 1,
|
|
120
|
-
});
|
|
121
|
-
});
|
|
122
|
-
it("returns empty object when all properties are empty at root level", () => {
|
|
123
|
-
assert.deepStrictEqual(removeEmptyValues({ a: null, b: "", c: [], d: {} }), {});
|
|
124
|
-
});
|
|
125
|
-
});
|
|
126
|
-
describe("nested structures", () => {
|
|
127
|
-
it("recursively cleans nested objects", () => {
|
|
128
|
-
const input = {
|
|
129
|
-
level1: {
|
|
130
|
-
level2: {
|
|
131
|
-
value: "keep",
|
|
132
|
-
empty: null,
|
|
133
|
-
},
|
|
134
|
-
emptyObj: {},
|
|
135
|
-
},
|
|
136
|
-
};
|
|
137
|
-
const expected = {
|
|
138
|
-
level1: {
|
|
139
|
-
level2: {
|
|
140
|
-
value: "keep",
|
|
141
|
-
},
|
|
142
|
-
},
|
|
143
|
-
};
|
|
144
|
-
assert.deepStrictEqual(removeEmptyValues(input), expected);
|
|
145
|
-
});
|
|
146
|
-
it("removes nested objects that become empty after cleaning", () => {
|
|
147
|
-
const input = {
|
|
148
|
-
keep: "value",
|
|
149
|
-
remove: {
|
|
150
|
-
nested: {
|
|
151
|
-
empty: null,
|
|
152
|
-
},
|
|
153
|
-
},
|
|
154
|
-
};
|
|
155
|
-
const expected = { keep: "value" };
|
|
156
|
-
assert.deepStrictEqual(removeEmptyValues(input), expected);
|
|
157
|
-
});
|
|
158
|
-
it("handles deeply nested structures", () => {
|
|
159
|
-
const input = {
|
|
160
|
-
a: {
|
|
161
|
-
b: {
|
|
162
|
-
c: {
|
|
163
|
-
d: {
|
|
164
|
-
value: "deep",
|
|
165
|
-
empty: "",
|
|
166
|
-
},
|
|
167
|
-
},
|
|
168
|
-
},
|
|
169
|
-
},
|
|
170
|
-
};
|
|
171
|
-
const expected = {
|
|
172
|
-
a: {
|
|
173
|
-
b: {
|
|
174
|
-
c: {
|
|
175
|
-
d: {
|
|
176
|
-
value: "deep",
|
|
177
|
-
},
|
|
178
|
-
},
|
|
179
|
-
},
|
|
180
|
-
},
|
|
181
|
-
};
|
|
182
|
-
assert.deepStrictEqual(removeEmptyValues(input), expected);
|
|
183
|
-
});
|
|
184
|
-
});
|
|
185
|
-
});
|
|
186
|
-
describe("removeEmptyElementsFromVariant", () => {
|
|
187
|
-
describe("non-object inputs", () => {
|
|
188
|
-
it("returns null for null", () => {
|
|
189
|
-
assert.strictEqual(removeEmptyElementsFromVariant(null), null);
|
|
190
|
-
});
|
|
191
|
-
it("returns undefined for undefined", () => {
|
|
192
|
-
assert.strictEqual(removeEmptyElementsFromVariant(undefined), undefined);
|
|
193
|
-
});
|
|
194
|
-
it("returns primitive values unchanged", () => {
|
|
195
|
-
assert.strictEqual(removeEmptyElementsFromVariant("string"), "string");
|
|
196
|
-
assert.strictEqual(removeEmptyElementsFromVariant(42), 42);
|
|
197
|
-
assert.strictEqual(removeEmptyElementsFromVariant(true), true);
|
|
198
|
-
});
|
|
199
|
-
});
|
|
200
|
-
describe("elements array filtering", () => {
|
|
201
|
-
it("removes elements with only element property", () => {
|
|
202
|
-
const input = {
|
|
203
|
-
elements: [{ element: { id: "id1" } }, { element: { id: "id2" } }],
|
|
204
|
-
};
|
|
205
|
-
const expected = {};
|
|
206
|
-
assert.deepStrictEqual(removeEmptyElementsFromVariant(input), expected);
|
|
207
|
-
});
|
|
208
|
-
it("keeps elements with value property", () => {
|
|
209
|
-
const input = {
|
|
210
|
-
elements: [{ element: { id: "id1" }, value: "content" }],
|
|
211
|
-
};
|
|
212
|
-
const expected = {
|
|
213
|
-
elements: [{ element: { id: "id1" }, value: "content" }],
|
|
214
|
-
};
|
|
215
|
-
assert.deepStrictEqual(removeEmptyElementsFromVariant(input), expected);
|
|
216
|
-
});
|
|
217
|
-
it("filters mixed elements array", () => {
|
|
218
|
-
const input = {
|
|
219
|
-
elements: [
|
|
220
|
-
{ element: { id: "id1" } },
|
|
221
|
-
{ element: { id: "id2" }, value: "keep" },
|
|
222
|
-
{ element: { id: "id3" } },
|
|
223
|
-
{ element: { id: "id4" }, components: [] },
|
|
224
|
-
],
|
|
225
|
-
};
|
|
226
|
-
const expected = {
|
|
227
|
-
elements: [
|
|
228
|
-
{ element: { id: "id2" }, value: "keep" },
|
|
229
|
-
{ element: { id: "id4" }, components: [] },
|
|
230
|
-
],
|
|
231
|
-
};
|
|
232
|
-
assert.deepStrictEqual(removeEmptyElementsFromVariant(input), expected);
|
|
233
|
-
});
|
|
234
|
-
it("keeps non-object elements in elements array", () => {
|
|
235
|
-
const input = {
|
|
236
|
-
elements: ["string", 42, { element: { id: "id1" } }],
|
|
237
|
-
};
|
|
238
|
-
const expected = {
|
|
239
|
-
elements: ["string", 42],
|
|
240
|
-
};
|
|
241
|
-
assert.deepStrictEqual(removeEmptyElementsFromVariant(input), expected);
|
|
242
|
-
});
|
|
243
|
-
});
|
|
244
|
-
describe("nested structures", () => {
|
|
245
|
-
it("processes nested objects with elements arrays", () => {
|
|
246
|
-
const input = {
|
|
247
|
-
item: {
|
|
248
|
-
elements: [{ element: { id: "id1" } }],
|
|
249
|
-
},
|
|
250
|
-
};
|
|
251
|
-
const expected = {
|
|
252
|
-
item: {},
|
|
253
|
-
};
|
|
254
|
-
assert.deepStrictEqual(removeEmptyElementsFromVariant(input), expected);
|
|
255
|
-
});
|
|
256
|
-
it("processes arrays of objects with elements", () => {
|
|
257
|
-
const input = {
|
|
258
|
-
variants: [
|
|
259
|
-
{ elements: [{ element: { id: "id1" } }] },
|
|
260
|
-
{ elements: [{ element: { id: "id2" }, value: "keep" }] },
|
|
261
|
-
],
|
|
262
|
-
};
|
|
263
|
-
const expected = {
|
|
264
|
-
variants: [
|
|
265
|
-
{},
|
|
266
|
-
{ elements: [{ element: { id: "id2" }, value: "keep" }] },
|
|
267
|
-
],
|
|
268
|
-
};
|
|
269
|
-
assert.deepStrictEqual(removeEmptyElementsFromVariant(input), expected);
|
|
270
|
-
});
|
|
271
|
-
});
|
|
272
|
-
describe("preserves other properties", () => {
|
|
273
|
-
it("keeps non-elements properties unchanged", () => {
|
|
274
|
-
const input = {
|
|
275
|
-
id: "123",
|
|
276
|
-
name: "Test",
|
|
277
|
-
elements: [{ element: { id: "id1" } }],
|
|
278
|
-
};
|
|
279
|
-
const expected = {
|
|
280
|
-
id: "123",
|
|
281
|
-
name: "Test",
|
|
282
|
-
};
|
|
283
|
-
assert.deepStrictEqual(removeEmptyElementsFromVariant(input), expected);
|
|
284
|
-
});
|
|
285
|
-
});
|
|
286
|
-
});
|
|
3
|
+
import { createMcpToolSuccessResponse } from "../../utils/responseHelper.js";
|
|
287
4
|
describe("createMcpToolSuccessResponse", () => {
|
|
288
|
-
it("returns correct structure", () => {
|
|
5
|
+
it("returns correct structure with text content", () => {
|
|
289
6
|
const response = createMcpToolSuccessResponse({ key: "value" });
|
|
290
7
|
assert.strictEqual(response.content.length, 1);
|
|
291
8
|
assert.strictEqual(response.content[0].type, "text");
|
|
292
9
|
});
|
|
293
|
-
it("
|
|
294
|
-
const input = {
|
|
295
|
-
id: "123",
|
|
296
|
-
name: null,
|
|
297
|
-
value: "",
|
|
298
|
-
items: [],
|
|
299
|
-
};
|
|
10
|
+
it("serializes object data as JSON string", () => {
|
|
11
|
+
const input = { id: "123", name: "Test" };
|
|
300
12
|
const response = createMcpToolSuccessResponse(input);
|
|
301
13
|
const parsed = JSON.parse(response.content[0].text);
|
|
302
|
-
assert.deepStrictEqual(parsed,
|
|
14
|
+
assert.deepStrictEqual(parsed, input);
|
|
303
15
|
});
|
|
304
|
-
it("
|
|
16
|
+
it("serializes nested data as JSON string", () => {
|
|
305
17
|
const input = {
|
|
306
|
-
contentType: {
|
|
307
|
-
id: "type-1",
|
|
308
|
-
name: "Article",
|
|
309
|
-
elements: [
|
|
310
|
-
{ id: "el-1", name: "Title", codename: "" },
|
|
311
|
-
{ id: "el-2", name: null, codename: "body" },
|
|
312
|
-
],
|
|
313
|
-
},
|
|
314
|
-
};
|
|
315
|
-
const response = createMcpToolSuccessResponse(input);
|
|
316
|
-
const parsed = JSON.parse(response.content[0].text);
|
|
317
|
-
assert.deepStrictEqual(parsed, {
|
|
318
18
|
contentType: {
|
|
319
19
|
id: "type-1",
|
|
320
20
|
name: "Article",
|
|
@@ -323,262 +23,28 @@ describe("createMcpToolSuccessResponse", () => {
|
|
|
323
23
|
{ id: "el-2", codename: "body" },
|
|
324
24
|
],
|
|
325
25
|
},
|
|
326
|
-
});
|
|
327
|
-
});
|
|
328
|
-
});
|
|
329
|
-
describe("createVariantMcpToolSuccessResponse", () => {
|
|
330
|
-
it("returns correct structure", () => {
|
|
331
|
-
const response = createVariantMcpToolSuccessResponse({ key: "value" });
|
|
332
|
-
assert.strictEqual(response.content.length, 1);
|
|
333
|
-
assert.strictEqual(response.content[0].type, "text");
|
|
334
|
-
});
|
|
335
|
-
it("removes empty values and empty elements", () => {
|
|
336
|
-
const input = {
|
|
337
|
-
item: { id: "item-1" },
|
|
338
|
-
language: { id: "lang-1" },
|
|
339
|
-
elements: [
|
|
340
|
-
{ element: { id: "el-1" }, value: "" },
|
|
341
|
-
{ element: { id: "el-2" }, value: "content" },
|
|
342
|
-
{ element: { id: "el-3" }, value: null },
|
|
343
|
-
],
|
|
344
|
-
};
|
|
345
|
-
const response = createVariantMcpToolSuccessResponse(input);
|
|
346
|
-
const parsed = JSON.parse(response.content[0].text);
|
|
347
|
-
assert.deepStrictEqual(parsed, {
|
|
348
|
-
item: { id: "item-1" },
|
|
349
|
-
language: { id: "lang-1" },
|
|
350
|
-
elements: [{ element: { id: "el-2" }, value: "content" }],
|
|
351
|
-
});
|
|
352
|
-
});
|
|
353
|
-
it("removes elements array when all elements become empty", () => {
|
|
354
|
-
const input = {
|
|
355
|
-
item: { id: "item-1" },
|
|
356
|
-
elements: [
|
|
357
|
-
{ element: { id: "el-1" }, value: "" },
|
|
358
|
-
{ element: { id: "el-2" }, value: null },
|
|
359
|
-
{ element: { id: "el-3" }, value: "<p><br/></p>" },
|
|
360
|
-
],
|
|
361
|
-
};
|
|
362
|
-
const response = createVariantMcpToolSuccessResponse(input);
|
|
363
|
-
const parsed = JSON.parse(response.content[0].text);
|
|
364
|
-
assert.deepStrictEqual(parsed, {
|
|
365
|
-
item: { id: "item-1" },
|
|
366
|
-
});
|
|
367
|
-
});
|
|
368
|
-
it("handles real-world variant response", () => {
|
|
369
|
-
const input = {
|
|
370
|
-
item: { id: "f4b3fc05-e988-4dae-9ac1-a94aba566474" },
|
|
371
|
-
language: { id: "d1f95fde-af02-b3b5-bd9e-f232311ccab8" },
|
|
372
|
-
last_modified: "2018-02-27T19:08:25.404Z",
|
|
373
|
-
workflow: {
|
|
374
|
-
workflow_identifier: { id: "00000000-0000-0000-0000-000000000000" },
|
|
375
|
-
step_identifier: { id: "c199950d-99f0-4983-b711-6c4c91624b22" },
|
|
376
|
-
},
|
|
377
|
-
elements: [
|
|
378
|
-
{ element: { id: "text-id" }, value: "Article Title" },
|
|
379
|
-
{ element: { id: "rich-id" }, value: "<p><br/></p>", components: [] },
|
|
380
|
-
{ element: { id: "number-id" }, value: null },
|
|
381
|
-
{ element: { id: "assets-id" }, value: [] },
|
|
382
|
-
{ element: { id: "taxonomy-id" }, value: [] },
|
|
383
|
-
],
|
|
384
26
|
};
|
|
385
|
-
const response =
|
|
27
|
+
const response = createMcpToolSuccessResponse(input);
|
|
386
28
|
const parsed = JSON.parse(response.content[0].text);
|
|
387
|
-
assert.deepStrictEqual(parsed,
|
|
388
|
-
item: { id: "f4b3fc05-e988-4dae-9ac1-a94aba566474" },
|
|
389
|
-
language: { id: "d1f95fde-af02-b3b5-bd9e-f232311ccab8" },
|
|
390
|
-
last_modified: "2018-02-27T19:08:25.404Z",
|
|
391
|
-
workflow: {
|
|
392
|
-
workflow_identifier: { id: "00000000-0000-0000-0000-000000000000" },
|
|
393
|
-
step_identifier: { id: "c199950d-99f0-4983-b711-6c4c91624b22" },
|
|
394
|
-
},
|
|
395
|
-
elements: [{ element: { id: "text-id" }, value: "Article Title" }],
|
|
396
|
-
});
|
|
29
|
+
assert.deepStrictEqual(parsed, input);
|
|
397
30
|
});
|
|
398
|
-
it("
|
|
399
|
-
const input = {
|
|
400
|
-
|
|
401
|
-
{
|
|
402
|
-
item: { id: "item-1" },
|
|
403
|
-
elements: [
|
|
404
|
-
{ element: { id: "el-1" }, value: "" },
|
|
405
|
-
{ element: { id: "el-2" }, value: "content" },
|
|
406
|
-
],
|
|
407
|
-
},
|
|
408
|
-
{
|
|
409
|
-
item: { id: "item-2" },
|
|
410
|
-
elements: [
|
|
411
|
-
{ element: { id: "el-1" }, value: null },
|
|
412
|
-
{ element: { id: "el-2" }, value: [] },
|
|
413
|
-
],
|
|
414
|
-
},
|
|
415
|
-
],
|
|
416
|
-
pagination: {
|
|
417
|
-
continuation_token: null,
|
|
418
|
-
next_page: "",
|
|
419
|
-
},
|
|
420
|
-
};
|
|
421
|
-
const response = createVariantMcpToolSuccessResponse(input);
|
|
31
|
+
it("serializes arrays as JSON string", () => {
|
|
32
|
+
const input = [{ id: "1" }, { id: "2" }];
|
|
33
|
+
const response = createMcpToolSuccessResponse(input);
|
|
422
34
|
const parsed = JSON.parse(response.content[0].text);
|
|
423
|
-
assert.deepStrictEqual(parsed,
|
|
424
|
-
variants: [
|
|
425
|
-
{
|
|
426
|
-
item: { id: "item-1" },
|
|
427
|
-
elements: [{ element: { id: "el-2" }, value: "content" }],
|
|
428
|
-
},
|
|
429
|
-
{
|
|
430
|
-
item: { id: "item-2" },
|
|
431
|
-
},
|
|
432
|
-
],
|
|
433
|
-
});
|
|
434
|
-
});
|
|
435
|
-
});
|
|
436
|
-
describe("variant with all empty elements", () => {
|
|
437
|
-
it("removes elements array entirely when all elements become empty", () => {
|
|
438
|
-
const input = {
|
|
439
|
-
elements: [
|
|
440
|
-
{ element: { id: "text-id" }, value: "" },
|
|
441
|
-
{ element: { id: "rich-id" }, value: "<p><br/></p>", components: [] },
|
|
442
|
-
{ element: { id: "number-id" }, value: null },
|
|
443
|
-
{ element: { id: "assets-id" }, value: [] },
|
|
444
|
-
],
|
|
445
|
-
};
|
|
446
|
-
const response = createVariantMcpToolSuccessResponse(input);
|
|
447
|
-
const result = JSON.parse(response.content[0].text);
|
|
448
|
-
assert.deepStrictEqual(result, {}, "All empty elements should be removed, resulting in an empty object");
|
|
35
|
+
assert.deepStrictEqual(parsed, input);
|
|
449
36
|
});
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
it("returns empty array when input is empty array", () => {
|
|
454
|
-
const response = createMcpToolSuccessResponse([]);
|
|
455
|
-
const parsed = JSON.parse(response.content[0].text);
|
|
456
|
-
assert.deepStrictEqual(parsed, []);
|
|
457
|
-
});
|
|
458
|
-
it("returns empty object when input is empty object", () => {
|
|
459
|
-
const response = createMcpToolSuccessResponse({});
|
|
460
|
-
const parsed = JSON.parse(response.content[0].text);
|
|
461
|
-
assert.deepStrictEqual(parsed, {});
|
|
462
|
-
});
|
|
463
|
-
it("returns empty object when all properties are removed", () => {
|
|
464
|
-
const input = { a: null, b: "", c: [] };
|
|
465
|
-
const response = createMcpToolSuccessResponse(input);
|
|
466
|
-
const parsed = JSON.parse(response.content[0].text);
|
|
467
|
-
assert.deepStrictEqual(parsed, {});
|
|
468
|
-
});
|
|
469
|
-
it("returns empty array when all array items are removed", () => {
|
|
470
|
-
const input = [null, "", [], {}];
|
|
471
|
-
const response = createMcpToolSuccessResponse(input);
|
|
472
|
-
const parsed = JSON.parse(response.content[0].text);
|
|
473
|
-
assert.deepStrictEqual(parsed, []);
|
|
474
|
-
});
|
|
475
|
-
it("returns valid JSON string (not undefined)", () => {
|
|
476
|
-
const response = createMcpToolSuccessResponse([]);
|
|
477
|
-
assert.strictEqual(typeof response.content[0].text, "string");
|
|
478
|
-
assert.doesNotThrow(() => JSON.parse(response.content[0].text));
|
|
479
|
-
});
|
|
480
|
-
it("handles filter-variants-like response with empty data array", () => {
|
|
481
|
-
const input = {
|
|
482
|
-
data: [],
|
|
483
|
-
pagination: {
|
|
484
|
-
continuation_token: null,
|
|
485
|
-
},
|
|
486
|
-
};
|
|
487
|
-
const response = createMcpToolSuccessResponse(input);
|
|
488
|
-
const parsed = JSON.parse(response.content[0].text);
|
|
489
|
-
assert.deepStrictEqual(parsed, {});
|
|
490
|
-
});
|
|
37
|
+
it("passes through string values without JSON encoding", () => {
|
|
38
|
+
const response = createMcpToolSuccessResponse("plain text");
|
|
39
|
+
assert.strictEqual(response.content[0].text, "plain text");
|
|
491
40
|
});
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
const parsed = JSON.parse(response.content[0].text);
|
|
496
|
-
assert.deepStrictEqual(parsed, []);
|
|
497
|
-
});
|
|
498
|
-
it("returns empty object when input is empty object", () => {
|
|
499
|
-
const response = createVariantMcpToolSuccessResponse({});
|
|
500
|
-
const parsed = JSON.parse(response.content[0].text);
|
|
501
|
-
assert.deepStrictEqual(parsed, {});
|
|
502
|
-
});
|
|
503
|
-
it("returns empty object when all properties are removed", () => {
|
|
504
|
-
const input = { a: null, b: "", c: [] };
|
|
505
|
-
const response = createVariantMcpToolSuccessResponse(input);
|
|
506
|
-
const parsed = JSON.parse(response.content[0].text);
|
|
507
|
-
assert.deepStrictEqual(parsed, {});
|
|
508
|
-
});
|
|
509
|
-
it("returns empty array when all array items are removed", () => {
|
|
510
|
-
const input = [null, "", [], {}];
|
|
511
|
-
const response = createVariantMcpToolSuccessResponse(input);
|
|
512
|
-
const parsed = JSON.parse(response.content[0].text);
|
|
513
|
-
assert.deepStrictEqual(parsed, []);
|
|
514
|
-
});
|
|
515
|
-
it("returns valid JSON string (not undefined)", () => {
|
|
516
|
-
const response = createVariantMcpToolSuccessResponse([]);
|
|
517
|
-
assert.strictEqual(typeof response.content[0].text, "string");
|
|
518
|
-
assert.doesNotThrow(() => JSON.parse(response.content[0].text));
|
|
519
|
-
});
|
|
520
|
-
it("handles filter-variants-like response with empty data array", () => {
|
|
521
|
-
const input = {
|
|
522
|
-
data: [],
|
|
523
|
-
pagination: {
|
|
524
|
-
continuation_token: null,
|
|
525
|
-
},
|
|
526
|
-
};
|
|
527
|
-
const response = createVariantMcpToolSuccessResponse(input);
|
|
528
|
-
const parsed = JSON.parse(response.content[0].text);
|
|
529
|
-
assert.deepStrictEqual(parsed, {});
|
|
530
|
-
});
|
|
531
|
-
it("handles filter-variants response with variants array becoming empty", () => {
|
|
532
|
-
const input = {
|
|
533
|
-
variants: [
|
|
534
|
-
{
|
|
535
|
-
elements: [
|
|
536
|
-
{ element: { id: "el-1" }, value: null },
|
|
537
|
-
{ element: { id: "el-2" }, value: "" },
|
|
538
|
-
],
|
|
539
|
-
},
|
|
540
|
-
],
|
|
541
|
-
pagination: {
|
|
542
|
-
continuation_token: null,
|
|
543
|
-
},
|
|
544
|
-
};
|
|
545
|
-
const response = createVariantMcpToolSuccessResponse(input);
|
|
546
|
-
const parsed = JSON.parse(response.content[0].text);
|
|
547
|
-
// variants array should still be present (with empty variant objects)
|
|
548
|
-
assert.ok(parsed.variants !== undefined);
|
|
549
|
-
assert.strictEqual(parsed.variants.length, 1);
|
|
550
|
-
});
|
|
551
|
-
});
|
|
552
|
-
});
|
|
553
|
-
describe("undefined input handling - MCP protocol compliance", () => {
|
|
554
|
-
describe("createMcpToolSuccessResponse", () => {
|
|
555
|
-
it("returns string when input is undefined", () => {
|
|
556
|
-
const response = createMcpToolSuccessResponse(undefined);
|
|
557
|
-
assert.strictEqual(typeof response.content[0].text, "string");
|
|
558
|
-
});
|
|
559
|
-
it("returns 'undefined' text when input is undefined", () => {
|
|
560
|
-
const response = createMcpToolSuccessResponse(undefined);
|
|
561
|
-
assert.strictEqual(response.content[0].text, "undefined");
|
|
562
|
-
});
|
|
563
|
-
it("returns valid JSON string when input is null", () => {
|
|
564
|
-
const response = createMcpToolSuccessResponse(null);
|
|
565
|
-
assert.strictEqual(typeof response.content[0].text, "string");
|
|
566
|
-
assert.doesNotThrow(() => JSON.parse(response.content[0].text));
|
|
567
|
-
});
|
|
41
|
+
it("returns 'undefined' text when input is undefined", () => {
|
|
42
|
+
const response = createMcpToolSuccessResponse(undefined);
|
|
43
|
+
assert.strictEqual(response.content[0].text, "undefined");
|
|
568
44
|
});
|
|
569
|
-
|
|
570
|
-
|
|
571
|
-
|
|
572
|
-
|
|
573
|
-
});
|
|
574
|
-
it("returns 'undefined' text when input is undefined", () => {
|
|
575
|
-
const response = createVariantMcpToolSuccessResponse(undefined);
|
|
576
|
-
assert.strictEqual(response.content[0].text, "undefined");
|
|
577
|
-
});
|
|
578
|
-
it("returns valid JSON string when input is null", () => {
|
|
579
|
-
const response = createVariantMcpToolSuccessResponse(null);
|
|
580
|
-
assert.strictEqual(typeof response.content[0].text, "string");
|
|
581
|
-
assert.doesNotThrow(() => JSON.parse(response.content[0].text));
|
|
582
|
-
});
|
|
45
|
+
it("returns valid JSON string when input is null", () => {
|
|
46
|
+
const response = createMcpToolSuccessResponse(null);
|
|
47
|
+
assert.strictEqual(typeof response.content[0].text, "string");
|
|
48
|
+
assert.doesNotThrow(() => JSON.parse(response.content[0].text));
|
|
583
49
|
});
|
|
584
50
|
});
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { createMapiClient } from "../clients/kontentClients.js";
|
|
2
2
|
import { bulkGetItemsWithVariantsSchema } from "../schemas/bulkGetItemsWithVariantsSchemas.js";
|
|
3
3
|
import { handleMcpToolError } from "../utils/errorHandler.js";
|
|
4
|
-
import {
|
|
4
|
+
import { createMcpToolSuccessResponse } from "../utils/responseHelper.js";
|
|
5
5
|
import { throwError } from "../utils/throwError.js";
|
|
6
6
|
export const registerTool = (server) => {
|
|
7
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 } = {} }) => {
|
|
@@ -17,7 +17,7 @@ export const registerTool = (server) => {
|
|
|
17
17
|
const response = await (continuation_token
|
|
18
18
|
? query.xContinuationToken(continuation_token)
|
|
19
19
|
: query).toPromise();
|
|
20
|
-
return
|
|
20
|
+
return createMcpToolSuccessResponse({
|
|
21
21
|
data: response.rawData.data,
|
|
22
22
|
pagination: {
|
|
23
23
|
continuation_token: response.data.pagination.continuationToken,
|
|
@@ -3,7 +3,7 @@ import { createMapiClient } from "../clients/kontentClients.js";
|
|
|
3
3
|
import { handleMcpToolError } from "../utils/errorHandler.js";
|
|
4
4
|
import { createMcpToolSuccessResponse } from "../utils/responseHelper.js";
|
|
5
5
|
export const registerTool = (server) => {
|
|
6
|
-
server.tool("delete-workflow-mapi", {
|
|
6
|
+
server.tool("delete-workflow-mapi", "Delete Kontent.ai workflow", {
|
|
7
7
|
id: z.guid().describe("Workflow ID"),
|
|
8
8
|
}, async ({ id }, { authInfo: { token, clientId } = {} }) => {
|
|
9
9
|
const client = createMapiClient(clientId, token);
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { z } from "zod";
|
|
2
2
|
import { createMapiClient } from "../clients/kontentClients.js";
|
|
3
3
|
import { handleMcpToolError } from "../utils/errorHandler.js";
|
|
4
|
-
import {
|
|
4
|
+
import { createMcpToolSuccessResponse } from "../utils/responseHelper.js";
|
|
5
5
|
export const registerTool = (server) => {
|
|
6
6
|
server.tool("get-latest-variant-mapi", "Get latest Kontent.ai language variant. Variants hold language-specific content; structure defined by content type and its snippets.", {
|
|
7
7
|
itemId: z.string().describe("Item ID"),
|
|
@@ -14,7 +14,7 @@ export const registerTool = (server) => {
|
|
|
14
14
|
.byItemId(itemId)
|
|
15
15
|
.byLanguageId(languageId)
|
|
16
16
|
.toPromise();
|
|
17
|
-
return
|
|
17
|
+
return createMcpToolSuccessResponse(response.rawData);
|
|
18
18
|
}
|
|
19
19
|
catch (error) {
|
|
20
20
|
return handleMcpToolError(error, "Latest Language Variant Retrieval");
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { z } from "zod";
|
|
2
2
|
import { createMapiClient } from "../clients/kontentClients.js";
|
|
3
3
|
import { handleMcpToolError } from "../utils/errorHandler.js";
|
|
4
|
-
import {
|
|
4
|
+
import { createMcpToolSuccessResponse } from "../utils/responseHelper.js";
|
|
5
5
|
export const registerTool = (server) => {
|
|
6
6
|
server.tool("get-published-variant-mapi", "Get published Kontent.ai language variant. Variants hold language-specific content; structure defined by content type and its snippets.", {
|
|
7
7
|
itemId: z.string().describe("Item ID"),
|
|
@@ -15,7 +15,7 @@ export const registerTool = (server) => {
|
|
|
15
15
|
.byLanguageId(languageId)
|
|
16
16
|
.published()
|
|
17
17
|
.toPromise();
|
|
18
|
-
return
|
|
18
|
+
return createMcpToolSuccessResponse(response.rawData);
|
|
19
19
|
}
|
|
20
20
|
catch (error) {
|
|
21
21
|
return handleMcpToolError(error, "Published Language Variant Retrieval");
|
|
@@ -2,7 +2,7 @@ import pRetry, { AbortError } from "p-retry";
|
|
|
2
2
|
import { createMapiClient } from "../clients/kontentClients.js";
|
|
3
3
|
import { searchOperationSchema } from "../schemas/searchOperationSchemas.js";
|
|
4
4
|
import { handleMcpToolError } from "../utils/errorHandler.js";
|
|
5
|
-
import { createMcpToolSuccessResponse
|
|
5
|
+
import { createMcpToolSuccessResponse } from "../utils/responseHelper.js";
|
|
6
6
|
import { throwError } from "../utils/throwError.js";
|
|
7
7
|
class OperationResultIncompleteError extends Error {
|
|
8
8
|
constructor() {
|
|
@@ -84,7 +84,7 @@ export const registerTool = (server) => {
|
|
|
84
84
|
maxTimeout: 10000,
|
|
85
85
|
factor: 1.5,
|
|
86
86
|
});
|
|
87
|
-
return
|
|
87
|
+
return createMcpToolSuccessResponse({
|
|
88
88
|
result: resultData,
|
|
89
89
|
});
|
|
90
90
|
}
|
|
@@ -2,7 +2,7 @@ import { z } from "zod";
|
|
|
2
2
|
import { createMapiClient } from "../clients/kontentClients.js";
|
|
3
3
|
import { languageVariantElementSchema } from "../schemas/contentItemSchemas.js";
|
|
4
4
|
import { handleMcpToolError } from "../utils/errorHandler.js";
|
|
5
|
-
import {
|
|
5
|
+
import { createMcpToolSuccessResponse } from "../utils/responseHelper.js";
|
|
6
6
|
export const registerTool = (server) => {
|
|
7
7
|
server.tool("upsert-language-variant-mapi", "Create or update Kontent.ai variant. Element values must fulfill limitations and guidelines defined in content type.", {
|
|
8
8
|
itemId: z.string().describe("Content item ID"),
|
|
@@ -28,7 +28,7 @@ export const registerTool = (server) => {
|
|
|
28
28
|
.byLanguageId(languageId)
|
|
29
29
|
.withData(() => data)
|
|
30
30
|
.toPromise();
|
|
31
|
-
return
|
|
31
|
+
return createMcpToolSuccessResponse(response.rawData);
|
|
32
32
|
}
|
|
33
33
|
catch (error) {
|
|
34
34
|
return handleMcpToolError(error, "Language Variant Upsert");
|
|
@@ -1,108 +1,13 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Utility for creating standardized MCP tool success responses
|
|
2
|
+
* Utility for creating standardized MCP tool success responses.
|
|
3
|
+
* Passes API data through to MCP response format without transformation.
|
|
3
4
|
*/
|
|
4
|
-
export function isEmptyOrDefault(value) {
|
|
5
|
-
if (value === null || value === undefined) {
|
|
6
|
-
return true;
|
|
7
|
-
}
|
|
8
|
-
if (typeof value === "string" && value === "") {
|
|
9
|
-
return true;
|
|
10
|
-
}
|
|
11
|
-
if (typeof value === "string" && value === "<p><br/></p>") {
|
|
12
|
-
return true;
|
|
13
|
-
}
|
|
14
|
-
if (Array.isArray(value) && value.length === 0) {
|
|
15
|
-
return true;
|
|
16
|
-
}
|
|
17
|
-
if (typeof value === "object" &&
|
|
18
|
-
!Array.isArray(value) &&
|
|
19
|
-
!(value instanceof Date) &&
|
|
20
|
-
!(value instanceof Function) &&
|
|
21
|
-
Object.keys(value).length === 0) {
|
|
22
|
-
return true;
|
|
23
|
-
}
|
|
24
|
-
return false;
|
|
25
|
-
}
|
|
26
|
-
export function removeEmptyElementsFromVariant(obj) {
|
|
27
|
-
if (obj === null || obj === undefined || typeof obj !== "object") {
|
|
28
|
-
return obj;
|
|
29
|
-
}
|
|
30
|
-
if (Array.isArray(obj)) {
|
|
31
|
-
return obj.map((item) => removeEmptyElementsFromVariant(item));
|
|
32
|
-
}
|
|
33
|
-
const result = {};
|
|
34
|
-
for (const [key, value] of Object.entries(obj)) {
|
|
35
|
-
if (key === "elements" && Array.isArray(value)) {
|
|
36
|
-
const filteredElements = value
|
|
37
|
-
.filter((element) => {
|
|
38
|
-
if (typeof element !== "object" || element === null) {
|
|
39
|
-
return true;
|
|
40
|
-
}
|
|
41
|
-
const keys = Object.keys(element);
|
|
42
|
-
return !(keys.length === 1 && keys[0] === "element");
|
|
43
|
-
})
|
|
44
|
-
.map((element) => removeEmptyElementsFromVariant(element));
|
|
45
|
-
if (filteredElements.length > 0) {
|
|
46
|
-
result[key] = filteredElements;
|
|
47
|
-
}
|
|
48
|
-
}
|
|
49
|
-
else {
|
|
50
|
-
const processedValue = removeEmptyElementsFromVariant(value);
|
|
51
|
-
if (processedValue !== undefined) {
|
|
52
|
-
result[key] = processedValue;
|
|
53
|
-
}
|
|
54
|
-
}
|
|
55
|
-
}
|
|
56
|
-
return result;
|
|
57
|
-
}
|
|
58
|
-
function removeEmptyValuesRecursive(obj) {
|
|
59
|
-
if (obj === null || obj === undefined) {
|
|
60
|
-
return undefined;
|
|
61
|
-
}
|
|
62
|
-
if (typeof obj !== "object") {
|
|
63
|
-
return isEmptyOrDefault(obj) ? undefined : obj;
|
|
64
|
-
}
|
|
65
|
-
if (Array.isArray(obj)) {
|
|
66
|
-
const cleaned = obj
|
|
67
|
-
.map((item) => removeEmptyValuesRecursive(item))
|
|
68
|
-
.filter((item) => item !== undefined);
|
|
69
|
-
return cleaned.length === 0 ? undefined : cleaned;
|
|
70
|
-
}
|
|
71
|
-
const cleaned = {};
|
|
72
|
-
for (const [key, value] of Object.entries(obj)) {
|
|
73
|
-
const cleanedValue = removeEmptyValuesRecursive(value);
|
|
74
|
-
if (cleanedValue !== undefined) {
|
|
75
|
-
cleaned[key] = cleanedValue;
|
|
76
|
-
}
|
|
77
|
-
}
|
|
78
|
-
const keys = Object.keys(cleaned);
|
|
79
|
-
return keys.length === 0 ? undefined : cleaned;
|
|
80
|
-
}
|
|
81
|
-
export function removeEmptyValues(obj) {
|
|
82
|
-
// At root level, preserve the structure even if empty
|
|
83
|
-
if (typeof obj !== "object" || obj === null) {
|
|
84
|
-
return obj;
|
|
85
|
-
}
|
|
86
|
-
if (Array.isArray(obj)) {
|
|
87
|
-
return obj
|
|
88
|
-
.map((item) => removeEmptyValuesRecursive(item))
|
|
89
|
-
.filter((item) => item !== undefined);
|
|
90
|
-
}
|
|
91
|
-
const cleaned = {};
|
|
92
|
-
for (const [key, value] of Object.entries(obj)) {
|
|
93
|
-
const cleanedValue = removeEmptyValuesRecursive(value);
|
|
94
|
-
if (cleanedValue !== undefined) {
|
|
95
|
-
cleaned[key] = cleanedValue;
|
|
96
|
-
}
|
|
97
|
-
}
|
|
98
|
-
return cleaned;
|
|
99
|
-
}
|
|
100
5
|
/**
|
|
101
6
|
* Converts data to MCP tool success response format.
|
|
102
7
|
* Handles undefined separately as JSON.stringify(undefined) returns undefined (not a string).
|
|
103
8
|
* Skips stringify for strings as they don't need JSON encoding for MCP text response.
|
|
104
9
|
*/
|
|
105
|
-
const
|
|
10
|
+
export const createMcpToolSuccessResponse = (data) => {
|
|
106
11
|
const text = data === undefined
|
|
107
12
|
? "undefined"
|
|
108
13
|
: typeof data === "string"
|
|
@@ -117,12 +22,3 @@ const toMcpSuccessResponse = (data) => {
|
|
|
117
22
|
],
|
|
118
23
|
};
|
|
119
24
|
};
|
|
120
|
-
export const createMcpToolSuccessResponse = (data) => {
|
|
121
|
-
const cleaned = removeEmptyValues(data);
|
|
122
|
-
return toMcpSuccessResponse(cleaned);
|
|
123
|
-
};
|
|
124
|
-
export const createVariantMcpToolSuccessResponse = (data) => {
|
|
125
|
-
const cleaned = removeEmptyValues(data);
|
|
126
|
-
const optimized = removeEmptyElementsFromVariant(cleaned);
|
|
127
|
-
return toMcpSuccessResponse(optimized);
|
|
128
|
-
};
|