@kontent-ai/mcp-server 0.28.1 → 0.30.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 CHANGED
@@ -98,10 +98,11 @@ npx @kontent-ai/mcp-server@latest shttp
98
98
  * **list-variants-type-mapi** – List Kontent.ai language variants by content type from Management API (paginated)
99
99
  * **list-variants-components-type-mapi** – List Kontent.ai language variants containing components of a specific content type from Management API (paginated)
100
100
  * **list-variants-space-mapi** – List Kontent.ai language variants by space from Management API (paginated)
101
- * **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
101
+ * **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 create-language-variant-mapi to add content to the item
102
102
  * **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
103
103
  * **delete-content-item-mapi** – Delete Kontent.ai content item by internal ID from Management API
104
- * **upsert-language-variant-mapi** – Create or update Kontent.ai language variant of a content item via Management API. Element values must fulfill limitations and guidelines defined in content type. When updating, only provided elements will be modified
104
+ * **create-language-variant-mapi** – Create Kontent.ai variant assigning current user as contributor. Element values must fulfill limitations and guidelines defined in content type
105
+ * **update-language-variant-mapi** – Update Kontent.ai language variant of a content item via Management API. Element values must fulfill limitations and guidelines defined in content type. Only provided elements will be modified
105
106
  * **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
106
107
  * **delete-language-variant-mapi** – Delete Kontent.ai language variant from Management API
107
108
  * **filter-variants-mapi** – Filter Kontent.ai items with variants returning references (item ID + language ID). Use for exact keyword matching and finding specific terms in content. Supports full filtering capabilities (content types, workflow steps, taxonomies, spaces, collections, publishing states, etc.). Returns paginated results with continuation token for fetching subsequent pages. Use bulk-get-items-variants-mapi to retrieve full content for matched items
@@ -153,28 +154,39 @@ npx @kontent-ai/mcp-server@latest shttp
153
154
 
154
155
  ## ⚙️ Configuration
155
156
 
156
- The server supports two configuration modes:
157
+ The server supports two modes, each tied to its transport:
157
158
 
158
- ### Single-Tenant Mode (Default)
159
+ | Transport | Mode | Authentication | Use Case |
160
+ |-----------|------|----------------|----------|
161
+ | **STDIO** | Single-tenant | Environment variables | Local communication with a single Kontent.ai environment |
162
+ | **Streamable HTTP** | Multi-tenant | Bearer token per request | Remote/shared server handling multiple environments |
159
163
 
160
- For single-tenant mode, configure environment variables:
164
+ ### Single-Tenant Mode (STDIO)
165
+
166
+ Configure credentials via environment variables:
161
167
 
162
168
  | Variable | Description | Required |
163
169
  |----------|-------------|----------|
164
170
  | KONTENT_API_KEY | Your Kontent.ai Management API key | ✅ |
165
171
  | KONTENT_ENVIRONMENT_ID | Your environment ID | ✅ |
166
- | PORT | Port for HTTP transport (defaults to 3001) | ❌ |
167
172
  | appInsightsConnectionString | Application Insights connection string for telemetry | ❌ |
168
173
  | projectLocation | Project location identifier for telemetry tracking | ❌ |
169
174
  | manageApiUrl | Custom Management API base URL (for preview environments) | ❌ |
170
175
 
171
- ### Multi-Tenant Mode
176
+ ### Multi-Tenant Mode (Streamable HTTP)
172
177
 
173
- For multi-tenant mode (Streamable HTTP only), the server accepts:
178
+ For the Streamable HTTP transport, credentials are provided per request:
174
179
  - **Environment ID** as a URL path parameter: `/{environmentId}/mcp`
175
180
  - **API Key** via Bearer token in the Authorization header: `Authorization: Bearer <api-key>`
176
181
 
177
- This mode allows a single server instance to handle requests for multiple Kontent.ai environments securely without requiring environment variables.
182
+ This allows a single server instance to handle requests for multiple Kontent.ai environments without requiring credential environment variables.
183
+
184
+ | Variable | Description | Required |
185
+ |----------|-------------|----------|
186
+ | PORT | Port for HTTP transport (defaults to 3001) | ❌ |
187
+ | appInsightsConnectionString | Application Insights connection string for telemetry | ❌ |
188
+ | projectLocation | Project location identifier for telemetry tracking | ❌ |
189
+ | manageApiUrl | Custom Management API base URL (for preview environments) | ❌ |
178
190
 
179
191
  ## 🚀 Transport Options
180
192
 
@@ -195,36 +207,16 @@ To run the server with STDIO transport, configure your MCP client with:
195
207
  }
196
208
  ```
197
209
 
198
- ### 🌊 Streamable HTTP Transport
210
+ ### 🌊 Streamable HTTP Transport (Multi-Tenant)
199
211
 
200
- For Streamable HTTP transport, first start the server:
212
+ Streamable HTTP transport serves multiple Kontent.ai environments from a single server instance. Each request provides credentials via URL path parameters and Bearer authentication.
213
+
214
+ First start the server:
201
215
 
202
216
  ```bash
203
217
  npx @kontent-ai/mcp-server@latest shttp
204
218
  ```
205
219
 
206
- #### Single-Tenant Mode
207
-
208
- With environment variables in a `.env` file, or otherwise accessible to the process:
209
- ```env
210
- KONTENT_API_KEY=<management-api-key>
211
- KONTENT_ENVIRONMENT_ID=<environment-id>
212
- PORT=3001 # optional, defaults to 3001
213
- ```
214
-
215
- Then configure your MCP client:
216
- ```json
217
- {
218
- "kontent-ai-http": {
219
- "url": "http://localhost:3001/mcp"
220
- }
221
- }
222
- ```
223
-
224
- #### Multi-Tenant Mode
225
-
226
- No environment variables required. The server accepts requests for multiple environments using URL path parameters and Bearer authentication.
227
-
228
220
  <details>
229
221
  <summary><strong>VS Code</strong></summary>
230
222
 
package/build/bin.js CHANGED
@@ -12,55 +12,6 @@ const version = packageJson.version;
12
12
  async function startStreamableHTTP() {
13
13
  const app = express();
14
14
  app.use(express.json());
15
- app.post("/mcp", async (req, res) => {
16
- try {
17
- const { server } = createServer();
18
- const transport = new StreamableHTTPServerTransport({
19
- sessionIdGenerator: undefined,
20
- });
21
- res.on("close", () => {
22
- console.log("Request closed");
23
- transport.close();
24
- server.close();
25
- });
26
- await server.connect(transport);
27
- await transport.handleRequest(req, res, req.body);
28
- }
29
- catch (error) {
30
- console.error("Error handling MCP request:", error);
31
- trackException(error, "MCP Request Handler");
32
- if (!res.headersSent) {
33
- res.status(500).json({
34
- jsonrpc: "2.0",
35
- error: {
36
- code: -32603,
37
- message: "Internal server error",
38
- },
39
- id: null,
40
- });
41
- }
42
- }
43
- });
44
- app.get("/mcp", async (_, res) => {
45
- res.writeHead(405).end(JSON.stringify({
46
- jsonrpc: "2.0",
47
- error: {
48
- code: -32000,
49
- message: "Method not allowed.",
50
- },
51
- id: null,
52
- }));
53
- });
54
- app.delete("/mcp", async (_, res) => {
55
- res.writeHead(405).end(JSON.stringify({
56
- jsonrpc: "2.0",
57
- error: {
58
- code: -32000,
59
- message: "Method not allowed.",
60
- },
61
- id: null,
62
- }));
63
- });
64
15
  app.post("/:environmentId/mcp", async (req, res) => {
65
16
  try {
66
17
  const { environmentId } = req.params;
@@ -144,8 +95,7 @@ async function startStreamableHTTP() {
144
95
  const PORT = process.env.PORT || 3001;
145
96
  app.listen(PORT, () => {
146
97
  console.log(`Kontent.ai MCP Server v${version} (Streamable HTTP) running on port ${PORT}.
147
- Available endpoints:
148
- /mcp
98
+ Available endpoint:
149
99
  /{environmentId}/mcp (requires Bearer authentication)`);
150
100
  });
151
101
  }
package/build/server.js CHANGED
@@ -9,6 +9,7 @@ import { registerTool as registerAddTaxonomyGroupMapi } from "./tools/add-taxono
9
9
  import { registerTool as registerAddWorkflowMapi } from "./tools/add-workflow-mapi.js";
10
10
  import { registerTool as registerBulkGetItemsVariantsMapi } from "./tools/bulk-get-items-variants-mapi.js";
11
11
  import { registerTool as registerChangeVariantWorkflowStepMapi } from "./tools/change-variant-workflow-step-mapi.js";
12
+ import { registerTool as registerCreateLanguageVariantMapi } from "./tools/create-language-variant-mapi.js";
12
13
  import { registerTool as registerCreateVariantVersionMapi } from "./tools/create-variant-version-mapi.js";
13
14
  import { registerTool as registerDeleteContentItemMapi } from "./tools/delete-content-item-mapi.js";
14
15
  import { registerTool as registerDeleteContentTypeMapi } from "./tools/delete-content-type-mapi.js";
@@ -53,8 +54,8 @@ import { registerTool as registerSearchVariantsMapi } from "./tools/search-varia
53
54
  import { registerTool as registerUnpublishVariantMapi } from "./tools/unpublish-variant-mapi.js";
54
55
  import { registerTool as registerUpdateAssetMapi } from "./tools/update-asset-mapi.js";
55
56
  import { registerTool as registerUpdateContentItemMapi } from "./tools/update-content-item-mapi.js";
57
+ import { registerTool as registerUpdateLanguageVariantMapi } from "./tools/update-language-variant-mapi.js";
56
58
  import { registerTool as registerUpdateWorkflowMapi } from "./tools/update-workflow-mapi.js";
57
- import { registerTool as registerUpsertLanguageVariantMapi } from "./tools/upsert-language-variant-mapi.js";
58
59
  // Create server instance
59
60
  export const createServer = () => {
60
61
  const server = new McpServer({
@@ -104,7 +105,8 @@ export const createServer = () => {
104
105
  registerAddContentItemMapi(server);
105
106
  registerUpdateContentItemMapi(server);
106
107
  registerDeleteContentItemMapi(server);
107
- registerUpsertLanguageVariantMapi(server);
108
+ registerCreateLanguageVariantMapi(server);
109
+ registerUpdateLanguageVariantMapi(server);
108
110
  registerCreateVariantVersionMapi(server);
109
111
  registerDeleteLanguageVariantMapi(server);
110
112
  registerListWorkflowsMapi(server);
@@ -0,0 +1,44 @@
1
+ import { z } from "zod";
2
+ import { createMapiClient } from "../clients/kontentClients.js";
3
+ import { languageVariantElementSchema } from "../schemas/contentItemSchemas.js";
4
+ import { handleMcpToolError } from "../utils/errorHandler.js";
5
+ import { extractUserIdFromToken } from "../utils/extractUserIdFromToken.js";
6
+ import { createMcpToolSuccessResponse } from "../utils/responseHelper.js";
7
+ export const registerTool = (server) => {
8
+ server.tool("create-language-variant-mapi", "Create Kontent.ai variant. Element values must fulfill limitations and guidelines defined in content type.", {
9
+ itemId: z.string().describe("Content item ID"),
10
+ languageId: z
11
+ .string()
12
+ .describe("Language variant ID (default: 00000000-0000-0000-0000-000000000000)"),
13
+ elements: z
14
+ .array(languageVariantElementSchema)
15
+ .describe("Content elements array"),
16
+ workflow_step_id: z.string().optional().describe("Workflow step ID"),
17
+ }, async ({ itemId, languageId, elements, workflow_step_id }, { authInfo: { token, clientId } = {} }) => {
18
+ const client = createMapiClient(clientId, token);
19
+ const data = {
20
+ elements,
21
+ };
22
+ if (workflow_step_id) {
23
+ data.workflow_step = { id: workflow_step_id };
24
+ }
25
+ if (token) {
26
+ const userId = extractUserIdFromToken(token);
27
+ if (userId) {
28
+ data.contributors = [{ id: userId }];
29
+ }
30
+ }
31
+ try {
32
+ const response = await client
33
+ .upsertLanguageVariant()
34
+ .byItemId(itemId)
35
+ .byLanguageId(languageId)
36
+ .withData(() => data)
37
+ .toPromise();
38
+ return createMcpToolSuccessResponse(response.rawData);
39
+ }
40
+ catch (error) {
41
+ return handleMcpToolError(error, "Language Variant Create");
42
+ }
43
+ });
44
+ };
@@ -4,7 +4,7 @@ import { languageVariantElementSchema } from "../schemas/contentItemSchemas.js";
4
4
  import { handleMcpToolError } from "../utils/errorHandler.js";
5
5
  import { createMcpToolSuccessResponse } from "../utils/responseHelper.js";
6
6
  export const registerTool = (server) => {
7
- server.tool("upsert-language-variant-mapi", "Create or update Kontent.ai variant. Element values must fulfill limitations and guidelines defined in content type.", {
7
+ server.tool("update-language-variant-mapi", "Update Kontent.ai variant. Element values must fulfill limitations and guidelines defined in content type.", {
8
8
  itemId: z.string().describe("Content item ID"),
9
9
  languageId: z
10
10
  .string()
@@ -31,7 +31,7 @@ export const registerTool = (server) => {
31
31
  return createMcpToolSuccessResponse(response.rawData);
32
32
  }
33
33
  catch (error) {
34
- return handleMcpToolError(error, "Language Variant Upsert");
34
+ return handleMcpToolError(error, "Language Variant Update");
35
35
  }
36
36
  });
37
37
  };
@@ -0,0 +1,19 @@
1
+ /**
2
+ * Extracts user ID from JWT token's uid claim.
3
+ * Decodes the JWT payload using base64url.
4
+ * @param token JWT token string
5
+ * @returns User ID string or null if decoding fails
6
+ */
7
+ export const extractUserIdFromToken = (token) => {
8
+ try {
9
+ const parts = token.split(".");
10
+ if (parts.length !== 3) {
11
+ return null;
12
+ }
13
+ const payload = JSON.parse(Buffer.from(parts[1], "base64url").toString("utf-8"));
14
+ return typeof payload.uid === "string" ? payload.uid : null;
15
+ }
16
+ catch {
17
+ return null;
18
+ }
19
+ };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@kontent-ai/mcp-server",
3
- "version": "0.28.1",
3
+ "version": "0.30.0",
4
4
  "type": "module",
5
5
  "mcpName": "io.github.kontent-ai/mcp-server",
6
6
  "repository": {
@@ -29,8 +29,8 @@
29
29
  "author": "Jiri Lojda",
30
30
  "license": "MIT",
31
31
  "dependencies": {
32
- "@kontent-ai/management-sdk": "^8.3.0",
33
- "@modelcontextprotocol/sdk": "^1.25.2",
32
+ "@kontent-ai/management-sdk": "^8.3.1",
33
+ "@modelcontextprotocol/sdk": "^1.26.0",
34
34
  "applicationinsights": "^2.9.8",
35
35
  "dotenv": "^17.2.3",
36
36
  "express": "^5.2.1",