@kontent-ai/mcp-server 0.15.0 → 0.17.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 +114 -25
- package/build/bin.js +73 -24
- package/build/server.js +2 -0
- 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 +32 -0
- 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 +8 -5
- 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/unpublish-variant-mapi.js +2 -2
- package/build/tools/update-content-item-mapi.js +2 -2
- package/build/tools/upsert-language-variant-mapi.js +6 -4
- package/build/utils/errorHandler.js +2 -2
- package/build/utils/extractBearerToken.js +12 -0
- package/build/utils/isValidGuid.js +9 -0
- package/build/utils/responseHelper.js +1 -1
- package/package.json +1 -3
package/README.md
CHANGED
|
@@ -48,10 +48,10 @@ You can run the Kontent.ai MCP Server with npx:
|
|
|
48
48
|
npx @kontent-ai/mcp-server@latest stdio
|
|
49
49
|
```
|
|
50
50
|
|
|
51
|
-
####
|
|
51
|
+
#### Streamable HTTP Transport
|
|
52
52
|
|
|
53
53
|
```bash
|
|
54
|
-
npx @kontent-ai/mcp-server@latest
|
|
54
|
+
npx @kontent-ai/mcp-server@latest shttp
|
|
55
55
|
```
|
|
56
56
|
|
|
57
57
|
## 🛠️ Available Tools
|
|
@@ -88,7 +88,8 @@ npx @kontent-ai/mcp-server@latest sse
|
|
|
88
88
|
* **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
|
|
89
89
|
* **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
|
|
90
90
|
* **delete-content-item-mapi** – Delete Kontent.ai content item by internal ID from Management API
|
|
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.
|
|
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
|
+
* **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
|
|
92
93
|
* **delete-language-variant-mapi** – Delete Kontent.ai language variant from Management API
|
|
93
94
|
* **filter-variants-mapi** – Search and filter Kontent.ai language variants of content items using Management API
|
|
94
95
|
|
|
@@ -110,13 +111,25 @@ npx @kontent-ai/mcp-server@latest sse
|
|
|
110
111
|
|
|
111
112
|
## ⚙️ Configuration
|
|
112
113
|
|
|
113
|
-
The server
|
|
114
|
+
The server supports two configuration modes:
|
|
115
|
+
|
|
116
|
+
### Single-Tenant Mode (Default)
|
|
117
|
+
|
|
118
|
+
For single-tenant mode, configure environment variables:
|
|
114
119
|
|
|
115
120
|
| Variable | Description | Required |
|
|
116
121
|
|----------|-------------|----------|
|
|
117
122
|
| KONTENT_API_KEY | Your Kontent.ai Management API key | ✅ |
|
|
118
123
|
| KONTENT_ENVIRONMENT_ID | Your environment ID | ✅ |
|
|
119
|
-
| PORT | Port for
|
|
124
|
+
| PORT | Port for HTTP transport (defaults to 3001) | ❌ |
|
|
125
|
+
|
|
126
|
+
### Multi-Tenant Mode
|
|
127
|
+
|
|
128
|
+
For multi-tenant mode (Streamable HTTP only), the server accepts:
|
|
129
|
+
- **Environment ID** as a URL path parameter: `/{environmentId}/mcp`
|
|
130
|
+
- **API Key** via Bearer token in the Authorization header: `Authorization: Bearer <api-key>`
|
|
131
|
+
|
|
132
|
+
This mode allows a single server instance to handle requests for multiple Kontent.ai environments securely without requiring environment variables.
|
|
120
133
|
|
|
121
134
|
## 🚀 Transport Options
|
|
122
135
|
|
|
@@ -137,14 +150,16 @@ To run the server with STDIO transport, configure your MCP client with:
|
|
|
137
150
|
}
|
|
138
151
|
```
|
|
139
152
|
|
|
140
|
-
###
|
|
153
|
+
### 🌊 Streamable HTTP Transport
|
|
141
154
|
|
|
142
|
-
For
|
|
155
|
+
For Streamable HTTP transport, first start the server:
|
|
143
156
|
|
|
144
157
|
```bash
|
|
145
|
-
npx @kontent-ai/mcp-server@latest
|
|
158
|
+
npx @kontent-ai/mcp-server@latest shttp
|
|
146
159
|
```
|
|
147
160
|
|
|
161
|
+
#### Single-Tenant Mode
|
|
162
|
+
|
|
148
163
|
With environment variables in a `.env` file, or otherwise accessible to the process:
|
|
149
164
|
```env
|
|
150
165
|
KONTENT_API_KEY=<management-api-key>
|
|
@@ -155,36 +170,112 @@ PORT=3001 # optional, defaults to 3001
|
|
|
155
170
|
Then configure your MCP client:
|
|
156
171
|
```json
|
|
157
172
|
{
|
|
158
|
-
"kontent-ai-
|
|
159
|
-
"url": "http://localhost:3001/
|
|
173
|
+
"kontent-ai-http": {
|
|
174
|
+
"url": "http://localhost:3001/mcp"
|
|
160
175
|
}
|
|
161
176
|
}
|
|
162
177
|
```
|
|
163
178
|
|
|
164
|
-
|
|
179
|
+
#### Multi-Tenant Mode
|
|
165
180
|
|
|
166
|
-
|
|
181
|
+
No environment variables required. The server accepts requests for multiple environments using URL path parameters and Bearer authentication.
|
|
167
182
|
|
|
168
|
-
|
|
169
|
-
|
|
183
|
+
##### VS Code Configuration
|
|
184
|
+
|
|
185
|
+
Create a `.vscode/mcp.json` file in your workspace:
|
|
186
|
+
|
|
187
|
+
```json
|
|
188
|
+
{
|
|
189
|
+
"servers": {
|
|
190
|
+
"kontent-ai-multi": {
|
|
191
|
+
"uri": "http://localhost:3001/<environment-id>/mcp",
|
|
192
|
+
"headers": {
|
|
193
|
+
"Authorization": "Bearer <management-api-key>"
|
|
194
|
+
}
|
|
195
|
+
}
|
|
196
|
+
}
|
|
197
|
+
}
|
|
170
198
|
```
|
|
171
199
|
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
200
|
+
For secure configuration with input prompts:
|
|
201
|
+
|
|
202
|
+
```json
|
|
203
|
+
{
|
|
204
|
+
"inputs": [
|
|
205
|
+
{
|
|
206
|
+
"id": "apiKey",
|
|
207
|
+
"type": "password",
|
|
208
|
+
"description": "Kontent.ai API Key"
|
|
209
|
+
},
|
|
210
|
+
{
|
|
211
|
+
"id": "environmentId",
|
|
212
|
+
"type": "text",
|
|
213
|
+
"description": "Environment ID"
|
|
214
|
+
}
|
|
215
|
+
],
|
|
216
|
+
"servers": {
|
|
217
|
+
"kontent-ai-multi": {
|
|
218
|
+
"uri": "http://localhost:3001/${inputs.environmentId}/mcp",
|
|
219
|
+
"headers": {
|
|
220
|
+
"Authorization": "Bearer ${inputs.apiKey}"
|
|
221
|
+
}
|
|
222
|
+
}
|
|
223
|
+
}
|
|
224
|
+
}
|
|
177
225
|
```
|
|
178
226
|
|
|
179
|
-
|
|
227
|
+
##### Claude Desktop Configuration
|
|
228
|
+
|
|
229
|
+
Update your Claude Desktop configuration file:
|
|
230
|
+
- **macOS**: `~/Library/Application Support/Claude/claude_desktop_config.json`
|
|
231
|
+
- **Windows**: `%APPDATA%\Claude\claude_desktop_config.json`
|
|
232
|
+
- **Linux**: `~/.config/Claude/claude_desktop_config.json`
|
|
233
|
+
|
|
234
|
+
Use `mcp-remote` as a proxy to add authentication headers:
|
|
235
|
+
|
|
180
236
|
```json
|
|
181
237
|
{
|
|
182
|
-
"
|
|
183
|
-
"
|
|
238
|
+
"mcpServers": {
|
|
239
|
+
"kontent-ai-multi": {
|
|
240
|
+
"command": "npx",
|
|
241
|
+
"args": [
|
|
242
|
+
"mcp-remote",
|
|
243
|
+
"http://localhost:3001/<environment-id>/mcp",
|
|
244
|
+
"--header",
|
|
245
|
+
"Authorization: Bearer <management-api-key>"
|
|
246
|
+
]
|
|
247
|
+
}
|
|
184
248
|
}
|
|
185
249
|
}
|
|
186
250
|
```
|
|
187
251
|
|
|
252
|
+
##### Claude Code Configuration
|
|
253
|
+
|
|
254
|
+
For Claude Code (claude.ai/code), add the server configuration:
|
|
255
|
+
|
|
256
|
+
```bash
|
|
257
|
+
# Add the multi-tenant server
|
|
258
|
+
claude mcp add \
|
|
259
|
+
--url "http://localhost:3001/<environment-id>/mcp" \
|
|
260
|
+
--header "Authorization: Bearer <management-api-key>" \
|
|
261
|
+
kontent-ai-multi
|
|
262
|
+
```
|
|
263
|
+
|
|
264
|
+
Or configure directly in the settings:
|
|
265
|
+
|
|
266
|
+
```json
|
|
267
|
+
{
|
|
268
|
+
"kontent-ai-multi": {
|
|
269
|
+
"url": "http://localhost:3001/<environment-id>/mcp",
|
|
270
|
+
"headers": {
|
|
271
|
+
"Authorization": "Bearer <management-api-key>"
|
|
272
|
+
}
|
|
273
|
+
}
|
|
274
|
+
}
|
|
275
|
+
```
|
|
276
|
+
|
|
277
|
+
**Important**: Replace `<environment-id>` with your actual Kontent.ai environment ID (GUID format) and `<management-api-key>` with your Management API key.
|
|
278
|
+
|
|
188
279
|
## 💻 Development
|
|
189
280
|
|
|
190
281
|
### 🛠 Local Installation
|
|
@@ -201,12 +292,10 @@ npm ci
|
|
|
201
292
|
npm run build
|
|
202
293
|
|
|
203
294
|
# Start the server
|
|
204
|
-
npm run start:sse # For SSE transport
|
|
205
295
|
npm run start:stdio # For STDIO transport
|
|
206
296
|
npm run start:shttp # For Streamable HTTP transport
|
|
207
297
|
|
|
208
298
|
# Start the server with automatic reloading (no need to build first)
|
|
209
|
-
npm run dev:sse # For SSE transport
|
|
210
299
|
npm run dev:stdio # For STDIO transport
|
|
211
300
|
npm run dev:shttp # For Streamable HTTP transport
|
|
212
301
|
```
|
|
@@ -231,7 +320,7 @@ For debugging, you can use the MCP inspector:
|
|
|
231
320
|
npx @modelcontextprotocol/inspector -e KONTENT_API_KEY=<key> -e KONTENT_ENVIRONMENT_ID=<env-id> node path/to/build/bin.js
|
|
232
321
|
```
|
|
233
322
|
|
|
234
|
-
Or use the MCP inspector on a running
|
|
323
|
+
Or use the MCP inspector on a running streamable HTTP server:
|
|
235
324
|
|
|
236
325
|
```bash
|
|
237
326
|
npx @modelcontextprotocol/inspector
|
package/build/bin.js
CHANGED
|
@@ -1,11 +1,12 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
import { SSEServerTransport } from "@modelcontextprotocol/sdk/server/sse.js";
|
|
3
2
|
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
|
|
4
3
|
import { StreamableHTTPServerTransport } from "@modelcontextprotocol/sdk/server/streamableHttp.js";
|
|
5
4
|
import "dotenv/config";
|
|
6
5
|
import express from "express";
|
|
7
6
|
import packageJson from "../package.json" with { type: "json" };
|
|
8
7
|
import { createServer } from "./server.js";
|
|
8
|
+
import { extractBearerToken } from "./utils/extractBearerToken.js";
|
|
9
|
+
import { isValidGuid } from "./utils/isValidGuid.js";
|
|
9
10
|
const version = packageJson.version;
|
|
10
11
|
async function startStreamableHTTP() {
|
|
11
12
|
const app = express();
|
|
@@ -39,7 +40,6 @@ async function startStreamableHTTP() {
|
|
|
39
40
|
}
|
|
40
41
|
});
|
|
41
42
|
app.get("/mcp", async (_, res) => {
|
|
42
|
-
console.log("Received GET MCP request");
|
|
43
43
|
res.writeHead(405).end(JSON.stringify({
|
|
44
44
|
jsonrpc: "2.0",
|
|
45
45
|
error: {
|
|
@@ -50,7 +50,6 @@ async function startStreamableHTTP() {
|
|
|
50
50
|
}));
|
|
51
51
|
});
|
|
52
52
|
app.delete("/mcp", async (_, res) => {
|
|
53
|
-
console.log("Received DELETE MCP request");
|
|
54
53
|
res.writeHead(405).end(JSON.stringify({
|
|
55
54
|
jsonrpc: "2.0",
|
|
56
55
|
error: {
|
|
@@ -60,25 +59,80 @@ async function startStreamableHTTP() {
|
|
|
60
59
|
id: null,
|
|
61
60
|
}));
|
|
62
61
|
});
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
62
|
+
app.post("/:environmentId/mcp", async (req, res) => {
|
|
63
|
+
try {
|
|
64
|
+
const { environmentId } = req.params;
|
|
65
|
+
if (!isValidGuid(environmentId)) {
|
|
66
|
+
res.status(400).json({
|
|
67
|
+
error: "Invalid environment ID format. Must be a valid GUID.",
|
|
68
|
+
});
|
|
69
|
+
return;
|
|
70
|
+
}
|
|
71
|
+
const { server } = createServer();
|
|
72
|
+
const transport = new StreamableHTTPServerTransport({
|
|
73
|
+
sessionIdGenerator: undefined,
|
|
74
|
+
});
|
|
75
|
+
res.on("close", () => {
|
|
76
|
+
console.log("Request closed");
|
|
77
|
+
transport.close();
|
|
78
|
+
server.close();
|
|
79
|
+
});
|
|
80
|
+
const authToken = extractBearerToken(req);
|
|
81
|
+
if (!authToken) {
|
|
82
|
+
res.status(401).json({
|
|
83
|
+
error: "Authorization header with Bearer token is required.",
|
|
84
|
+
});
|
|
85
|
+
return;
|
|
86
|
+
}
|
|
87
|
+
await server.connect(transport);
|
|
88
|
+
await transport.handleRequest(Object.assign(req, {
|
|
89
|
+
auth: {
|
|
90
|
+
clientId: environmentId,
|
|
91
|
+
token: authToken,
|
|
92
|
+
scopes: [],
|
|
93
|
+
},
|
|
94
|
+
}), res, req.body);
|
|
95
|
+
}
|
|
96
|
+
catch (error) {
|
|
97
|
+
console.error("Error handling MCP request:", error);
|
|
98
|
+
if (!res.headersSent) {
|
|
99
|
+
res.status(500).json({
|
|
100
|
+
jsonrpc: "2.0",
|
|
101
|
+
error: {
|
|
102
|
+
code: -32603,
|
|
103
|
+
message: "Internal server error",
|
|
104
|
+
},
|
|
105
|
+
id: null,
|
|
106
|
+
});
|
|
107
|
+
}
|
|
108
|
+
}
|
|
66
109
|
});
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
110
|
+
app.get("/:environmentId/mcp", async (_, res) => {
|
|
111
|
+
res.writeHead(405).end(JSON.stringify({
|
|
112
|
+
jsonrpc: "2.0",
|
|
113
|
+
error: {
|
|
114
|
+
code: -32000,
|
|
115
|
+
message: "Method not allowed.",
|
|
116
|
+
},
|
|
117
|
+
id: null,
|
|
118
|
+
}));
|
|
75
119
|
});
|
|
76
|
-
app.
|
|
77
|
-
|
|
120
|
+
app.delete("/:environmentId/mcp", async (_, res) => {
|
|
121
|
+
res.writeHead(405).end(JSON.stringify({
|
|
122
|
+
jsonrpc: "2.0",
|
|
123
|
+
error: {
|
|
124
|
+
code: -32000,
|
|
125
|
+
message: "Method not allowed.",
|
|
126
|
+
},
|
|
127
|
+
id: null,
|
|
128
|
+
}));
|
|
78
129
|
});
|
|
79
130
|
const PORT = process.env.PORT || 3001;
|
|
80
131
|
app.listen(PORT, () => {
|
|
81
|
-
console.log(`Kontent.ai MCP Server v${version} (
|
|
132
|
+
console.log(`Kontent.ai MCP Server v${version} (Streamable HTTP) running on port ${PORT}.
|
|
133
|
+
Available endpoints:
|
|
134
|
+
/mcp
|
|
135
|
+
/{environmentId}/mcp (requires Bearer authentication)`);
|
|
82
136
|
});
|
|
83
137
|
}
|
|
84
138
|
async function startStdio() {
|
|
@@ -91,18 +145,13 @@ async function main() {
|
|
|
91
145
|
const args = process.argv.slice(2);
|
|
92
146
|
const transportType = args[0]?.toLowerCase();
|
|
93
147
|
if (!transportType ||
|
|
94
|
-
(transportType !== "stdio" &&
|
|
95
|
-
|
|
96
|
-
transportType !== "shttp")) {
|
|
97
|
-
console.error("Please specify a valid transport type: stdio, sse, or shttp");
|
|
148
|
+
(transportType !== "stdio" && transportType !== "shttp")) {
|
|
149
|
+
console.error("Please specify a valid transport type: stdio or shttp");
|
|
98
150
|
process.exit(1);
|
|
99
151
|
}
|
|
100
152
|
if (transportType === "stdio") {
|
|
101
153
|
await startStdio();
|
|
102
154
|
}
|
|
103
|
-
else if (transportType === "sse") {
|
|
104
|
-
await startSSE();
|
|
105
|
-
}
|
|
106
155
|
else if (transportType === "shttp") {
|
|
107
156
|
await startStreamableHTTP();
|
|
108
157
|
}
|
package/build/server.js
CHANGED
|
@@ -5,6 +5,7 @@ import { registerTool as registerAddContentTypeMapi } from "./tools/add-content-
|
|
|
5
5
|
import { registerTool as registerAddContentTypeSnippetMapi } from "./tools/add-content-type-snippet-mapi.js";
|
|
6
6
|
import { registerTool as registerAddTaxonomyGroupMapi } from "./tools/add-taxonomy-group-mapi.js";
|
|
7
7
|
import { registerTool as registerChangeVariantWorkflowStepMapi } from "./tools/change-variant-workflow-step-mapi.js";
|
|
8
|
+
import { registerTool as registerCreateVariantVersionMapi } from "./tools/create-variant-version-mapi.js";
|
|
8
9
|
import { registerTool as registerDeleteContentItemMapi } from "./tools/delete-content-item-mapi.js";
|
|
9
10
|
import { registerTool as registerDeleteContentTypeMapi } from "./tools/delete-content-type-mapi.js";
|
|
10
11
|
import { registerTool as registerDeleteLanguageVariantMapi } from "./tools/delete-language-variant-mapi.js";
|
|
@@ -61,6 +62,7 @@ export const createServer = () => {
|
|
|
61
62
|
registerUpdateContentItemMapi(server);
|
|
62
63
|
registerDeleteContentItemMapi(server);
|
|
63
64
|
registerUpsertLanguageVariantMapi(server);
|
|
65
|
+
registerCreateVariantVersionMapi(server);
|
|
64
66
|
registerDeleteLanguageVariantMapi(server);
|
|
65
67
|
registerListWorkflowsMapi(server);
|
|
66
68
|
registerChangeVariantWorkflowStepMapi(server);
|
|
@@ -32,8 +32,8 @@ export const registerTool = (server) => {
|
|
|
32
32
|
})
|
|
33
33
|
.optional()
|
|
34
34
|
.describe("Reference to a collection by id, codename, or external_id (optional)"),
|
|
35
|
-
}, async ({ name, type, codename, external_id, collection }) => {
|
|
36
|
-
const client = createMapiClient();
|
|
35
|
+
}, async ({ name, type, codename, external_id, collection }, { authInfo: { token, clientId } = {} }) => {
|
|
36
|
+
const client = createMapiClient(clientId, token);
|
|
37
37
|
try {
|
|
38
38
|
const response = await client
|
|
39
39
|
.addContentItem()
|
|
@@ -21,8 +21,8 @@ export const registerTool = (server) => {
|
|
|
21
21
|
.array(contentGroupSchema)
|
|
22
22
|
.optional()
|
|
23
23
|
.describe("Array of content groups (optional)"),
|
|
24
|
-
}, async ({ name, codename, external_id, elements, content_groups }) => {
|
|
25
|
-
const client = createMapiClient();
|
|
24
|
+
}, async ({ name, codename, external_id, elements, content_groups }, { authInfo: { token, clientId } = {} }) => {
|
|
25
|
+
const client = createMapiClient(clientId, token);
|
|
26
26
|
try {
|
|
27
27
|
const response = await client
|
|
28
28
|
.addContentType()
|
|
@@ -17,8 +17,8 @@ export const registerTool = (server) => {
|
|
|
17
17
|
elements: z
|
|
18
18
|
.array(snippetElementSchema)
|
|
19
19
|
.describe("Array of elements that define the structure of the content type snippet"),
|
|
20
|
-
}, async ({ name, codename, external_id, elements }) => {
|
|
21
|
-
const client = createMapiClient();
|
|
20
|
+
}, async ({ name, codename, external_id, elements }, { authInfo: { token, clientId } = {} }) => {
|
|
21
|
+
const client = createMapiClient(clientId, token);
|
|
22
22
|
try {
|
|
23
23
|
const response = await client
|
|
24
24
|
.addContentTypeSnippet()
|
|
@@ -3,8 +3,8 @@ import { taxonomyGroupSchemas } from "../schemas/taxonomySchemas.js";
|
|
|
3
3
|
import { handleMcpToolError } from "../utils/errorHandler.js";
|
|
4
4
|
import { createMcpToolSuccessResponse } from "../utils/responseHelper.js";
|
|
5
5
|
export const registerTool = (server) => {
|
|
6
|
-
server.tool("add-taxonomy-group-mapi", "Add new Kontent.ai taxonomy group via Management API", taxonomyGroupSchemas, async (taxonomyGroup) => {
|
|
7
|
-
const client = createMapiClient();
|
|
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);
|
|
8
8
|
try {
|
|
9
9
|
const response = await client
|
|
10
10
|
.addTaxonomy()
|
|
@@ -20,8 +20,8 @@ export const registerTool = (server) => {
|
|
|
20
20
|
.string()
|
|
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
|
-
}, async ({ itemId, languageId, workflowId, workflowStepId }) => {
|
|
24
|
-
const client = createMapiClient();
|
|
23
|
+
}, async ({ itemId, languageId, workflowId, workflowStepId }, { authInfo: { token, clientId } = {} }) => {
|
|
24
|
+
const client = createMapiClient(clientId, token);
|
|
25
25
|
try {
|
|
26
26
|
const response = await client
|
|
27
27
|
.changeWorkflowOfLanguageVariant()
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import { z } from "zod";
|
|
2
|
+
import { createMapiClient } from "../clients/kontentClients.js";
|
|
3
|
+
import { handleMcpToolError } from "../utils/errorHandler.js";
|
|
4
|
+
import { createMcpToolSuccessResponse } from "../utils/responseHelper.js";
|
|
5
|
+
export const registerTool = (server) => {
|
|
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
|
+
itemId: z
|
|
8
|
+
.string()
|
|
9
|
+
.uuid()
|
|
10
|
+
.describe("Internal ID (UUID) of the content item whose language variant you want to create a new version of"),
|
|
11
|
+
languageId: z
|
|
12
|
+
.string()
|
|
13
|
+
.uuid()
|
|
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
|
+
}, async ({ itemId, languageId }, { authInfo: { token, clientId } = {} }) => {
|
|
16
|
+
const client = createMapiClient(clientId, token);
|
|
17
|
+
try {
|
|
18
|
+
const response = await client
|
|
19
|
+
.createNewVersionOfLanguageVariant()
|
|
20
|
+
.byItemId(itemId)
|
|
21
|
+
.byLanguageId(languageId)
|
|
22
|
+
.toPromise();
|
|
23
|
+
return createMcpToolSuccessResponse({
|
|
24
|
+
message: `Successfully created new version of language variant '${languageId}' for content item '${itemId}'`,
|
|
25
|
+
result: response.data,
|
|
26
|
+
});
|
|
27
|
+
}
|
|
28
|
+
catch (error) {
|
|
29
|
+
return handleMcpToolError(error, "Variant Version Creation");
|
|
30
|
+
}
|
|
31
|
+
});
|
|
32
|
+
};
|
|
@@ -5,8 +5,8 @@ import { createMcpToolSuccessResponse } from "../utils/responseHelper.js";
|
|
|
5
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
|
-
}, async ({ id }) => {
|
|
9
|
-
const client = createMapiClient();
|
|
8
|
+
}, async ({ id }, { authInfo: { token, clientId } = {} }) => {
|
|
9
|
+
const client = createMapiClient(clientId, token);
|
|
10
10
|
try {
|
|
11
11
|
const response = await client
|
|
12
12
|
.deleteContentItem()
|
|
@@ -5,8 +5,8 @@ import { createMcpToolSuccessResponse } from "../utils/responseHelper.js";
|
|
|
5
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
|
-
}, async ({ codename }) => {
|
|
9
|
-
const client = createMapiClient();
|
|
8
|
+
}, async ({ codename }, { authInfo: { token, clientId } = {} }) => {
|
|
9
|
+
const client = createMapiClient(clientId, token);
|
|
10
10
|
try {
|
|
11
11
|
const response = await client
|
|
12
12
|
.deleteContentType()
|
|
@@ -8,8 +8,8 @@ export const registerTool = (server) => {
|
|
|
8
8
|
languageId: z
|
|
9
9
|
.string()
|
|
10
10
|
.describe("Internal ID of the language variant to delete"),
|
|
11
|
-
}, async ({ itemId, languageId }) => {
|
|
12
|
-
const client = createMapiClient();
|
|
11
|
+
}, async ({ itemId, languageId }, { authInfo: { token, clientId } = {} }) => {
|
|
12
|
+
const client = createMapiClient(clientId, token);
|
|
13
13
|
try {
|
|
14
14
|
const response = await client
|
|
15
15
|
.deleteLanguageVariant()
|
|
@@ -3,7 +3,7 @@ import { handleMcpToolError } from "../utils/errorHandler.js";
|
|
|
3
3
|
import { createMcpToolSuccessResponse } from "../utils/responseHelper.js";
|
|
4
4
|
import { throwError } from "../utils/throwError.js";
|
|
5
5
|
export const registerTool = (server) => {
|
|
6
|
-
server.tool("filter-variants-mapi", "Search and filter Kontent.ai language variants of content items using Management API", filterVariantsSchema.shape, async ({ search_phrase, content_types, contributors, has_no_contributors, completion_statuses, language, workflow_steps, taxonomy_groups, order_by, order_direction, continuation_token, }) => {
|
|
6
|
+
server.tool("filter-variants-mapi", "Search and filter Kontent.ai language variants of content items using Management API", 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 } = {} }) => {
|
|
7
7
|
try {
|
|
8
8
|
const requestPayload = {
|
|
9
9
|
filters: {
|
|
@@ -23,10 +23,13 @@ export const registerTool = (server) => {
|
|
|
23
23
|
}
|
|
24
24
|
: null,
|
|
25
25
|
};
|
|
26
|
-
const environmentId = process.env.KONTENT_ENVIRONMENT_ID;
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
26
|
+
const environmentId = clientId ?? process.env.KONTENT_ENVIRONMENT_ID;
|
|
27
|
+
if (!environmentId) {
|
|
28
|
+
throwError("Missing required environment ID");
|
|
29
|
+
}
|
|
30
|
+
const apiKey = token ?? process.env.KONTENT_API_KEY;
|
|
31
|
+
if (!apiKey) {
|
|
32
|
+
throwError("Missing required API key");
|
|
30
33
|
}
|
|
31
34
|
const url = `https://manage.kontent.ai/v2/projects/${environmentId}/early-access/variants/filter`;
|
|
32
35
|
const headers = {
|
|
@@ -5,8 +5,8 @@ import { createMcpToolSuccessResponse } from "../utils/responseHelper.js";
|
|
|
5
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
|
-
}, async ({ assetId }) => {
|
|
9
|
-
const client = createMapiClient();
|
|
8
|
+
}, async ({ assetId }, { authInfo: { token, clientId } = {} }) => {
|
|
9
|
+
const client = createMapiClient(clientId, token);
|
|
10
10
|
try {
|
|
11
11
|
const response = await client
|
|
12
12
|
.viewAsset()
|
|
@@ -5,8 +5,8 @@ import { createMcpToolSuccessResponse } from "../utils/responseHelper.js";
|
|
|
5
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
|
-
}, async ({ id }) => {
|
|
9
|
-
const client = createMapiClient();
|
|
8
|
+
}, async ({ id }, { authInfo: { token, clientId } = {} }) => {
|
|
9
|
+
const client = createMapiClient(clientId, token);
|
|
10
10
|
try {
|
|
11
11
|
const response = await client
|
|
12
12
|
.viewContentItem()
|
|
@@ -5,8 +5,8 @@ import { createMcpToolSuccessResponse } from "../utils/responseHelper.js";
|
|
|
5
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
|
-
}, async ({ id }) => {
|
|
9
|
-
const client = createMapiClient();
|
|
8
|
+
}, async ({ id }, { authInfo: { token, clientId } = {} }) => {
|
|
9
|
+
const client = createMapiClient(clientId, token);
|
|
10
10
|
try {
|
|
11
11
|
const response = await client
|
|
12
12
|
.getTaxonomy()
|
|
@@ -5,8 +5,8 @@ import { createMcpToolSuccessResponse } from "../utils/responseHelper.js";
|
|
|
5
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
|
-
}, async ({ id }) => {
|
|
9
|
-
const client = createMapiClient();
|
|
8
|
+
}, async ({ id }, { authInfo: { token, clientId } = {} }) => {
|
|
9
|
+
const client = createMapiClient(clientId, token);
|
|
10
10
|
try {
|
|
11
11
|
const response = await client
|
|
12
12
|
.viewContentType()
|
|
@@ -5,8 +5,8 @@ import { createMcpToolSuccessResponse } from "../utils/responseHelper.js";
|
|
|
5
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
|
-
}, async ({ id }) => {
|
|
9
|
-
const client = createMapiClient();
|
|
8
|
+
}, async ({ id }, { authInfo: { token, clientId } = {} }) => {
|
|
9
|
+
const client = createMapiClient(clientId, token);
|
|
10
10
|
try {
|
|
11
11
|
const response = await client
|
|
12
12
|
.viewContentTypeSnippet()
|
|
@@ -8,8 +8,8 @@ export const registerTool = (server) => {
|
|
|
8
8
|
languageId: z
|
|
9
9
|
.string()
|
|
10
10
|
.describe("Internal ID of the language variant to get"),
|
|
11
|
-
}, async ({ itemId, languageId }) => {
|
|
12
|
-
const client = createMapiClient();
|
|
11
|
+
}, async ({ itemId, languageId }, { authInfo: { token, clientId } = {} }) => {
|
|
12
|
+
const client = createMapiClient(clientId, token);
|
|
13
13
|
try {
|
|
14
14
|
const response = await client
|
|
15
15
|
.viewLanguageVariant()
|
|
@@ -2,8 +2,8 @@ import { createMapiClient } from "../clients/kontentClients.js";
|
|
|
2
2
|
import { handleMcpToolError } from "../utils/errorHandler.js";
|
|
3
3
|
import { createMcpToolSuccessResponse } from "../utils/responseHelper.js";
|
|
4
4
|
export const registerTool = (server) => {
|
|
5
|
-
server.tool("list-assets-mapi", "Get all Kontent.ai assets from Management API", {}, async () => {
|
|
6
|
-
const client = createMapiClient();
|
|
5
|
+
server.tool("list-assets-mapi", "Get all Kontent.ai assets from Management API", {}, async (_, { authInfo: { token, clientId } = {} }) => {
|
|
6
|
+
const client = createMapiClient(clientId, token);
|
|
7
7
|
try {
|
|
8
8
|
const response = await client.listAssets().toAllPromise();
|
|
9
9
|
return createMcpToolSuccessResponse(response.data);
|
|
@@ -2,8 +2,8 @@ import { createMapiClient } from "../clients/kontentClients.js";
|
|
|
2
2
|
import { handleMcpToolError } from "../utils/errorHandler.js";
|
|
3
3
|
import { createMcpToolSuccessResponse } from "../utils/responseHelper.js";
|
|
4
4
|
export const registerTool = (server) => {
|
|
5
|
-
server.tool("list-content-type-snippets-mapi", "Get all Kontent.ai content type snippets from Management API", {}, async () => {
|
|
6
|
-
const client = createMapiClient();
|
|
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);
|
|
7
7
|
try {
|
|
8
8
|
const response = await client.listContentTypeSnippets().toAllPromise();
|
|
9
9
|
return createMcpToolSuccessResponse(response.data);
|
|
@@ -2,8 +2,8 @@ import { createMapiClient } from "../clients/kontentClients.js";
|
|
|
2
2
|
import { handleMcpToolError } from "../utils/errorHandler.js";
|
|
3
3
|
import { createMcpToolSuccessResponse } from "../utils/responseHelper.js";
|
|
4
4
|
export const registerTool = (server) => {
|
|
5
|
-
server.tool("list-content-types-mapi", "Get all Kontent.ai content types from Management API", {}, async () => {
|
|
6
|
-
const client = createMapiClient();
|
|
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);
|
|
7
7
|
try {
|
|
8
8
|
const response = await client.listContentTypes().toAllPromise();
|
|
9
9
|
return createMcpToolSuccessResponse(response.data);
|
|
@@ -2,8 +2,8 @@ import { createMapiClient } from "../clients/kontentClients.js";
|
|
|
2
2
|
import { handleMcpToolError } from "../utils/errorHandler.js";
|
|
3
3
|
import { createMcpToolSuccessResponse } from "../utils/responseHelper.js";
|
|
4
4
|
export const registerTool = (server) => {
|
|
5
|
-
server.tool("list-languages-mapi", "Get all Kontent.ai languages from Management API", {}, async () => {
|
|
6
|
-
const client = createMapiClient();
|
|
5
|
+
server.tool("list-languages-mapi", "Get all Kontent.ai languages from Management API", {}, async (_, { authInfo: { token, clientId } = {} }) => {
|
|
6
|
+
const client = createMapiClient(clientId, token);
|
|
7
7
|
try {
|
|
8
8
|
const response = await client.listLanguages().toAllPromise();
|
|
9
9
|
return createMcpToolSuccessResponse(response.data);
|
|
@@ -2,8 +2,8 @@ import { createMapiClient } from "../clients/kontentClients.js";
|
|
|
2
2
|
import { handleMcpToolError } from "../utils/errorHandler.js";
|
|
3
3
|
import { createMcpToolSuccessResponse } from "../utils/responseHelper.js";
|
|
4
4
|
export const registerTool = (server) => {
|
|
5
|
-
server.tool("list-taxonomy-groups-mapi", "Get all Kontent.ai taxonomy groups from Management API", {}, async () => {
|
|
6
|
-
const client = createMapiClient();
|
|
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);
|
|
7
7
|
try {
|
|
8
8
|
const response = await client.listTaxonomies().toAllPromise();
|
|
9
9
|
return createMcpToolSuccessResponse(response.data);
|
|
@@ -2,8 +2,8 @@ import { createMapiClient } from "../clients/kontentClients.js";
|
|
|
2
2
|
import { handleMcpToolError } from "../utils/errorHandler.js";
|
|
3
3
|
import { createMcpToolSuccessResponse } from "../utils/responseHelper.js";
|
|
4
4
|
export const registerTool = (server) => {
|
|
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 () => {
|
|
6
|
-
const client = createMapiClient();
|
|
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);
|
|
7
7
|
try {
|
|
8
8
|
const response = await client.listWorkflows().toPromise();
|
|
9
9
|
return createMcpToolSuccessResponse(response.data);
|
|
@@ -54,8 +54,8 @@ export const registerTool = (server) => {
|
|
|
54
54
|
- Consider element ordering when using move operations
|
|
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
|
-
}, async ({ codename, operations }) => {
|
|
58
|
-
const client = createMapiClient();
|
|
57
|
+
}, async ({ codename, operations }, { authInfo: { token, clientId } = {} }) => {
|
|
58
|
+
const client = createMapiClient(clientId, token);
|
|
59
59
|
try {
|
|
60
60
|
// Apply patch operations using the modifyContentType method
|
|
61
61
|
const response = await client
|
|
@@ -21,8 +21,8 @@ export const registerTool = (server) => {
|
|
|
21
21
|
.string()
|
|
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
|
-
}, async ({ itemId, languageId, scheduledTo, displayTimezone }) => {
|
|
25
|
-
const client = createMapiClient();
|
|
24
|
+
}, async ({ itemId, languageId, scheduledTo, displayTimezone }, { authInfo: { token, clientId } = {} }) => {
|
|
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) {
|
|
@@ -21,8 +21,8 @@ export const registerTool = (server) => {
|
|
|
21
21
|
.string()
|
|
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
|
-
}, async ({ itemId, languageId, scheduledTo, displayTimezone }) => {
|
|
25
|
-
const client = createMapiClient();
|
|
24
|
+
}, async ({ itemId, languageId, scheduledTo, displayTimezone }, { authInfo: { token, clientId } = {} }) => {
|
|
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) {
|
|
@@ -19,8 +19,8 @@ export const registerTool = (server) => {
|
|
|
19
19
|
})
|
|
20
20
|
.optional()
|
|
21
21
|
.describe("Reference to a collection by id, codename, or external_id (optional)"),
|
|
22
|
-
}, async ({ id, name, collection }) => {
|
|
23
|
-
const client = createMapiClient();
|
|
22
|
+
}, async ({ id, name, collection }, { authInfo: { token, clientId } = {} }) => {
|
|
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();
|
|
@@ -4,18 +4,20 @@ 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 language variant of a content item via Management API. This adds actual content to the content item elements.
|
|
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
|
|
10
10
|
.string()
|
|
11
11
|
.describe("Internal ID of the language variant (e.g., '00000000-0000-0000-0000-000000000000' for default language)"),
|
|
12
|
-
elements: z
|
|
12
|
+
elements: z
|
|
13
|
+
.array(languageVariantElementSchema)
|
|
14
|
+
.describe("Array of content elements, each with 'element' (reference object with id/codename/external_id) and 'value' properties. Additional properties may be required depending on element type (e.g., 'mode' for URL slugs)."),
|
|
13
15
|
workflow_step_id: z
|
|
14
16
|
.string()
|
|
15
17
|
.optional()
|
|
16
18
|
.describe("Internal ID of the workflow step (optional)"),
|
|
17
|
-
}, async ({ itemId, languageId, elements, workflow_step_id }) => {
|
|
18
|
-
const client = createMapiClient();
|
|
19
|
+
}, async ({ itemId, languageId, elements, workflow_step_id }, { authInfo: { token, clientId } = {} }) => {
|
|
20
|
+
const client = createMapiClient(clientId, token);
|
|
19
21
|
const data = {
|
|
20
22
|
elements,
|
|
21
23
|
};
|
|
@@ -50,7 +50,7 @@ export const handleMcpToolError = (error, context) => {
|
|
|
50
50
|
content: [
|
|
51
51
|
{
|
|
52
52
|
type: "text",
|
|
53
|
-
text: `${contextPrefix}HTTP Error ${error.response.status}: ${error.response.statusText || "Unknown HTTP error"}\n\nResponse: ${JSON.stringify(error.response.data
|
|
53
|
+
text: `${contextPrefix}HTTP Error ${error.response.status}: ${error.response.statusText || "Unknown HTTP error"}\n\nResponse: ${JSON.stringify(error.response.data)}`,
|
|
54
54
|
},
|
|
55
55
|
],
|
|
56
56
|
isError: true,
|
|
@@ -61,7 +61,7 @@ export const handleMcpToolError = (error, context) => {
|
|
|
61
61
|
content: [
|
|
62
62
|
{
|
|
63
63
|
type: "text",
|
|
64
|
-
text: `${contextPrefix}Unexpected error: ${error instanceof Error ? error.message : "Unknown error occurred"}\n\nFull error: ${JSON.stringify(error
|
|
64
|
+
text: `${contextPrefix}Unexpected error: ${error instanceof Error ? error.message : "Unknown error occurred"}\n\nFull error: ${JSON.stringify(error)}`,
|
|
65
65
|
},
|
|
66
66
|
],
|
|
67
67
|
isError: true,
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Extracts Bearer token from request authorization header
|
|
3
|
+
* @param req Express request object
|
|
4
|
+
* @returns Bearer token string or null if not found
|
|
5
|
+
*/
|
|
6
|
+
export const extractBearerToken = (req) => {
|
|
7
|
+
const authHeader = req.headers.authorization;
|
|
8
|
+
if (authHeader?.startsWith("Bearer ")) {
|
|
9
|
+
return authHeader.substring(7); // Remove 'Bearer ' prefix
|
|
10
|
+
}
|
|
11
|
+
return null;
|
|
12
|
+
};
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Validates if a string is a valid GUID format
|
|
3
|
+
* @param guid String to validate
|
|
4
|
+
* @returns true if valid GUID format, false otherwise
|
|
5
|
+
*/
|
|
6
|
+
export const isValidGuid = (guid) => {
|
|
7
|
+
const guidRegex = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
|
|
8
|
+
return guidRegex.test(guid);
|
|
9
|
+
};
|
package/package.json
CHANGED
|
@@ -1,14 +1,12 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@kontent-ai/mcp-server",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.17.0",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"scripts": {
|
|
6
6
|
"build": "rimraf build && tsc",
|
|
7
7
|
"start:stdio": "node build/bin.js stdio",
|
|
8
|
-
"start:sse": "node build/bin.js sse",
|
|
9
8
|
"start:shttp": "node build/bin.js shttp",
|
|
10
9
|
"dev:stdio": "tsx watch src/bin.ts stdio",
|
|
11
|
-
"dev:sse": "tsx watch src/bin.ts sse",
|
|
12
10
|
"dev:shttp": "tsx watch src/bin.ts shttp",
|
|
13
11
|
"format": "cross-env node node_modules/@biomejs/biome/bin/biome ci ./ --config-path=./biome.json",
|
|
14
12
|
"format:fix": "cross-env node node_modules/@biomejs/biome/bin/biome check ./ --fix --unsafe --config-path=./biome.json"
|