@kontent-ai/mcp-server 0.4.2 → 0.6.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 (31) hide show
  1. package/README.md +127 -32
  2. package/build/bin.js +11 -7
  3. package/build/clients/kontentClients.js +12 -8
  4. package/build/schemas/contentItemSchemas.js +205 -0
  5. package/build/schemas/contentTypeSchemas.js +250 -127
  6. package/build/schemas/taxonomySchemas.js +11 -3
  7. package/build/server.js +22 -11
  8. package/build/tools/add-content-item-mapi.js +54 -0
  9. package/build/tools/add-content-type-mapi.js +35 -28
  10. package/build/tools/add-content-type-snippet-mapi.js +31 -29
  11. package/build/tools/add-taxonomy-group-mapi.js +14 -14
  12. package/build/tools/delete-content-item-mapi.js +24 -0
  13. package/build/tools/delete-language-variant-mapi.js +28 -0
  14. package/build/tools/get-asset-mapi.js +14 -14
  15. package/build/tools/get-item-dapi.js +13 -13
  16. package/build/tools/get-item-mapi.js +14 -14
  17. package/build/tools/get-taxonomy-group-mapi.js +14 -14
  18. package/build/tools/get-type-mapi.js +14 -14
  19. package/build/tools/get-type-snippet-mapi.js +16 -14
  20. package/build/tools/get-variant-mapi.js +17 -15
  21. package/build/tools/list-assets-mapi.js +10 -12
  22. package/build/tools/list-content-type-snippets-mapi.js +10 -12
  23. package/build/tools/list-content-types-mapi.js +10 -12
  24. package/build/tools/list-languages-mapi.js +10 -12
  25. package/build/tools/list-taxonomy-groups-mapi.js +11 -13
  26. package/build/tools/update-content-item-mapi.js +74 -0
  27. package/build/tools/upsert-language-variant-mapi.js +46 -0
  28. package/build/utils/errorHandler.js +87 -0
  29. package/build/utils/responseHelper.js +18 -0
  30. package/build/utils/throwError.js +3 -0
  31. package/package.json +13 -5
package/README.md CHANGED
@@ -8,34 +8,113 @@
8
8
  [![MIT License][license-shield]][license-url]
9
9
  [![Discord][discord-shield]][discord-url]
10
10
 
11
- This server provides a Model Context Protocol (MCP) interface for interacting with Kontent.ai's Management and Delivery APIs. It enables AI assistants to access and manipulate Kontent.ai content using standardized tools.
11
+ > Transform your content operations with AI-powered tools for Kontent.ai. Create, manage, and explore your structured content through natural language conversations in your favorite AI-enabled editor.
12
12
 
13
- ## Features
13
+ Kontent.ai MCP Server implements the Model Context Protocol to connect your Kontent.ai projects with AI tools like Claude, Cursor, and VS Code. It enables AI models to understand your content structure and perform operations through natural language instructions.
14
14
 
15
- - Retrieve content items, variants, and assets
16
- - List available languages and assets
17
- - Get, List, and Create content types and snippets
18
- - Get, List, and Create taxonomies
15
+ ## Key Features
19
16
 
20
- ## Getting Started
17
+ * 🚀 **Rapid prototyping**: Transform your diagrams into live content models in seconds
18
+ * 📈 **Data Visualisation**: Visualise your content model in any format you want
21
19
 
22
- ### Prerequisites
20
+ ## Table of Contents
23
21
 
24
- - Node.js (version specified in `.nvmrc`)
25
- - Kontent.ai account with API keys
22
+ - [✨ Key Features](#-key-features)
23
+ - [🔌 Quickstart](#-quickstart)
24
+ - [🛠️ Available Tools](#️-available-tools)
25
+ - [⚙️ Configuration](#️-configuration)
26
+ - [🚀 Transport Options](#-transport-options)
27
+ - [💻 Development](#-development)
28
+ - [License](#license)
26
29
 
27
- ### Running
30
+ ## 🔌 Quickstart
28
31
 
29
- You can run this server with either stdio or sse transport.
32
+ ### 🔑 Prerequisites
30
33
 
31
- ### Stdio transport
34
+ Before you can use the MCP server, you need:
35
+
36
+ 1. **A Kontent.ai account** - [Sign up](https://kontent.ai/signup) if you don't have an account.
37
+ 1. **A project** - [Create a project](https://kontent.ai/learn/docs/projects#a-create-projects) to work with.
38
+ 1. **Management API key** - [Create a Management API key](https://kontent.ai/learn/docs/apis/api-keys#a-create-management-api-keys) with appropriate permissions.
39
+ 1. **Environment ID** - [Get your environment ID](https://kontent.ai/learn/docs/environments#a-get-your-environment-id).
40
+
41
+ ### 🛠 Setup Options
42
+
43
+ You can run the Kontent.ai MCP Server with npx:
44
+
45
+ #### STDIO Transport
46
+
47
+ ```bash
48
+ npx @kontent-ai/mcp-server@latest stdio
49
+ ```
50
+
51
+ #### SSE Transport
52
+
53
+ ```bash
54
+ npx @kontent-ai/mcp-server@latest sse
55
+ ```
56
+
57
+ ## 🛠️ Available Tools
58
+
59
+ ### Content Type Management
60
+
61
+ * **get-type-mapi** – Get a specific content type by codename
62
+ * **list-content-types-mapi** – List all content types in the environment
63
+ * **add-content-type-mapi** – Create a new content type with elements
64
+
65
+ ### Content Type Snippet Management
66
+
67
+ * **get-type-snippet-mapi** – Get a specific content type snippet by codename
68
+ * **list-content-type-snippets-mapi** – List all content type snippets
69
+ * **add-content-type-snippet-mapi** – Create a new content type snippet
70
+
71
+ ### Taxonomy Management
72
+
73
+ * **get-taxonomy-group-mapi** – Get a specific taxonomy group by codename
74
+ * **list-taxonomy-groups-mapi** – List all taxonomy groups
75
+ * **add-taxonomy-group-mapi** – Create a new taxonomy group with terms
76
+
77
+ ### Content Item Management
78
+
79
+ * **get-item-mapi** – Get a specific content item by codename
80
+ * **get-item-dapi** – Get a content item by codename from Delivery API
81
+ * **get-variant-mapi** – Get a language variant of a content item
82
+ * **add-content-item-mapi** – Create a new content item (structure only)
83
+ * **update-content-item-mapi** – Update an existing content item by codename (name, collection)
84
+ * **delete-content-item-mapi** – Delete a content item by codename
85
+ * **upsert-language-variant-mapi** – Create or update a language variant with content
86
+ * **delete-language-variant-mapi** – Delete a language variant of a content item
87
+
88
+ ### Asset Management
89
+
90
+ * **get-asset-mapi** – Get a specific asset by codename
91
+ * **list-assets-mapi** – List all assets in the environment
92
+
93
+ ### Language Management
94
+
95
+ * **list-languages-mapi** – List all languages configured in the environment
96
+
97
+ ## ⚙️ Configuration
98
+
99
+ The server requires the following environment variables:
100
+
101
+ | Variable | Description | Required |
102
+ |----------|-------------|----------|
103
+ | KONTENT_API_KEY | Your Kontent.ai Management API key | ✅ |
104
+ | KONTENT_ENVIRONMENT_ID | Your environment ID | ✅ |
105
+ | PORT | Port for SSE transport (defaults to 3001) | ❌ |
106
+
107
+ ## 🚀 Transport Options
108
+
109
+ ### 📟 STDIO Transport
110
+
111
+ To run the server with STDIO transport, configure your MCP client with:
32
112
 
33
- To run the server with stdio transport configure your MCP client with the command to run and the necessary environment variables.
34
- Example:
35
113
  ```json
36
114
  {
37
115
  "kontent-ai-stdio": {
38
- "command": "npx @kontent-ai/mcp-server@latest stdio",
116
+ "command": "npx",
117
+ "args": ["@kontent-ai/mcp-server@latest", "stdio"],
39
118
  "env": {
40
119
  "KONTENT_API_KEY": "<management-api-key>",
41
120
  "KONTENT_ENVIRONMENT_ID": "<environment-id>"
@@ -44,22 +123,22 @@ Example:
44
123
  }
45
124
  ```
46
125
 
47
- ### SSE transport
126
+ ### 🌐 SSE Transport
48
127
 
49
- You can also run your server manually with the SSE transport and configure your MCP client to connect to the port the server is running on.
50
- Run the following command to start the server and ensure the environment variables are defined for it either by providing `.env` file in the `cwd` or providing the variables to the process any other way.
128
+ For SSE transport, first start the server:
51
129
 
52
130
  ```bash
53
131
  npx @kontent-ai/mcp-server@latest sse
54
132
  ```
133
+
134
+ With environment variables in a `.env` file, or otherwise accessible to the process:
55
135
  ```env
56
136
  KONTENT_API_KEY=<management-api-key>
57
137
  KONTENT_ENVIRONMENT_ID=<environment-id>
58
- PORT=<port-number> # optionally specify port, defaults to 3001
138
+ PORT=3001 # optional, defaults to 3001
59
139
  ```
60
140
 
61
- Then configure your MCP client to connect to the running server.
62
- Example:
141
+ Then configure your MCP client:
63
142
  ```json
64
143
  {
65
144
  "kontent-ai-sse": {
@@ -68,10 +147,9 @@ Example:
68
147
  }
69
148
  ```
70
149
 
150
+ ## 💻 Development
71
151
 
72
- ## Development
73
-
74
- ### Local Installation
152
+ ### 🛠 Local Installation
75
153
 
76
154
  ```bash
77
155
  # Clone the repository
@@ -87,23 +165,40 @@ npm run build
87
165
  # Start the server
88
166
  npm run start:sse # For SSE transport
89
167
  npm run start:stdio # For STDIO transport
90
- ```
91
-
92
- ### Available Scripts
93
168
 
94
- - `npm run build` - Compile TypeScript to JavaScript
95
- - `npm run start:sse` - Start the server with Server-Sent Events transport
96
- - `npm run start:stdio` - Start the server with Standard IO transport
169
+ # Start the server with automatic reloading (no need to build first)
170
+ npm run dev:sse # For SSE transport
171
+ npm run dev:stdio # For STDIO transport
172
+ ```
97
173
 
98
- ### Project Structure
174
+ ### 📂 Project Structure
99
175
 
100
176
  - `src/` - Source code
101
177
  - `tools/` - MCP tool implementations
102
178
  - `clients/` - Kontent.ai API client setup
103
179
  - `schemas/` - Data validation schemas
180
+ - `utils/` - Utility functions
181
+ - `errorHandler.ts` - Standardized error handling for MCP tools
182
+ - `throwError.ts` - Generic error throwing utility
104
183
  - `server.ts` - Main server setup and tool registration
105
184
  - `bin.ts` - Single entry point that handles both transport types
106
185
 
186
+ ### 🔍 Debugging
187
+
188
+ For debugging, you can use the MCP inspector:
189
+
190
+ ```bash
191
+ npx @modelcontextprotocol/inspector -e KONTENT_API_KEY=<key> -e KONTENT_ENVIRONMENT_ID=<env-id> node path/to/build/bin.js
192
+ ```
193
+
194
+ Or use the MCP inspector on a running sse server:
195
+
196
+ ```bash
197
+ npx @modelcontextprotocol/inspector
198
+ ```
199
+
200
+ This provides a web interface for inspecting and testing the available tools.
201
+
107
202
  ## License
108
203
 
109
204
  MIT
package/build/bin.js CHANGED
@@ -1,14 +1,16 @@
1
1
  #!/usr/bin/env node
2
- import 'dotenv/config';
2
+ import "dotenv/config";
3
3
  import { SSEServerTransport } from "@modelcontextprotocol/sdk/server/sse.js";
4
4
  import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
5
5
  import express from "express";
6
+ import packageJson from "../package.json" with { type: "json" };
6
7
  import { createServer } from "./server.js";
8
+ const version = packageJson.version;
7
9
  async function startSSE() {
8
10
  const app = express();
9
11
  const { server } = createServer();
10
12
  let transport;
11
- app.get("/sse", async (req, res) => {
13
+ app.get("/sse", async (_req, res) => {
12
14
  transport = new SSEServerTransport("/message", res);
13
15
  await server.connect(transport);
14
16
  });
@@ -17,25 +19,27 @@ async function startSSE() {
17
19
  });
18
20
  const PORT = process.env.PORT || 3001;
19
21
  app.listen(PORT, () => {
20
- console.log(`Kontent.ai MCP Server (SSE) running on port ${PORT}`);
22
+ console.log(`Kontent.ai MCP Server v${version} (SSE) running on port ${PORT}`);
21
23
  });
22
24
  }
23
25
  async function startStdio() {
24
26
  const { server } = createServer();
25
27
  const transport = new StdioServerTransport();
28
+ console.log(`Kontent.ai MCP Server v${version} (stdio) starting`);
26
29
  await server.connect(transport);
27
30
  }
28
31
  async function main() {
29
32
  const args = process.argv.slice(2);
30
33
  const transportType = args[0]?.toLowerCase();
31
- if (!transportType || (transportType !== 'stdio' && transportType !== 'sse')) {
32
- console.error('Please specify a valid transport type: stdio or sse');
34
+ if (!transportType ||
35
+ (transportType !== "stdio" && transportType !== "sse")) {
36
+ console.error("Please specify a valid transport type: stdio or sse");
33
37
  process.exit(1);
34
38
  }
35
- if (transportType === 'stdio') {
39
+ if (transportType === "stdio") {
36
40
  await startStdio();
37
41
  }
38
- else if (transportType === 'sse') {
42
+ else if (transportType === "sse") {
39
43
  await startSSE();
40
44
  }
41
45
  }
@@ -1,5 +1,6 @@
1
- import { createManagementClient } from '@kontent-ai/management-sdk';
1
+ import { createManagementClient } from "@kontent-ai/management-sdk";
2
2
  import packageJson from "../../package.json" with { type: "json" };
3
+ import { throwError } from "../utils/throwError.js";
3
4
  const sourceTrackingHeaderName = "X-KC-SOURCE";
4
5
  /**
5
6
  * Creates a Kontent.ai Management API client
@@ -9,14 +10,17 @@ const sourceTrackingHeaderName = "X-KC-SOURCE";
9
10
  */
10
11
  export const createMapiClient = (environmentId, apiKey) => {
11
12
  return createManagementClient({
12
- apiKey: apiKey ?? process.env.KONTENT_API_KEY ?? throwError("KONTENT_API_KEY is not set"),
13
- environmentId: environmentId ?? process.env.KONTENT_ENVIRONMENT_ID ?? throwError("KONTENT_ENVIRONMENT_ID is not set"),
14
- headers: [{
13
+ apiKey: apiKey ??
14
+ process.env.KONTENT_API_KEY ??
15
+ throwError("KONTENT_API_KEY is not set"),
16
+ environmentId: environmentId ??
17
+ process.env.KONTENT_ENVIRONMENT_ID ??
18
+ throwError("KONTENT_ENVIRONMENT_ID is not set"),
19
+ headers: [
20
+ {
15
21
  header: sourceTrackingHeaderName,
16
22
  value: `${packageJson.name};${packageJson.version}`,
17
- }],
23
+ },
24
+ ],
18
25
  });
19
26
  };
20
- const throwError = (message) => {
21
- throw new Error(message);
22
- };
@@ -0,0 +1,205 @@
1
+ import { z } from "zod";
2
+ // Define a reusable reference object schema
3
+ const referenceObjectSchema = z
4
+ .object({
5
+ id: z.string().optional(),
6
+ codename: z.string().optional(),
7
+ external_id: z.string().optional(),
8
+ })
9
+ .describe("An object with an id, codename, or external_id property referencing another item. Using codename is preferred for better readability.");
10
+ // Language variant element value schemas
11
+ const textElementValueSchema = z.object({
12
+ value: z.string().describe("The text content of the element"),
13
+ });
14
+ const numberElementValueSchema = z.object({
15
+ value: z.number().describe("The numeric value of the element"),
16
+ });
17
+ const dateTimeElementValueSchema = z.object({
18
+ value: z
19
+ .string()
20
+ .nullable()
21
+ .describe("The ISO-8601 formatted date-time string, or null for empty"),
22
+ });
23
+ const multipleChoiceElementValueSchema = z.object({
24
+ value: z
25
+ .array(referenceObjectSchema)
26
+ .describe("Array of references to the selected options by their codename or id"),
27
+ });
28
+ const assetElementValueSchema = z.object({
29
+ value: z
30
+ .array(referenceObjectSchema)
31
+ .describe("Array of references to assets by their codename or id"),
32
+ });
33
+ const modularContentElementValueSchema = z.object({
34
+ value: z
35
+ .array(referenceObjectSchema)
36
+ .describe("Array of references to linked content items by their codename or id"),
37
+ });
38
+ const taxonomyElementValueSchema = z.object({
39
+ value: z
40
+ .array(referenceObjectSchema)
41
+ .describe("Array of references to taxonomy terms by their codename or id"),
42
+ });
43
+ const richTextElementValueSchema = z.object({
44
+ value: z.string().describe("The rich text content as HTML string"),
45
+ });
46
+ const urlSlugElementValueSchema = z.object({
47
+ value: z.string().describe("The URL slug value"),
48
+ });
49
+ const customElementValueSchema = z.object({
50
+ value: z.string().describe("The JSON string value for custom element"),
51
+ });
52
+ // Union type for all possible element values
53
+ const _elementValueSchema = z.discriminatedUnion("value", [
54
+ textElementValueSchema,
55
+ numberElementValueSchema,
56
+ dateTimeElementValueSchema,
57
+ multipleChoiceElementValueSchema,
58
+ assetElementValueSchema,
59
+ modularContentElementValueSchema,
60
+ taxonomyElementValueSchema,
61
+ richTextElementValueSchema,
62
+ urlSlugElementValueSchema,
63
+ customElementValueSchema,
64
+ ]);
65
+ // Language variant element schema
66
+ const languageVariantElementSchema = z
67
+ .record(z.string().describe("Element codename as key"), z
68
+ .union([
69
+ textElementValueSchema,
70
+ numberElementValueSchema,
71
+ dateTimeElementValueSchema,
72
+ multipleChoiceElementValueSchema,
73
+ assetElementValueSchema,
74
+ modularContentElementValueSchema,
75
+ taxonomyElementValueSchema,
76
+ richTextElementValueSchema,
77
+ urlSlugElementValueSchema,
78
+ customElementValueSchema,
79
+ ])
80
+ .describe("Element value object with 'value' property containing the element's content"))
81
+ .describe("Object where keys are element codenames and values are element value objects");
82
+ // Collection reference schema
83
+ const collectionReferenceSchema = z
84
+ .object({
85
+ reference: referenceObjectSchema
86
+ .nullable()
87
+ .describe("Reference to a collection by id, codename, or external_id. Use null to remove from collection."),
88
+ })
89
+ .describe("Collection assignment for the content item");
90
+ // Content item creation schema
91
+ export const contentItemCreateSchema = z
92
+ .object({
93
+ name: z
94
+ .string()
95
+ .min(1)
96
+ .max(200)
97
+ .describe("Display name of the content item (1-200 characters)"),
98
+ codename: z
99
+ .string()
100
+ .optional()
101
+ .describe("Codename of the content item (optional, will be generated from name if not provided)"),
102
+ type: referenceObjectSchema.describe("Reference to the content type by id, codename, or external_id"),
103
+ external_id: z
104
+ .string()
105
+ .optional()
106
+ .describe("External ID for the content item (optional, useful for external system integration)"),
107
+ collection: collectionReferenceSchema
108
+ .optional()
109
+ .describe("Collection assignment for the content item (optional)"),
110
+ })
111
+ .describe("Schema for creating a new content item");
112
+ // Language variant creation/update schema
113
+ export const languageVariantUpsertSchema = z
114
+ .object({
115
+ elements: languageVariantElementSchema.describe("Object containing element values for the language variant. Keys are element codenames, values are element value objects."),
116
+ workflow_step: referenceObjectSchema
117
+ .optional()
118
+ .describe("Reference to workflow step by id or codename (optional)"),
119
+ })
120
+ .describe("Schema for creating or updating a language variant of a content item");
121
+ // Complete content item with language variant schema for convenience
122
+ export const contentItemWithVariantSchema = z
123
+ .object({
124
+ item: contentItemCreateSchema.describe("Content item data"),
125
+ language_codename: z
126
+ .string()
127
+ .default("default")
128
+ .describe("Codename of the language for the variant (defaults to 'default')"),
129
+ variant: languageVariantUpsertSchema.describe("Language variant data with element values"),
130
+ })
131
+ .describe("Schema for creating a content item along with its language variant in one operation");
132
+ // Element value helper schemas for better LLM understanding
133
+ export const elementValueHelpers = {
134
+ text: z
135
+ .object({
136
+ value: z.string().describe("Text content"),
137
+ })
138
+ .describe("Text element value - use for simple text fields like titles, descriptions, etc."),
139
+ richText: z
140
+ .object({
141
+ value: z.string().describe("HTML content as string"),
142
+ })
143
+ .describe("Rich text element value - use for formatted content with HTML tags"),
144
+ number: z
145
+ .object({
146
+ value: z.number().describe("Numeric value"),
147
+ })
148
+ .describe("Number element value - use for numeric fields like prices, quantities, etc."),
149
+ dateTime: z
150
+ .object({
151
+ value: z
152
+ .string()
153
+ .nullable()
154
+ .describe("ISO-8601 date-time string or null"),
155
+ })
156
+ .describe("Date-time element value - use ISO format like '2023-12-25T10:30:00.000Z' or null for empty"),
157
+ multipleChoice: z
158
+ .object({
159
+ value: z
160
+ .array(z.object({
161
+ codename: z.string().describe("Codename of the selected option"),
162
+ }))
163
+ .describe("Array of selected option references"),
164
+ })
165
+ .describe("Multiple choice element value - reference options by their codename"),
166
+ asset: z
167
+ .object({
168
+ value: z
169
+ .array(z.object({
170
+ codename: z.string().describe("Codename of the asset"),
171
+ }))
172
+ .describe("Array of asset references"),
173
+ })
174
+ .describe("Asset element value - reference assets by their codename"),
175
+ modularContent: z
176
+ .object({
177
+ value: z
178
+ .array(z.object({
179
+ codename: z
180
+ .string()
181
+ .describe("Codename of the linked content item"),
182
+ }))
183
+ .describe("Array of content item references"),
184
+ })
185
+ .describe("Modular content element value - reference other content items by their codename"),
186
+ taxonomy: z
187
+ .object({
188
+ value: z
189
+ .array(z.object({
190
+ codename: z.string().describe("Codename of the taxonomy term"),
191
+ }))
192
+ .describe("Array of taxonomy term references"),
193
+ })
194
+ .describe("Taxonomy element value - reference taxonomy terms by their codename"),
195
+ urlSlug: z
196
+ .object({
197
+ value: z.string().describe("URL slug value"),
198
+ })
199
+ .describe("URL slug element value - use for SEO-friendly URLs"),
200
+ custom: z
201
+ .object({
202
+ value: z.string().describe("JSON string value"),
203
+ })
204
+ .describe("Custom element value - depends on the custom element implementation"),
205
+ };