@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 +25 -33
- package/build/bin.js +1 -51
- package/build/server.js +4 -2
- package/build/tools/create-language-variant-mapi.js +44 -0
- package/build/tools/{upsert-language-variant-mapi.js → update-language-variant-mapi.js} +2 -2
- package/build/utils/extractUserIdFromToken.js +19 -0
- package/package.json +3 -3
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
|
|
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
|
-
* **
|
|
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
|
|
157
|
+
The server supports two modes, each tied to its transport:
|
|
157
158
|
|
|
158
|
-
|
|
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
|
-
|
|
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
|
|
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
|
|
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
|
-
|
|
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
|
|
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
|
-
|
|
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("
|
|
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
|
|
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.
|
|
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.
|
|
33
|
-
"@modelcontextprotocol/sdk": "^1.
|
|
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",
|