@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.
Files changed (37) hide show
  1. package/README.md +4 -1
  2. package/build/bin.js +9 -13
  3. package/build/clients/kontentClients.js +3 -3
  4. package/build/schemas/filterVariantSchemas.js +21 -10
  5. package/build/server.js +29 -29
  6. package/build/telemetry/applicationInsights.js +12 -8
  7. package/build/tools/add-content-item-mapi.js +2 -2
  8. package/build/tools/add-content-type-mapi.js +2 -2
  9. package/build/tools/add-content-type-snippet-mapi.js +2 -2
  10. package/build/tools/add-taxonomy-group-mapi.js +2 -2
  11. package/build/tools/change-variant-workflow-step-mapi.js +2 -2
  12. package/build/tools/create-variant-version-mapi.js +2 -2
  13. package/build/tools/delete-content-item-mapi.js +2 -2
  14. package/build/tools/delete-content-type-mapi.js +2 -2
  15. package/build/tools/delete-language-variant-mapi.js +2 -2
  16. package/build/tools/filter-variants-mapi.js +15 -18
  17. package/build/tools/get-asset-mapi.js +2 -2
  18. package/build/tools/get-item-mapi.js +2 -2
  19. package/build/tools/get-taxonomy-group-mapi.js +2 -2
  20. package/build/tools/get-type-mapi.js +2 -2
  21. package/build/tools/get-type-snippet-mapi.js +2 -2
  22. package/build/tools/get-variant-mapi.js +2 -2
  23. package/build/tools/list-assets-mapi.js +2 -2
  24. package/build/tools/list-content-type-snippets-mapi.js +2 -2
  25. package/build/tools/list-content-types-mapi.js +2 -2
  26. package/build/tools/list-languages-mapi.js +2 -2
  27. package/build/tools/list-taxonomy-groups-mapi.js +2 -2
  28. package/build/tools/list-workflows-mapi.js +2 -2
  29. package/build/tools/patch-content-type-mapi.js +2 -2
  30. package/build/tools/publish-variant-mapi.js +2 -2
  31. package/build/tools/search-variants-mapi.js +19 -17
  32. package/build/tools/unpublish-variant-mapi.js +2 -2
  33. package/build/tools/update-content-item-mapi.js +2 -2
  34. package/build/tools/upsert-language-variant-mapi.js +2 -2
  35. package/build/utils/errorHandler.js +13 -2
  36. package/package.json +4 -7
  37. 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(config) {
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(config);
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(config);
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(config) {
147
- const { server } = createServer(config);
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
- const config = await loadAppConfiguration();
154
- if (config) {
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(config);
162
+ await startStdio();
167
163
  }
168
164
  else if (transportType === "shttp") {
169
- await startStreamableHTTP(config);
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, config, additionalHeaders) => {
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: config ? `${config.manageApiUrl}v2` : undefined,
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
- const userReferenceSchema = z.object({
4
- id: z.string().optional().describe("User identifier"),
5
- email: z.string().email().optional().describe("User email address"),
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", "completed", "not_translated", "all_done"]))
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", "due", "last_modified"])
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
- continuation_token: z
68
- .string()
78
+ include_content: z
79
+ .boolean()
69
80
  .optional()
70
- .describe("Continuation token for pagination"),
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 = (config) => {
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, config);
45
- registerGetVariantMapi(server, config);
46
- registerGetTypeMapi(server, config);
47
- registerListContentTypesMapi(server, config);
48
- registerDeleteContentTypeMapi(server, config);
49
- registerListLanguagesMapi(server, config);
50
- registerGetAssetMapi(server, config);
51
- registerListAssetsMapi(server, config);
52
- registerAddContentTypeMapi(server, config);
53
- registerPatchContentTypeMapi(server, config);
54
- registerAddContentTypeSnippetMapi(server, config);
55
- registerGetTypeSnippetMapi(server, config);
56
- registerListContentTypeSnippetsMapi(server, config);
57
- registerAddTaxonomyGroupMapi(server, config);
58
- registerListTaxonomyGroupsMapi(server, config);
59
- registerGetTaxonomyGroupMapi(server, config);
60
- registerAddContentItemMapi(server, config);
61
- registerUpdateContentItemMapi(server, config);
62
- registerDeleteContentItemMapi(server, config);
63
- registerUpsertLanguageVariantMapi(server, config);
64
- registerCreateVariantVersionMapi(server, config);
65
- registerDeleteLanguageVariantMapi(server, config);
66
- registerListWorkflowsMapi(server, config);
67
- registerChangeVariantWorkflowStepMapi(server, config);
68
- registerFilterVariantsMapi(server, config);
69
- registerSearchVariantsMapi(server, config);
70
- registerPublishVariantMapi(server, config);
71
- registerUnpublishVariantMapi(server, config);
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 instanceof AxiosError) {
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(config) {
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
- config.projectLocation || "unknown";
132
+ process.env.projectLocation || "unknown";
130
133
  }
131
134
  return true;
132
135
  };
133
136
  }
134
- export function initializeApplicationInsights(config) {
137
+ export function initializeApplicationInsights() {
135
138
  try {
136
- if (!config?.applicationInsightsConnectionString) {
139
+ const connectionString = process.env.appInsightsConnectionString;
140
+ if (!connectionString) {
137
141
  return;
138
142
  }
139
143
  appInsights
140
- .setup(config.applicationInsightsConnectionString)
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(config));
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, config) => {
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, config);
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, config) => {
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, config);
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, config) => {
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, config);
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, config) => {
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, config);
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, config) => {
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, config);
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, config) => {
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, config);
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, config) => {
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, config);
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, config) => {
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, config);
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, config) => {
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, config);
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, config) => {
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`, filterVariantsSchema.shape, async ({ search_phrase, content_types, contributors, has_no_contributors, completion_statuses, language, workflow_steps, taxonomy_groups, order_by, order_direction, continuation_token, }, { authInfo: { token, clientId } = {} }) => {
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 additionalHeaders = continuation_token
20
- ? [{ header: "X-Continuation", value: continuation_token }]
21
- : undefined;
22
- const client = createMapiClient(environmentId, token, config, additionalHeaders);
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 === "desc" ? "Descending" : "Ascending",
37
+ direction: order_direction || "asc",
38
38
  }
39
- : null,
40
- };
41
- const response = await client
42
- .post()
43
- .withAction(`projects/${environmentId}/early-access/variants/filter`)
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 Search");
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, config) => {
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, config);
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, config) => {
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, config);
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, config) => {
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, config);
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, config) => {
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, config);
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, config) => {
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, config);
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, config) => {
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, config);
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, config) => {
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, config);
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, config) => {
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, config);
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, config) => {
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, config);
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, config) => {
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, config);
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, config) => {
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, config);
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, config) => {
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, config);
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, config) => {
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, config);
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, config) => {
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, config);
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, config) => {
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, config);
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
- const pollResponse = await client
70
- .get()
71
- .withAction(`projects/${environmentId}/early-access/ai-operation/${operationId}`)
72
- .toPromise();
73
- const data = pollResponse.data;
74
- if (data.message.includes("still in progress")) {
75
- throw new Error("Operation still in progress");
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
- if (resultData.message.includes("completed successfully")) {
85
- return createMcpToolSuccessResponse({
86
- result: resultData.result,
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, config) => {
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, config);
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, config) => {
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, config);
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, config) => {
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, config);
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?.response) {
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.response.status}: ${error.response.statusText || "Unknown HTTP error"}\n\nResponse: ${JSON.stringify(error.response.data)}`,
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.19.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 ./ --config-path=./biome.json",
12
- "format:fix": "cross-env node node_modules/@biomejs/biome/bin/biome check ./ --fix --unsafe --config-path=./biome.json"
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
- "@azure/app-configuration": "^1.9.0",
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
- }