@kontent-ai/mcp-server 0.19.0 → 0.21.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 +4 -1
- package/build/bin.js +9 -13
- package/build/clients/kontentClients.js +3 -3
- package/build/schemas/filterVariantSchemas.js +21 -10
- package/build/server.js +29 -29
- package/build/telemetry/applicationInsights.js +12 -8
- package/build/tools/add-content-item-mapi.js +2 -2
- package/build/tools/add-content-type-mapi.js +2 -2
- package/build/tools/add-content-type-snippet-mapi.js +2 -2
- package/build/tools/add-taxonomy-group-mapi.js +2 -2
- package/build/tools/change-variant-workflow-step-mapi.js +2 -2
- package/build/tools/create-variant-version-mapi.js +2 -2
- package/build/tools/delete-content-item-mapi.js +2 -2
- package/build/tools/delete-content-type-mapi.js +2 -2
- package/build/tools/delete-language-variant-mapi.js +2 -2
- package/build/tools/filter-variants-mapi.js +15 -18
- package/build/tools/get-asset-mapi.js +2 -2
- package/build/tools/get-item-mapi.js +2 -2
- package/build/tools/get-taxonomy-group-mapi.js +2 -2
- package/build/tools/get-type-mapi.js +2 -2
- package/build/tools/get-type-snippet-mapi.js +2 -2
- package/build/tools/get-variant-mapi.js +2 -2
- package/build/tools/list-assets-mapi.js +2 -2
- package/build/tools/list-content-type-snippets-mapi.js +2 -2
- package/build/tools/list-content-types-mapi.js +2 -2
- package/build/tools/list-languages-mapi.js +2 -2
- package/build/tools/list-taxonomy-groups-mapi.js +2 -2
- package/build/tools/list-workflows-mapi.js +2 -2
- package/build/tools/patch-content-type-mapi.js +2 -2
- package/build/tools/publish-variant-mapi.js +2 -2
- package/build/tools/search-variants-mapi.js +19 -17
- package/build/tools/unpublish-variant-mapi.js +2 -2
- package/build/tools/update-content-item-mapi.js +2 -2
- package/build/tools/upsert-language-variant-mapi.js +2 -2
- package/build/utils/errorHandler.js +13 -2
- package/package.json +4 -7
- package/build/config/appConfiguration.js +0 -51
package/README.md
CHANGED
|
@@ -91,7 +91,7 @@ npx @kontent-ai/mcp-server@latest shttp
|
|
|
91
91
|
* **upsert-language-variant-mapi** – Create or update Kontent.ai language variant of a content item via Management API. This adds actual content to the content item elements. When updating an existing variant, only the provided elements will be modified
|
|
92
92
|
* **create-variant-version-mapi** – Create new version of Kontent.ai language variant via Management API. This operation creates a new version of an existing language variant, useful for content versioning and creating new drafts from published content
|
|
93
93
|
* **delete-language-variant-mapi** – Delete Kontent.ai language variant from Management API
|
|
94
|
-
* **filter-variants-mapi** – Filter Kontent.ai language variants of content items using Management API. Use for exact keyword matching and finding specific terms in content. Supports full filtering capabilities (content types, workflow steps, taxonomies, etc.)
|
|
94
|
+
* **filter-variants-mapi** – Filter Kontent.ai language variants of content items using Management API. Use for exact keyword matching and finding specific terms in content. Supports full filtering capabilities (content types, workflow steps, taxonomies, etc.) and optionally includes full content of variants
|
|
95
95
|
* **search-variants-mapi** – AI-powered semantic search for finding content by meaning and concepts in a specific language variant. Use for: conceptual searches when you don't know exact keywords. Limited filtering options (variant ID only)
|
|
96
96
|
|
|
97
97
|
### Asset Management
|
|
@@ -123,6 +123,9 @@ For single-tenant mode, configure environment variables:
|
|
|
123
123
|
| KONTENT_API_KEY | Your Kontent.ai Management API key | ✅ |
|
|
124
124
|
| KONTENT_ENVIRONMENT_ID | Your environment ID | ✅ |
|
|
125
125
|
| PORT | Port for HTTP transport (defaults to 3001) | ❌ |
|
|
126
|
+
| appInsightsConnectionString | Application Insights connection string for telemetry | ❌ |
|
|
127
|
+
| projectLocation | Project location identifier for telemetry tracking | ❌ |
|
|
128
|
+
| manageApiUrl | Custom Management API base URL (for preview environments) | ❌ |
|
|
126
129
|
|
|
127
130
|
### Multi-Tenant Mode
|
|
128
131
|
|
package/build/bin.js
CHANGED
|
@@ -4,18 +4,17 @@ import { StreamableHTTPServerTransport } from "@modelcontextprotocol/sdk/server/
|
|
|
4
4
|
import "dotenv/config";
|
|
5
5
|
import express from "express";
|
|
6
6
|
import packageJson from "../package.json" with { type: "json" };
|
|
7
|
-
import { loadAppConfiguration, } from "./config/appConfiguration.js";
|
|
8
7
|
import { createServer } from "./server.js";
|
|
9
8
|
import { initializeApplicationInsights, trackException, trackServerStartup, } from "./telemetry/applicationInsights.js";
|
|
10
9
|
import { extractBearerToken } from "./utils/extractBearerToken.js";
|
|
11
10
|
import { isValidGuid } from "./utils/isValidGuid.js";
|
|
12
11
|
const version = packageJson.version;
|
|
13
|
-
async function startStreamableHTTP(
|
|
12
|
+
async function startStreamableHTTP() {
|
|
14
13
|
const app = express();
|
|
15
14
|
app.use(express.json());
|
|
16
15
|
app.post("/mcp", async (req, res) => {
|
|
17
16
|
try {
|
|
18
|
-
const { server } = createServer(
|
|
17
|
+
const { server } = createServer();
|
|
19
18
|
const transport = new StreamableHTTPServerTransport({
|
|
20
19
|
sessionIdGenerator: undefined,
|
|
21
20
|
});
|
|
@@ -71,7 +70,7 @@ async function startStreamableHTTP(config) {
|
|
|
71
70
|
});
|
|
72
71
|
return;
|
|
73
72
|
}
|
|
74
|
-
const { server } = createServer(
|
|
73
|
+
const { server } = createServer();
|
|
75
74
|
const transport = new StreamableHTTPServerTransport({
|
|
76
75
|
sessionIdGenerator: undefined,
|
|
77
76
|
});
|
|
@@ -143,18 +142,15 @@ Available endpoints:
|
|
|
143
142
|
/{environmentId}/mcp (requires Bearer authentication)`);
|
|
144
143
|
});
|
|
145
144
|
}
|
|
146
|
-
async function startStdio(
|
|
147
|
-
const { server } = createServer(
|
|
145
|
+
async function startStdio() {
|
|
146
|
+
const { server } = createServer();
|
|
148
147
|
const transport = new StdioServerTransport();
|
|
149
148
|
console.log(`Kontent.ai MCP Server v${version} (stdio) starting`);
|
|
150
149
|
await server.connect(transport);
|
|
151
150
|
}
|
|
152
151
|
async function main() {
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
initializeApplicationInsights(config);
|
|
156
|
-
trackServerStartup(version);
|
|
157
|
-
}
|
|
152
|
+
initializeApplicationInsights();
|
|
153
|
+
trackServerStartup(version);
|
|
158
154
|
const args = process.argv.slice(2);
|
|
159
155
|
const transportType = args[0]?.toLowerCase();
|
|
160
156
|
if (!transportType ||
|
|
@@ -163,10 +159,10 @@ async function main() {
|
|
|
163
159
|
process.exit(1);
|
|
164
160
|
}
|
|
165
161
|
if (transportType === "stdio") {
|
|
166
|
-
await startStdio(
|
|
162
|
+
await startStdio();
|
|
167
163
|
}
|
|
168
164
|
else if (transportType === "shttp") {
|
|
169
|
-
await startStreamableHTTP(
|
|
165
|
+
await startStreamableHTTP();
|
|
170
166
|
}
|
|
171
167
|
}
|
|
172
168
|
main().catch((error) => {
|
|
@@ -6,11 +6,10 @@ const sourceTrackingHeaderName = "X-KC-SOURCE";
|
|
|
6
6
|
* Creates a Kontent.ai Management API client
|
|
7
7
|
* @param environmentId Optional environment ID (defaults to process.env.KONTENT_ENVIRONMENT_ID)
|
|
8
8
|
* @param apiKey Optional API key (defaults to process.env.KONTENT_API_KEY)
|
|
9
|
-
* @param config Optional configuration object
|
|
10
9
|
* @param additionalHeaders Optional additional headers to include in requests
|
|
11
10
|
* @returns Management API client instance
|
|
12
11
|
*/
|
|
13
|
-
export const createMapiClient = (environmentId, apiKey,
|
|
12
|
+
export const createMapiClient = (environmentId, apiKey, additionalHeaders) => {
|
|
14
13
|
const allHeaders = [
|
|
15
14
|
{
|
|
16
15
|
header: sourceTrackingHeaderName,
|
|
@@ -18,6 +17,7 @@ export const createMapiClient = (environmentId, apiKey, config, additionalHeader
|
|
|
18
17
|
},
|
|
19
18
|
...(additionalHeaders || []),
|
|
20
19
|
];
|
|
20
|
+
const manageApiUrl = process.env.manageApiUrl;
|
|
21
21
|
return createManagementClient({
|
|
22
22
|
apiKey: apiKey ??
|
|
23
23
|
process.env.KONTENT_API_KEY ??
|
|
@@ -25,7 +25,7 @@ export const createMapiClient = (environmentId, apiKey, config, additionalHeader
|
|
|
25
25
|
environmentId: environmentId ??
|
|
26
26
|
process.env.KONTENT_ENVIRONMENT_ID ??
|
|
27
27
|
throwError("KONTENT_ENVIRONMENT_ID is not set"),
|
|
28
|
-
baseUrl:
|
|
28
|
+
baseUrl: manageApiUrl ? `${manageApiUrl}v2` : undefined,
|
|
29
29
|
headers: allHeaders,
|
|
30
30
|
});
|
|
31
31
|
};
|
|
@@ -1,9 +1,18 @@
|
|
|
1
1
|
import { z } from "zod";
|
|
2
2
|
import { referenceObjectSchema } from "./referenceObjectSchema.js";
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
3
|
+
// UserReferenceDataContract is a union type - either id or email, but not both
|
|
4
|
+
const userReferenceSchema = z
|
|
5
|
+
.union([
|
|
6
|
+
z.object({
|
|
7
|
+
id: z.string().describe("User identifier"),
|
|
8
|
+
email: z.never().optional(),
|
|
9
|
+
}),
|
|
10
|
+
z.object({
|
|
11
|
+
id: z.never().optional(),
|
|
12
|
+
email: z.string().email().describe("User email address"),
|
|
13
|
+
}),
|
|
14
|
+
])
|
|
15
|
+
.describe("Reference to a user by either their id or email (but not both)");
|
|
7
16
|
// Search variants tool input schema
|
|
8
17
|
export const filterVariantsSchema = z.object({
|
|
9
18
|
search_phrase: z
|
|
@@ -19,13 +28,13 @@ export const filterVariantsSchema = z.object({
|
|
|
19
28
|
.array(userReferenceSchema)
|
|
20
29
|
.min(1)
|
|
21
30
|
.optional()
|
|
22
|
-
.describe("Array of references to users by their id or email"),
|
|
31
|
+
.describe("Array of references to users by their id or email (but not both per user)"),
|
|
23
32
|
has_no_contributors: z
|
|
24
33
|
.boolean()
|
|
25
34
|
.optional()
|
|
26
35
|
.describe("Filter for content item language variants that have no contributors assigned"),
|
|
27
36
|
completion_statuses: z
|
|
28
|
-
.array(z.enum(["unfinished", "
|
|
37
|
+
.array(z.enum(["unfinished", "ready", "not_translated", "all_done"]))
|
|
29
38
|
.min(1)
|
|
30
39
|
.optional()
|
|
31
40
|
.describe("Array of completion statuses to filter by. It is not the same thing as workflow steps, it reflects e.g. not filled in required elements"),
|
|
@@ -48,24 +57,26 @@ export const filterVariantsSchema = z.object({
|
|
|
48
57
|
taxonomy_identifier: referenceObjectSchema.describe("Reference to a taxonomy group by its id, codename, or external id"),
|
|
49
58
|
term_identifiers: z
|
|
50
59
|
.array(referenceObjectSchema)
|
|
60
|
+
.optional()
|
|
51
61
|
.describe("Array of references to taxonomy terms by their id, codename, or external id"),
|
|
52
62
|
include_uncategorized: z
|
|
53
63
|
.boolean()
|
|
64
|
+
.optional()
|
|
54
65
|
.describe("Whether to include content item language variants that don't have any taxonomy terms assigned in this taxonomy group"),
|
|
55
66
|
}))
|
|
56
67
|
.min(1)
|
|
57
68
|
.optional()
|
|
58
69
|
.describe("Array of taxonomy groups with taxonomy terms"),
|
|
59
70
|
order_by: z
|
|
60
|
-
.enum(["name", "
|
|
71
|
+
.enum(["name", "due_date", "last_modified"])
|
|
61
72
|
.optional()
|
|
62
73
|
.describe("Field to order by"),
|
|
63
74
|
order_direction: z
|
|
64
75
|
.enum(["asc", "desc"])
|
|
65
76
|
.optional()
|
|
66
77
|
.describe("Order direction"),
|
|
67
|
-
|
|
68
|
-
.
|
|
78
|
+
include_content: z
|
|
79
|
+
.boolean()
|
|
69
80
|
.optional()
|
|
70
|
-
.describe("
|
|
81
|
+
.describe("Whether to include the full content of language variants in the response"),
|
|
71
82
|
});
|
package/build/server.js
CHANGED
|
@@ -30,7 +30,7 @@ import { registerTool as registerUnpublishVariantMapi } from "./tools/unpublish-
|
|
|
30
30
|
import { registerTool as registerUpdateContentItemMapi } from "./tools/update-content-item-mapi.js";
|
|
31
31
|
import { registerTool as registerUpsertLanguageVariantMapi } from "./tools/upsert-language-variant-mapi.js";
|
|
32
32
|
// Create server instance
|
|
33
|
-
export const createServer = (
|
|
33
|
+
export const createServer = () => {
|
|
34
34
|
const server = new McpServer({
|
|
35
35
|
name: "kontent-ai",
|
|
36
36
|
version: packageJson.version,
|
|
@@ -41,33 +41,33 @@ export const createServer = (config) => {
|
|
|
41
41
|
});
|
|
42
42
|
// Register all tools
|
|
43
43
|
registerGetInitialContext(server);
|
|
44
|
-
registerGetItemMapi(server
|
|
45
|
-
registerGetVariantMapi(server
|
|
46
|
-
registerGetTypeMapi(server
|
|
47
|
-
registerListContentTypesMapi(server
|
|
48
|
-
registerDeleteContentTypeMapi(server
|
|
49
|
-
registerListLanguagesMapi(server
|
|
50
|
-
registerGetAssetMapi(server
|
|
51
|
-
registerListAssetsMapi(server
|
|
52
|
-
registerAddContentTypeMapi(server
|
|
53
|
-
registerPatchContentTypeMapi(server
|
|
54
|
-
registerAddContentTypeSnippetMapi(server
|
|
55
|
-
registerGetTypeSnippetMapi(server
|
|
56
|
-
registerListContentTypeSnippetsMapi(server
|
|
57
|
-
registerAddTaxonomyGroupMapi(server
|
|
58
|
-
registerListTaxonomyGroupsMapi(server
|
|
59
|
-
registerGetTaxonomyGroupMapi(server
|
|
60
|
-
registerAddContentItemMapi(server
|
|
61
|
-
registerUpdateContentItemMapi(server
|
|
62
|
-
registerDeleteContentItemMapi(server
|
|
63
|
-
registerUpsertLanguageVariantMapi(server
|
|
64
|
-
registerCreateVariantVersionMapi(server
|
|
65
|
-
registerDeleteLanguageVariantMapi(server
|
|
66
|
-
registerListWorkflowsMapi(server
|
|
67
|
-
registerChangeVariantWorkflowStepMapi(server
|
|
68
|
-
registerFilterVariantsMapi(server
|
|
69
|
-
registerSearchVariantsMapi(server
|
|
70
|
-
registerPublishVariantMapi(server
|
|
71
|
-
registerUnpublishVariantMapi(server
|
|
44
|
+
registerGetItemMapi(server);
|
|
45
|
+
registerGetVariantMapi(server);
|
|
46
|
+
registerGetTypeMapi(server);
|
|
47
|
+
registerListContentTypesMapi(server);
|
|
48
|
+
registerDeleteContentTypeMapi(server);
|
|
49
|
+
registerListLanguagesMapi(server);
|
|
50
|
+
registerGetAssetMapi(server);
|
|
51
|
+
registerListAssetsMapi(server);
|
|
52
|
+
registerAddContentTypeMapi(server);
|
|
53
|
+
registerPatchContentTypeMapi(server);
|
|
54
|
+
registerAddContentTypeSnippetMapi(server);
|
|
55
|
+
registerGetTypeSnippetMapi(server);
|
|
56
|
+
registerListContentTypeSnippetsMapi(server);
|
|
57
|
+
registerAddTaxonomyGroupMapi(server);
|
|
58
|
+
registerListTaxonomyGroupsMapi(server);
|
|
59
|
+
registerGetTaxonomyGroupMapi(server);
|
|
60
|
+
registerAddContentItemMapi(server);
|
|
61
|
+
registerUpdateContentItemMapi(server);
|
|
62
|
+
registerDeleteContentItemMapi(server);
|
|
63
|
+
registerUpsertLanguageVariantMapi(server);
|
|
64
|
+
registerCreateVariantVersionMapi(server);
|
|
65
|
+
registerDeleteLanguageVariantMapi(server);
|
|
66
|
+
registerListWorkflowsMapi(server);
|
|
67
|
+
registerChangeVariantWorkflowStepMapi(server);
|
|
68
|
+
registerFilterVariantsMapi(server);
|
|
69
|
+
registerSearchVariantsMapi(server);
|
|
70
|
+
registerPublishVariantMapi(server);
|
|
71
|
+
registerUnpublishVariantMapi(server);
|
|
72
72
|
return { server };
|
|
73
73
|
};
|
|
@@ -1,9 +1,12 @@
|
|
|
1
1
|
import { SharedModels } from "@kontent-ai/management-sdk";
|
|
2
2
|
import appInsights from "applicationinsights";
|
|
3
|
-
import { AxiosError } from "axios";
|
|
4
3
|
import { sanitizeTelemetry, sanitizeUrl } from "./telemetrySanitizer.js";
|
|
5
4
|
let isInitialized = false;
|
|
6
5
|
function trackKontentApiError(error, context) {
|
|
6
|
+
if (error.validationErrors && error.validationErrors.length > 0) {
|
|
7
|
+
// Don't log 400 responses as exceptions
|
|
8
|
+
return;
|
|
9
|
+
}
|
|
7
10
|
const safeError = new Error(error.message);
|
|
8
11
|
safeError.stack = error.originalError.stack || safeError.stack;
|
|
9
12
|
appInsights.defaultClient.trackException({
|
|
@@ -87,7 +90,7 @@ export function trackException(error, context) {
|
|
|
87
90
|
trackKontentApiError(error, context);
|
|
88
91
|
return;
|
|
89
92
|
}
|
|
90
|
-
if (error
|
|
93
|
+
if (error.isAxiosError) {
|
|
91
94
|
trackHttpError(error, context);
|
|
92
95
|
return;
|
|
93
96
|
}
|
|
@@ -118,7 +121,7 @@ export function trackServerStartup(version) {
|
|
|
118
121
|
// Silently fail - tracking should never break the application
|
|
119
122
|
}
|
|
120
123
|
}
|
|
121
|
-
function createTelemetryProcessor(
|
|
124
|
+
function createTelemetryProcessor() {
|
|
122
125
|
return (envelope) => {
|
|
123
126
|
sanitizeTelemetry(envelope);
|
|
124
127
|
if (envelope.data?.baseData) {
|
|
@@ -126,18 +129,19 @@ function createTelemetryProcessor(config) {
|
|
|
126
129
|
envelope.data.baseData.properties || {};
|
|
127
130
|
envelope.data.baseData.properties["component.name"] = "mcp-server";
|
|
128
131
|
envelope.data.baseData.properties["component.location"] =
|
|
129
|
-
|
|
132
|
+
process.env.projectLocation || "unknown";
|
|
130
133
|
}
|
|
131
134
|
return true;
|
|
132
135
|
};
|
|
133
136
|
}
|
|
134
|
-
export function initializeApplicationInsights(
|
|
137
|
+
export function initializeApplicationInsights() {
|
|
135
138
|
try {
|
|
136
|
-
|
|
139
|
+
const connectionString = process.env.appInsightsConnectionString;
|
|
140
|
+
if (!connectionString) {
|
|
137
141
|
return;
|
|
138
142
|
}
|
|
139
143
|
appInsights
|
|
140
|
-
.setup(
|
|
144
|
+
.setup(connectionString)
|
|
141
145
|
.setAutoCollectExceptions(true)
|
|
142
146
|
.setAutoCollectRequests(true)
|
|
143
147
|
.setAutoCollectConsole(false)
|
|
@@ -149,7 +153,7 @@ export function initializeApplicationInsights(config) {
|
|
|
149
153
|
.setAutoCollectPreAggregatedMetrics(false)
|
|
150
154
|
.start();
|
|
151
155
|
isInitialized = true;
|
|
152
|
-
appInsights.defaultClient.addTelemetryProcessor(createTelemetryProcessor(
|
|
156
|
+
appInsights.defaultClient.addTelemetryProcessor(createTelemetryProcessor());
|
|
153
157
|
}
|
|
154
158
|
catch (error) {
|
|
155
159
|
console.log("Failed to initialize Application Insights:", error);
|
|
@@ -2,7 +2,7 @@ import { z } from "zod";
|
|
|
2
2
|
import { createMapiClient } from "../clients/kontentClients.js";
|
|
3
3
|
import { handleMcpToolError } from "../utils/errorHandler.js";
|
|
4
4
|
import { createMcpToolSuccessResponse } from "../utils/responseHelper.js";
|
|
5
|
-
export const registerTool = (server
|
|
5
|
+
export const registerTool = (server) => {
|
|
6
6
|
server.tool("add-content-item-mapi", "Add new Kontent.ai content item via Management API. This creates the content item structure but does not add content to language variants. Use upsert-language-variant-mapi to add content to the item.", {
|
|
7
7
|
name: z
|
|
8
8
|
.string()
|
|
@@ -33,7 +33,7 @@ export const registerTool = (server, config) => {
|
|
|
33
33
|
.optional()
|
|
34
34
|
.describe("Reference to a collection by id, codename, or external_id (optional)"),
|
|
35
35
|
}, async ({ name, type, codename, external_id, collection }, { authInfo: { token, clientId } = {} }) => {
|
|
36
|
-
const client = createMapiClient(clientId, token
|
|
36
|
+
const client = createMapiClient(clientId, token);
|
|
37
37
|
try {
|
|
38
38
|
const response = await client
|
|
39
39
|
.addContentItem()
|
|
@@ -3,7 +3,7 @@ import { createMapiClient } from "../clients/kontentClients.js";
|
|
|
3
3
|
import { contentGroupSchema, elementSchema, } from "../schemas/contentTypeSchemas.js";
|
|
4
4
|
import { handleMcpToolError } from "../utils/errorHandler.js";
|
|
5
5
|
import { createMcpToolSuccessResponse } from "../utils/responseHelper.js";
|
|
6
|
-
export const registerTool = (server
|
|
6
|
+
export const registerTool = (server) => {
|
|
7
7
|
server.tool("add-content-type-mapi", "Add new Kontent.ai content type via Management API", {
|
|
8
8
|
name: z.string().describe("Display name of the content type"),
|
|
9
9
|
codename: z
|
|
@@ -22,7 +22,7 @@ export const registerTool = (server, config) => {
|
|
|
22
22
|
.optional()
|
|
23
23
|
.describe("Array of content groups (optional)"),
|
|
24
24
|
}, async ({ name, codename, external_id, elements, content_groups }, { authInfo: { token, clientId } = {} }) => {
|
|
25
|
-
const client = createMapiClient(clientId, token
|
|
25
|
+
const client = createMapiClient(clientId, token);
|
|
26
26
|
try {
|
|
27
27
|
const response = await client
|
|
28
28
|
.addContentType()
|
|
@@ -3,7 +3,7 @@ import { createMapiClient } from "../clients/kontentClients.js";
|
|
|
3
3
|
import { snippetElementSchema } from "../schemas/contentTypeSchemas.js";
|
|
4
4
|
import { handleMcpToolError } from "../utils/errorHandler.js";
|
|
5
5
|
import { createMcpToolSuccessResponse } from "../utils/responseHelper.js";
|
|
6
|
-
export const registerTool = (server
|
|
6
|
+
export const registerTool = (server) => {
|
|
7
7
|
server.tool("add-content-type-snippet-mapi", "Add new Kontent.ai content type snippet via Management API", {
|
|
8
8
|
name: z.string().describe("Display name of the content type snippet"),
|
|
9
9
|
codename: z
|
|
@@ -18,7 +18,7 @@ export const registerTool = (server, config) => {
|
|
|
18
18
|
.array(snippetElementSchema)
|
|
19
19
|
.describe("Array of elements that define the structure of the content type snippet"),
|
|
20
20
|
}, async ({ name, codename, external_id, elements }, { authInfo: { token, clientId } = {} }) => {
|
|
21
|
-
const client = createMapiClient(clientId, token
|
|
21
|
+
const client = createMapiClient(clientId, token);
|
|
22
22
|
try {
|
|
23
23
|
const response = await client
|
|
24
24
|
.addContentTypeSnippet()
|
|
@@ -2,9 +2,9 @@ import { createMapiClient } from "../clients/kontentClients.js";
|
|
|
2
2
|
import { taxonomyGroupSchemas } from "../schemas/taxonomySchemas.js";
|
|
3
3
|
import { handleMcpToolError } from "../utils/errorHandler.js";
|
|
4
4
|
import { createMcpToolSuccessResponse } from "../utils/responseHelper.js";
|
|
5
|
-
export const registerTool = (server
|
|
5
|
+
export const registerTool = (server) => {
|
|
6
6
|
server.tool("add-taxonomy-group-mapi", "Add new Kontent.ai taxonomy group via Management API", taxonomyGroupSchemas, async (taxonomyGroup, { authInfo: { token, clientId } = {} }) => {
|
|
7
|
-
const client = createMapiClient(clientId, token
|
|
7
|
+
const client = createMapiClient(clientId, token);
|
|
8
8
|
try {
|
|
9
9
|
const response = await client
|
|
10
10
|
.addTaxonomy()
|
|
@@ -2,7 +2,7 @@ import { z } from "zod";
|
|
|
2
2
|
import { createMapiClient } from "../clients/kontentClients.js";
|
|
3
3
|
import { handleMcpToolError } from "../utils/errorHandler.js";
|
|
4
4
|
import { createMcpToolSuccessResponse } from "../utils/responseHelper.js";
|
|
5
|
-
export const registerTool = (server
|
|
5
|
+
export const registerTool = (server) => {
|
|
6
6
|
server.tool("change-variant-workflow-step-mapi", "Change the workflow step of a language variant in Kontent.ai. This operation moves a language variant to a different step in the workflow, enabling content lifecycle management such as moving content from draft to review, review to published, etc.", {
|
|
7
7
|
itemId: z
|
|
8
8
|
.string()
|
|
@@ -21,7 +21,7 @@ export const registerTool = (server, config) => {
|
|
|
21
21
|
.uuid()
|
|
22
22
|
.describe("Internal ID (UUID) of the target workflow step. This must be a valid step ID from the specified workflow. Common steps include Draft, Review, Published, and Archived, but the actual IDs depend on your specific workflow configuration"),
|
|
23
23
|
}, async ({ itemId, languageId, workflowId, workflowStepId }, { authInfo: { token, clientId } = {} }) => {
|
|
24
|
-
const client = createMapiClient(clientId, token
|
|
24
|
+
const client = createMapiClient(clientId, token);
|
|
25
25
|
try {
|
|
26
26
|
const response = await client
|
|
27
27
|
.changeWorkflowOfLanguageVariant()
|
|
@@ -2,7 +2,7 @@ import { z } from "zod";
|
|
|
2
2
|
import { createMapiClient } from "../clients/kontentClients.js";
|
|
3
3
|
import { handleMcpToolError } from "../utils/errorHandler.js";
|
|
4
4
|
import { createMcpToolSuccessResponse } from "../utils/responseHelper.js";
|
|
5
|
-
export const registerTool = (server
|
|
5
|
+
export const registerTool = (server) => {
|
|
6
6
|
server.tool("create-variant-version-mapi", "Create new version of Kontent.ai language variant via Management API. This operation creates a new version of an existing language variant, useful for content versioning and creating new drafts from published content.", {
|
|
7
7
|
itemId: z
|
|
8
8
|
.string()
|
|
@@ -13,7 +13,7 @@ export const registerTool = (server, config) => {
|
|
|
13
13
|
.uuid()
|
|
14
14
|
.describe("Internal ID (UUID) of the language variant to create a new version of. Use '00000000-0000-0000-0000-000000000000' for the default language"),
|
|
15
15
|
}, async ({ itemId, languageId }, { authInfo: { token, clientId } = {} }) => {
|
|
16
|
-
const client = createMapiClient(clientId, token
|
|
16
|
+
const client = createMapiClient(clientId, token);
|
|
17
17
|
try {
|
|
18
18
|
const response = await client
|
|
19
19
|
.createNewVersionOfLanguageVariant()
|
|
@@ -2,11 +2,11 @@ import { z } from "zod";
|
|
|
2
2
|
import { createMapiClient } from "../clients/kontentClients.js";
|
|
3
3
|
import { handleMcpToolError } from "../utils/errorHandler.js";
|
|
4
4
|
import { createMcpToolSuccessResponse } from "../utils/responseHelper.js";
|
|
5
|
-
export const registerTool = (server
|
|
5
|
+
export const registerTool = (server) => {
|
|
6
6
|
server.tool("delete-content-item-mapi", "Delete Kontent.ai content item by internal ID from Management API", {
|
|
7
7
|
id: z.string().describe("Internal ID of the content item to delete"),
|
|
8
8
|
}, async ({ id }, { authInfo: { token, clientId } = {} }) => {
|
|
9
|
-
const client = createMapiClient(clientId, token
|
|
9
|
+
const client = createMapiClient(clientId, token);
|
|
10
10
|
try {
|
|
11
11
|
const response = await client
|
|
12
12
|
.deleteContentItem()
|
|
@@ -2,11 +2,11 @@ import { z } from "zod";
|
|
|
2
2
|
import { createMapiClient } from "../clients/kontentClients.js";
|
|
3
3
|
import { handleMcpToolError } from "../utils/errorHandler.js";
|
|
4
4
|
import { createMcpToolSuccessResponse } from "../utils/responseHelper.js";
|
|
5
|
-
export const registerTool = (server
|
|
5
|
+
export const registerTool = (server) => {
|
|
6
6
|
server.tool("delete-content-type-mapi", "Delete a content type by codename from Management API", {
|
|
7
7
|
codename: z.string().describe("Codename of the content type to delete"),
|
|
8
8
|
}, async ({ codename }, { authInfo: { token, clientId } = {} }) => {
|
|
9
|
-
const client = createMapiClient(clientId, token
|
|
9
|
+
const client = createMapiClient(clientId, token);
|
|
10
10
|
try {
|
|
11
11
|
const response = await client
|
|
12
12
|
.deleteContentType()
|
|
@@ -2,14 +2,14 @@ import { z } from "zod";
|
|
|
2
2
|
import { createMapiClient } from "../clients/kontentClients.js";
|
|
3
3
|
import { handleMcpToolError } from "../utils/errorHandler.js";
|
|
4
4
|
import { createMcpToolSuccessResponse } from "../utils/responseHelper.js";
|
|
5
|
-
export const registerTool = (server
|
|
5
|
+
export const registerTool = (server) => {
|
|
6
6
|
server.tool("delete-language-variant-mapi", "Delete Kontent.ai language variant from Management API", {
|
|
7
7
|
itemId: z.string().describe("Internal ID of the content item"),
|
|
8
8
|
languageId: z
|
|
9
9
|
.string()
|
|
10
10
|
.describe("Internal ID of the language variant to delete"),
|
|
11
11
|
}, async ({ itemId, languageId }, { authInfo: { token, clientId } = {} }) => {
|
|
12
|
-
const client = createMapiClient(clientId, token
|
|
12
|
+
const client = createMapiClient(clientId, token);
|
|
13
13
|
try {
|
|
14
14
|
const response = await client
|
|
15
15
|
.deleteLanguageVariant()
|
|
@@ -3,24 +3,24 @@ import { filterVariantsSchema } from "../schemas/filterVariantSchemas.js";
|
|
|
3
3
|
import { handleMcpToolError } from "../utils/errorHandler.js";
|
|
4
4
|
import { createMcpToolSuccessResponse } from "../utils/responseHelper.js";
|
|
5
5
|
import { throwError } from "../utils/throwError.js";
|
|
6
|
-
export const registerTool = (server
|
|
6
|
+
export const registerTool = (server) => {
|
|
7
7
|
server.tool("filter-variants-mapi", `Filter Kontent.ai language variants of content items using Management API.
|
|
8
|
-
|
|
8
|
+
|
|
9
9
|
USE FOR:
|
|
10
10
|
- EXACT keyword matching: finding specific words, phrases, names, codes, or IDs in content. Example: 'find items containing rabbit' → search 'rabbit'
|
|
11
11
|
- Advanced filtering by content type, contributors, workflow steps, taxonomies etc
|
|
12
12
|
- CAN expand concepts to keywords when using filter (e.g., "neurology-related" → "neurology neurological brain nervous system")
|
|
13
|
-
- Also use as fallback when AI search is unavailable
|
|
13
|
+
- Also use as fallback when AI search is unavailable
|
|
14
|
+
- Optionally includes full content of variants with include_content parameter`, filterVariantsSchema.shape, async ({ search_phrase, content_types, contributors, has_no_contributors, completion_statuses, language, workflow_steps, taxonomy_groups, order_by, order_direction, include_content, }, { authInfo: { token, clientId } = {} }) => {
|
|
14
15
|
try {
|
|
15
16
|
const environmentId = clientId ?? process.env.KONTENT_ENVIRONMENT_ID;
|
|
16
17
|
if (!environmentId) {
|
|
17
18
|
throwError("Missing required environment ID");
|
|
18
19
|
}
|
|
19
|
-
const
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
const requestPayload = {
|
|
20
|
+
const client = createMapiClient(environmentId, token);
|
|
21
|
+
const response = await client.earlyAccess
|
|
22
|
+
.filterLanguageVariants()
|
|
23
|
+
.withData({
|
|
24
24
|
filters: {
|
|
25
25
|
search_phrase,
|
|
26
26
|
content_types,
|
|
@@ -34,19 +34,16 @@ export const registerTool = (server, config) => {
|
|
|
34
34
|
order: order_by
|
|
35
35
|
? {
|
|
36
36
|
by: order_by,
|
|
37
|
-
direction: order_direction
|
|
37
|
+
direction: order_direction || "asc",
|
|
38
38
|
}
|
|
39
|
-
:
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
.
|
|
43
|
-
|
|
44
|
-
.withData(requestPayload)
|
|
45
|
-
.toPromise();
|
|
46
|
-
return createMcpToolSuccessResponse(response.data);
|
|
39
|
+
: undefined,
|
|
40
|
+
include_content: include_content ?? false,
|
|
41
|
+
})
|
|
42
|
+
.toAllPromise();
|
|
43
|
+
return createMcpToolSuccessResponse(response.responses.flatMap((r) => r.rawData.data));
|
|
47
44
|
}
|
|
48
45
|
catch (error) {
|
|
49
|
-
return handleMcpToolError(error, "Variant
|
|
46
|
+
return handleMcpToolError(error, "Variant Filter");
|
|
50
47
|
}
|
|
51
48
|
});
|
|
52
49
|
};
|
|
@@ -2,11 +2,11 @@ import { z } from "zod";
|
|
|
2
2
|
import { createMapiClient } from "../clients/kontentClients.js";
|
|
3
3
|
import { handleMcpToolError } from "../utils/errorHandler.js";
|
|
4
4
|
import { createMcpToolSuccessResponse } from "../utils/responseHelper.js";
|
|
5
|
-
export const registerTool = (server
|
|
5
|
+
export const registerTool = (server) => {
|
|
6
6
|
server.tool("get-asset-mapi", "Get a specific Kontent.ai asset by internal ID from Management API", {
|
|
7
7
|
assetId: z.string().describe("Internal ID of the asset to retrieve"),
|
|
8
8
|
}, async ({ assetId }, { authInfo: { token, clientId } = {} }) => {
|
|
9
|
-
const client = createMapiClient(clientId, token
|
|
9
|
+
const client = createMapiClient(clientId, token);
|
|
10
10
|
try {
|
|
11
11
|
const response = await client
|
|
12
12
|
.viewAsset()
|
|
@@ -2,11 +2,11 @@ import { z } from "zod";
|
|
|
2
2
|
import { createMapiClient } from "../clients/kontentClients.js";
|
|
3
3
|
import { handleMcpToolError } from "../utils/errorHandler.js";
|
|
4
4
|
import { createMcpToolSuccessResponse } from "../utils/responseHelper.js";
|
|
5
|
-
export const registerTool = (server
|
|
5
|
+
export const registerTool = (server) => {
|
|
6
6
|
server.tool("get-item-mapi", "Get Kontent.ai item by internal ID from Management API", {
|
|
7
7
|
id: z.string().describe("Internal ID of the item to get"),
|
|
8
8
|
}, async ({ id }, { authInfo: { token, clientId } = {} }) => {
|
|
9
|
-
const client = createMapiClient(clientId, token
|
|
9
|
+
const client = createMapiClient(clientId, token);
|
|
10
10
|
try {
|
|
11
11
|
const response = await client
|
|
12
12
|
.viewContentItem()
|
|
@@ -2,11 +2,11 @@ import { z } from "zod";
|
|
|
2
2
|
import { createMapiClient } from "../clients/kontentClients.js";
|
|
3
3
|
import { handleMcpToolError } from "../utils/errorHandler.js";
|
|
4
4
|
import { createMcpToolSuccessResponse } from "../utils/responseHelper.js";
|
|
5
|
-
export const registerTool = (server
|
|
5
|
+
export const registerTool = (server) => {
|
|
6
6
|
server.tool("get-taxonomy-group-mapi", "Get Kontent.ai taxonomy group by internal ID from Management API", {
|
|
7
7
|
id: z.string().describe("Internal ID of the taxonomy group to get"),
|
|
8
8
|
}, async ({ id }, { authInfo: { token, clientId } = {} }) => {
|
|
9
|
-
const client = createMapiClient(clientId, token
|
|
9
|
+
const client = createMapiClient(clientId, token);
|
|
10
10
|
try {
|
|
11
11
|
const response = await client
|
|
12
12
|
.getTaxonomy()
|
|
@@ -2,11 +2,11 @@ import { z } from "zod";
|
|
|
2
2
|
import { createMapiClient } from "../clients/kontentClients.js";
|
|
3
3
|
import { handleMcpToolError } from "../utils/errorHandler.js";
|
|
4
4
|
import { createMcpToolSuccessResponse } from "../utils/responseHelper.js";
|
|
5
|
-
export const registerTool = (server
|
|
5
|
+
export const registerTool = (server) => {
|
|
6
6
|
server.tool("get-type-mapi", "Get Kontent.ai content type by internal ID from Management API", {
|
|
7
7
|
id: z.string().describe("Internal ID of the content type to get"),
|
|
8
8
|
}, async ({ id }, { authInfo: { token, clientId } = {} }) => {
|
|
9
|
-
const client = createMapiClient(clientId, token
|
|
9
|
+
const client = createMapiClient(clientId, token);
|
|
10
10
|
try {
|
|
11
11
|
const response = await client
|
|
12
12
|
.viewContentType()
|
|
@@ -2,11 +2,11 @@ import { z } from "zod";
|
|
|
2
2
|
import { createMapiClient } from "../clients/kontentClients.js";
|
|
3
3
|
import { handleMcpToolError } from "../utils/errorHandler.js";
|
|
4
4
|
import { createMcpToolSuccessResponse } from "../utils/responseHelper.js";
|
|
5
|
-
export const registerTool = (server
|
|
5
|
+
export const registerTool = (server) => {
|
|
6
6
|
server.tool("get-type-snippet-mapi", "Get Kontent.ai content type snippet by internal ID from Management API", {
|
|
7
7
|
id: z.string().describe("Internal ID of the content type snippet to get"),
|
|
8
8
|
}, async ({ id }, { authInfo: { token, clientId } = {} }) => {
|
|
9
|
-
const client = createMapiClient(clientId, token
|
|
9
|
+
const client = createMapiClient(clientId, token);
|
|
10
10
|
try {
|
|
11
11
|
const response = await client
|
|
12
12
|
.viewContentTypeSnippet()
|
|
@@ -2,14 +2,14 @@ import { z } from "zod";
|
|
|
2
2
|
import { createMapiClient } from "../clients/kontentClients.js";
|
|
3
3
|
import { handleMcpToolError } from "../utils/errorHandler.js";
|
|
4
4
|
import { createMcpToolSuccessResponse } from "../utils/responseHelper.js";
|
|
5
|
-
export const registerTool = (server
|
|
5
|
+
export const registerTool = (server) => {
|
|
6
6
|
server.tool("get-variant-mapi", "Get Kontent.ai language variant of content item from Management API", {
|
|
7
7
|
itemId: z.string().describe("Internal ID of the content item"),
|
|
8
8
|
languageId: z
|
|
9
9
|
.string()
|
|
10
10
|
.describe("Internal ID of the language variant to get"),
|
|
11
11
|
}, async ({ itemId, languageId }, { authInfo: { token, clientId } = {} }) => {
|
|
12
|
-
const client = createMapiClient(clientId, token
|
|
12
|
+
const client = createMapiClient(clientId, token);
|
|
13
13
|
try {
|
|
14
14
|
const response = await client
|
|
15
15
|
.viewLanguageVariant()
|
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
import { createMapiClient } from "../clients/kontentClients.js";
|
|
2
2
|
import { handleMcpToolError } from "../utils/errorHandler.js";
|
|
3
3
|
import { createMcpToolSuccessResponse } from "../utils/responseHelper.js";
|
|
4
|
-
export const registerTool = (server
|
|
4
|
+
export const registerTool = (server) => {
|
|
5
5
|
server.tool("list-assets-mapi", "Get all Kontent.ai assets from Management API", {}, async (_, { authInfo: { token, clientId } = {} }) => {
|
|
6
|
-
const client = createMapiClient(clientId, token
|
|
6
|
+
const client = createMapiClient(clientId, token);
|
|
7
7
|
try {
|
|
8
8
|
const response = await client.listAssets().toAllPromise();
|
|
9
9
|
const rawData = response.responses.flatMap((r) => r.rawData.assets);
|
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
import { createMapiClient } from "../clients/kontentClients.js";
|
|
2
2
|
import { handleMcpToolError } from "../utils/errorHandler.js";
|
|
3
3
|
import { createMcpToolSuccessResponse } from "../utils/responseHelper.js";
|
|
4
|
-
export const registerTool = (server
|
|
4
|
+
export const registerTool = (server) => {
|
|
5
5
|
server.tool("list-content-type-snippets-mapi", "Get all Kontent.ai content type snippets from Management API", {}, async (_, { authInfo: { token, clientId } = {} }) => {
|
|
6
|
-
const client = createMapiClient(clientId, token
|
|
6
|
+
const client = createMapiClient(clientId, token);
|
|
7
7
|
try {
|
|
8
8
|
const response = await client.listContentTypeSnippets().toAllPromise();
|
|
9
9
|
const rawData = response.responses.flatMap((r) => r.rawData.snippets);
|
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
import { createMapiClient } from "../clients/kontentClients.js";
|
|
2
2
|
import { handleMcpToolError } from "../utils/errorHandler.js";
|
|
3
3
|
import { createMcpToolSuccessResponse } from "../utils/responseHelper.js";
|
|
4
|
-
export const registerTool = (server
|
|
4
|
+
export const registerTool = (server) => {
|
|
5
5
|
server.tool("list-content-types-mapi", "Get all Kontent.ai content types from Management API", {}, async (_, { authInfo: { token, clientId } = {} }) => {
|
|
6
|
-
const client = createMapiClient(clientId, token
|
|
6
|
+
const client = createMapiClient(clientId, token);
|
|
7
7
|
try {
|
|
8
8
|
const response = await client.listContentTypes().toAllPromise();
|
|
9
9
|
const rawData = response.responses.flatMap((r) => r.rawData.types);
|
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
import { createMapiClient } from "../clients/kontentClients.js";
|
|
2
2
|
import { handleMcpToolError } from "../utils/errorHandler.js";
|
|
3
3
|
import { createMcpToolSuccessResponse } from "../utils/responseHelper.js";
|
|
4
|
-
export const registerTool = (server
|
|
4
|
+
export const registerTool = (server) => {
|
|
5
5
|
server.tool("list-languages-mapi", "Get all Kontent.ai languages from Management API", {}, async (_, { authInfo: { token, clientId } = {} }) => {
|
|
6
|
-
const client = createMapiClient(clientId, token
|
|
6
|
+
const client = createMapiClient(clientId, token);
|
|
7
7
|
try {
|
|
8
8
|
const response = await client.listLanguages().toAllPromise();
|
|
9
9
|
const rawData = response.responses.flatMap((r) => r.rawData.languages);
|
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
import { createMapiClient } from "../clients/kontentClients.js";
|
|
2
2
|
import { handleMcpToolError } from "../utils/errorHandler.js";
|
|
3
3
|
import { createMcpToolSuccessResponse } from "../utils/responseHelper.js";
|
|
4
|
-
export const registerTool = (server
|
|
4
|
+
export const registerTool = (server) => {
|
|
5
5
|
server.tool("list-taxonomy-groups-mapi", "Get all Kontent.ai taxonomy groups from Management API", {}, async (_, { authInfo: { token, clientId } = {} }) => {
|
|
6
|
-
const client = createMapiClient(clientId, token
|
|
6
|
+
const client = createMapiClient(clientId, token);
|
|
7
7
|
try {
|
|
8
8
|
const response = await client.listTaxonomies().toAllPromise();
|
|
9
9
|
const rawData = response.responses.flatMap((r) => r.rawData.taxonomies);
|
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
import { createMapiClient } from "../clients/kontentClients.js";
|
|
2
2
|
import { handleMcpToolError } from "../utils/errorHandler.js";
|
|
3
3
|
import { createMcpToolSuccessResponse } from "../utils/responseHelper.js";
|
|
4
|
-
export const registerTool = (server
|
|
4
|
+
export const registerTool = (server) => {
|
|
5
5
|
server.tool("list-workflows-mapi", "Get all Kontent.ai workflows from Management API. Workflows define the content lifecycle stages and transitions between them.", {}, async (_, { authInfo: { token, clientId } = {} }) => {
|
|
6
|
-
const client = createMapiClient(clientId, token
|
|
6
|
+
const client = createMapiClient(clientId, token);
|
|
7
7
|
try {
|
|
8
8
|
const response = await client.listWorkflows().toPromise();
|
|
9
9
|
return createMcpToolSuccessResponse(response.rawData);
|
|
@@ -3,7 +3,7 @@ import { createMapiClient } from "../clients/kontentClients.js";
|
|
|
3
3
|
import { patchOperationsSchema } from "../schemas/patchSchemas/contentTypePatchSchemas.js";
|
|
4
4
|
import { handleMcpToolError } from "../utils/errorHandler.js";
|
|
5
5
|
import { createMcpToolSuccessResponse } from "../utils/responseHelper.js";
|
|
6
|
-
export const registerTool = (server
|
|
6
|
+
export const registerTool = (server) => {
|
|
7
7
|
server.tool("patch-content-type-mapi", "Update an existing Kontent.ai content type by codename via Management API. Supports move, addInto, remove, and replace operations following RFC 6902 JSON Patch specification.", {
|
|
8
8
|
codename: z.string().describe("Codename of the content type to update"),
|
|
9
9
|
operations: patchOperationsSchema.describe(`Array of patch operations to apply. Supports: 'move' (reorganize elements), 'addInto' (add new elements), 'remove' (delete elements), 'replace' (update existing elements/properties).
|
|
@@ -55,7 +55,7 @@ export const registerTool = (server, config) => {
|
|
|
55
55
|
- Use atomic operations for complex changes like removing content groups
|
|
56
56
|
- When adding to allowed_formatting or allowed_table_formatting, always ensure 'unstyled' is the first item in the array`),
|
|
57
57
|
}, async ({ codename, operations }, { authInfo: { token, clientId } = {} }) => {
|
|
58
|
-
const client = createMapiClient(clientId, token
|
|
58
|
+
const client = createMapiClient(clientId, token);
|
|
59
59
|
try {
|
|
60
60
|
// Apply patch operations using the modifyContentType method
|
|
61
61
|
const response = await client
|
|
@@ -2,7 +2,7 @@ import { z } from "zod";
|
|
|
2
2
|
import { createMapiClient } from "../clients/kontentClients.js";
|
|
3
3
|
import { handleMcpToolError } from "../utils/errorHandler.js";
|
|
4
4
|
import { createMcpToolSuccessResponse } from "../utils/responseHelper.js";
|
|
5
|
-
export const registerTool = (server
|
|
5
|
+
export const registerTool = (server) => {
|
|
6
6
|
server.tool("publish-variant-mapi", "Publish or schedule a language variant of a content item in Kontent.ai. This operation can either immediately publish the variant (publishing happens right now) or schedule it for publication at a specific future date and time. For immediate publishing: the variant is published immediately and becomes available through the Delivery API. For scheduled publishing: the variant moves to a 'Scheduled' workflow state and will automatically transition to 'Published' at the specified time. The variant must be in a valid state with all required fields filled and validation rules satisfied. A variant can only be published if a transition is defined between the variant's current workflow step and the Published workflow step.", {
|
|
7
7
|
itemId: z
|
|
8
8
|
.string()
|
|
@@ -22,7 +22,7 @@ export const registerTool = (server, config) => {
|
|
|
22
22
|
.optional()
|
|
23
23
|
.describe("The timezone identifier for displaying the scheduled time in the Kontent.ai UI (e.g., 'America/New_York', 'Europe/London', 'UTC'). This parameter is used for scheduled publishing to specify the timezone context for the scheduled_to parameter. If not provided, the system will use the default timezone. This helps content creators understand when content will be published in their local context."),
|
|
24
24
|
}, async ({ itemId, languageId, scheduledTo, displayTimezone }, { authInfo: { token, clientId } = {} }) => {
|
|
25
|
-
const client = createMapiClient(clientId, token
|
|
25
|
+
const client = createMapiClient(clientId, token);
|
|
26
26
|
try {
|
|
27
27
|
// Validate that displayTimezone can only be used with scheduledTo
|
|
28
28
|
if (displayTimezone && !scheduledTo) {
|
|
@@ -1,10 +1,10 @@
|
|
|
1
|
-
import pRetry from "p-retry";
|
|
1
|
+
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
5
|
import { createMcpToolSuccessResponse } from "../utils/responseHelper.js";
|
|
6
6
|
import { throwError } from "../utils/throwError.js";
|
|
7
|
-
export const registerTool = (server
|
|
7
|
+
export const registerTool = (server) => {
|
|
8
8
|
server.tool("search-variants-mapi", `AI-powered semantic search for finding Kontent.ai content by meaning, concepts, themes, and content similarity in a specific language variant. This tool uses vector database and AI to enable searching by meaning and similarity rather than exact keyword matching.
|
|
9
9
|
|
|
10
10
|
CRITICAL REQUIREMENTS:
|
|
@@ -25,7 +25,7 @@ export const registerTool = (server, config) => {
|
|
|
25
25
|
if (!environmentId) {
|
|
26
26
|
throwError("Missing required environment ID");
|
|
27
27
|
}
|
|
28
|
-
const client = createMapiClient(environmentId, token
|
|
28
|
+
const client = createMapiClient(environmentId, token);
|
|
29
29
|
// Step 1: Initiate the AI search operation
|
|
30
30
|
const searchPayload = {
|
|
31
31
|
actionName: "Search",
|
|
@@ -66,27 +66,29 @@ export const registerTool = (server, config) => {
|
|
|
66
66
|
const operationId = operationData.operationId;
|
|
67
67
|
// Step 2: Poll for results with exponential backoff
|
|
68
68
|
const resultData = await pRetry(async () => {
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
69
|
+
try {
|
|
70
|
+
const pollResponse = await client
|
|
71
|
+
.get()
|
|
72
|
+
.withAction(`projects/${environmentId}/early-access/ai-operation-result/${operationId}`)
|
|
73
|
+
.toPromise();
|
|
74
|
+
return pollResponse.data;
|
|
75
|
+
}
|
|
76
|
+
catch (error) {
|
|
77
|
+
if (error?.response?.status === 404) {
|
|
78
|
+
throw new Error("Operation result not available yet. Retrying.");
|
|
79
|
+
}
|
|
80
|
+
throw new AbortError(error);
|
|
76
81
|
}
|
|
77
|
-
return data;
|
|
78
82
|
}, {
|
|
83
|
+
// Worst-case retry time: ~1 minute
|
|
79
84
|
retries: 10,
|
|
80
85
|
minTimeout: 1000,
|
|
81
86
|
maxTimeout: 10000,
|
|
82
87
|
factor: 1.5,
|
|
83
88
|
});
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
});
|
|
88
|
-
}
|
|
89
|
-
throw new Error(`Search operation error: ${resultData.message}. Operation ID: ${operationId}`);
|
|
89
|
+
return createMcpToolSuccessResponse({
|
|
90
|
+
result: resultData,
|
|
91
|
+
});
|
|
90
92
|
}
|
|
91
93
|
catch (error) {
|
|
92
94
|
return handleMcpToolError(error, "AI-powered Variant Search");
|
|
@@ -2,7 +2,7 @@ import { z } from "zod";
|
|
|
2
2
|
import { createMapiClient } from "../clients/kontentClients.js";
|
|
3
3
|
import { handleMcpToolError } from "../utils/errorHandler.js";
|
|
4
4
|
import { createMcpToolSuccessResponse } from "../utils/responseHelper.js";
|
|
5
|
-
export const registerTool = (server
|
|
5
|
+
export const registerTool = (server) => {
|
|
6
6
|
server.tool("unpublish-variant-mapi", "Unpublish or schedule unpublishing of a language variant of a content item in Kontent.ai. This operation can either immediately unpublish the variant (making it unavailable through the Delivery API) or schedule it for unpublishing at a specific future date and time. For immediate unpublishing: the variant is unpublished right away and moves to the 'Archived' workflow step, becoming unavailable through the Delivery API. For scheduled unpublishing: the variant remains published but is scheduled to be automatically unpublished at the specified time. The variant must currently be in the 'Published' state for this operation to succeed.", {
|
|
7
7
|
itemId: z
|
|
8
8
|
.string()
|
|
@@ -22,7 +22,7 @@ export const registerTool = (server, config) => {
|
|
|
22
22
|
.optional()
|
|
23
23
|
.describe("The timezone identifier for displaying the scheduled time in the Kontent.ai UI (e.g., 'America/New_York', 'Europe/London', 'UTC'). This parameter is used for scheduled unpublishing to specify the timezone context for the scheduled_to parameter. If not provided, the system will use the default timezone. This helps content creators understand when content will be unpublished in their local context."),
|
|
24
24
|
}, async ({ itemId, languageId, scheduledTo, displayTimezone }, { authInfo: { token, clientId } = {} }) => {
|
|
25
|
-
const client = createMapiClient(clientId, token
|
|
25
|
+
const client = createMapiClient(clientId, token);
|
|
26
26
|
try {
|
|
27
27
|
// Validate that displayTimezone can only be used with scheduledTo
|
|
28
28
|
if (displayTimezone && !scheduledTo) {
|
|
@@ -2,7 +2,7 @@ import { z } from "zod";
|
|
|
2
2
|
import { createMapiClient } from "../clients/kontentClients.js";
|
|
3
3
|
import { handleMcpToolError } from "../utils/errorHandler.js";
|
|
4
4
|
import { createMcpToolSuccessResponse } from "../utils/responseHelper.js";
|
|
5
|
-
export const registerTool = (server
|
|
5
|
+
export const registerTool = (server) => {
|
|
6
6
|
server.tool("update-content-item-mapi", "Update existing Kontent.ai content item by internal ID via Management API. The content item must already exist - this tool will not create new items.", {
|
|
7
7
|
id: z.string().describe("Internal ID of the content item to update"),
|
|
8
8
|
name: z
|
|
@@ -20,7 +20,7 @@ export const registerTool = (server, config) => {
|
|
|
20
20
|
.optional()
|
|
21
21
|
.describe("Reference to a collection by id, codename, or external_id (optional)"),
|
|
22
22
|
}, async ({ id, name, collection }, { authInfo: { token, clientId } = {} }) => {
|
|
23
|
-
const client = createMapiClient(clientId, token
|
|
23
|
+
const client = createMapiClient(clientId, token);
|
|
24
24
|
try {
|
|
25
25
|
// First, verify the item exists by trying to get it
|
|
26
26
|
await client.viewContentItem().byItemId(id).toPromise();
|
|
@@ -3,7 +3,7 @@ import { createMapiClient } from "../clients/kontentClients.js";
|
|
|
3
3
|
import { languageVariantElementSchema } from "../schemas/contentItemSchemas.js";
|
|
4
4
|
import { handleMcpToolError } from "../utils/errorHandler.js";
|
|
5
5
|
import { createMcpToolSuccessResponse } from "../utils/responseHelper.js";
|
|
6
|
-
export const registerTool = (server
|
|
6
|
+
export const registerTool = (server) => {
|
|
7
7
|
server.tool("upsert-language-variant-mapi", "Create or update Kontent.ai language variant of a content item via Management API. This adds actual content to the content item elements. When updating an existing variant, only the provided elements will be modified.", {
|
|
8
8
|
itemId: z.string().describe("Internal ID of the content item"),
|
|
9
9
|
languageId: z
|
|
@@ -17,7 +17,7 @@ export const registerTool = (server, config) => {
|
|
|
17
17
|
.optional()
|
|
18
18
|
.describe("Internal ID of the workflow step (optional)"),
|
|
19
19
|
}, async ({ itemId, languageId, elements, workflow_step_id }, { authInfo: { token, clientId } = {} }) => {
|
|
20
|
-
const client = createMapiClient(clientId, token
|
|
20
|
+
const client = createMapiClient(clientId, token);
|
|
21
21
|
const data = {
|
|
22
22
|
elements,
|
|
23
23
|
};
|
|
@@ -46,12 +46,23 @@ export const handleMcpToolError = (error, context) => {
|
|
|
46
46
|
};
|
|
47
47
|
}
|
|
48
48
|
// Handle network or other HTTP errors
|
|
49
|
-
if (error
|
|
49
|
+
if (error.isAxiosError) {
|
|
50
|
+
if (error.response) {
|
|
51
|
+
return {
|
|
52
|
+
content: [
|
|
53
|
+
{
|
|
54
|
+
type: "text",
|
|
55
|
+
text: `${contextPrefix}HTTP Error ${error?.response?.status}: ${error?.response?.statusText || "Unknown HTTP error"}\n\nResponse: ${JSON.stringify(error?.response?.data)}`,
|
|
56
|
+
},
|
|
57
|
+
],
|
|
58
|
+
isError: true,
|
|
59
|
+
};
|
|
60
|
+
}
|
|
50
61
|
return {
|
|
51
62
|
content: [
|
|
52
63
|
{
|
|
53
64
|
type: "text",
|
|
54
|
-
text: `${contextPrefix}HTTP Error ${error.
|
|
65
|
+
text: `${contextPrefix}HTTP Error ${error.message}`,
|
|
55
66
|
},
|
|
56
67
|
],
|
|
57
68
|
isError: true,
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@kontent-ai/mcp-server",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.21.0",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"scripts": {
|
|
6
6
|
"build": "rimraf build && tsc",
|
|
@@ -8,8 +8,8 @@
|
|
|
8
8
|
"start:shttp": "node build/bin.js shttp",
|
|
9
9
|
"dev:stdio": "tsx watch src/bin.ts stdio",
|
|
10
10
|
"dev:shttp": "tsx watch src/bin.ts shttp",
|
|
11
|
-
"format": "cross-env node node_modules/@biomejs/biome/bin/biome ci ./
|
|
12
|
-
"format:fix": "cross-env node node_modules/@biomejs/biome/bin/biome check ./ --fix --unsafe
|
|
11
|
+
"format": "cross-env node node_modules/@biomejs/biome/bin/biome ci ./",
|
|
12
|
+
"format:fix": "cross-env node node_modules/@biomejs/biome/bin/biome check ./ --fix --unsafe"
|
|
13
13
|
},
|
|
14
14
|
"bin": {
|
|
15
15
|
"@kontent-ai/mcp-server": "./build/bin.js"
|
|
@@ -21,10 +21,7 @@
|
|
|
21
21
|
"author": "Jiri Lojda",
|
|
22
22
|
"license": "MIT",
|
|
23
23
|
"dependencies": {
|
|
24
|
-
"@
|
|
25
|
-
"@azure/app-configuration-provider": "^2.2.0",
|
|
26
|
-
"@azure/identity": "^4.11.1",
|
|
27
|
-
"@kontent-ai/management-sdk": "^7.9.0",
|
|
24
|
+
"@kontent-ai/management-sdk": "^7.11.0",
|
|
28
25
|
"@modelcontextprotocol/sdk": "^1.12.0",
|
|
29
26
|
"applicationinsights": "^2.9.8",
|
|
30
27
|
"dotenv": "^16.5.0",
|
|
@@ -1,51 +0,0 @@
|
|
|
1
|
-
import { load } from "@azure/app-configuration-provider";
|
|
2
|
-
import { DefaultAzureCredential } from "@azure/identity";
|
|
3
|
-
function loadIndexedEnvVars(prefix) {
|
|
4
|
-
const values = [];
|
|
5
|
-
let index = 0;
|
|
6
|
-
while (true) {
|
|
7
|
-
const envVar = process.env[`${prefix}__${index}`];
|
|
8
|
-
if (!envVar)
|
|
9
|
-
break;
|
|
10
|
-
const trimmed = envVar.trim();
|
|
11
|
-
if (trimmed) {
|
|
12
|
-
values.push(trimmed);
|
|
13
|
-
}
|
|
14
|
-
index++;
|
|
15
|
-
}
|
|
16
|
-
return values;
|
|
17
|
-
}
|
|
18
|
-
function getConfigValue(configMap, key) {
|
|
19
|
-
const value = configMap.get(key);
|
|
20
|
-
return value && typeof value === "string" ? value : undefined;
|
|
21
|
-
}
|
|
22
|
-
export async function loadAppConfiguration() {
|
|
23
|
-
try {
|
|
24
|
-
const appConfigEndpoint = process.env.ConfigStore__Endpoints__0;
|
|
25
|
-
const labels = loadIndexedEnvVars("ConfigStore__Labels");
|
|
26
|
-
if (!appConfigEndpoint || labels.length === 0) {
|
|
27
|
-
return null;
|
|
28
|
-
}
|
|
29
|
-
const selectors = labels.flatMap((label) => [
|
|
30
|
-
{ keyFilter: "ApplicationInsights:*", labelFilter: label },
|
|
31
|
-
{ keyFilter: "Global:Runtime:*", labelFilter: label },
|
|
32
|
-
{ keyFilter: "Draft:ManageApi:*", labelFilter: label },
|
|
33
|
-
]);
|
|
34
|
-
const credential = new DefaultAzureCredential();
|
|
35
|
-
const configMap = await load(appConfigEndpoint, credential, {
|
|
36
|
-
selectors: selectors,
|
|
37
|
-
keyVaultOptions: {
|
|
38
|
-
credential: credential,
|
|
39
|
-
},
|
|
40
|
-
});
|
|
41
|
-
return {
|
|
42
|
-
applicationInsightsConnectionString: getConfigValue(configMap, "ApplicationInsights:ConnectionString"),
|
|
43
|
-
projectLocation: getConfigValue(configMap, "Global:Runtime:ProjectLocation"),
|
|
44
|
-
manageApiUrl: getConfigValue(configMap, "Draft:ManageApi:Url"),
|
|
45
|
-
};
|
|
46
|
-
}
|
|
47
|
-
catch (error) {
|
|
48
|
-
console.log("Failed to load App Configuration:", error);
|
|
49
|
-
return null;
|
|
50
|
-
}
|
|
51
|
-
}
|