# Directory Structure ``` ├── .env.example ├── .gitignore ├── LICENSE ├── package-lock.json ├── package.json ├── README.md ├── src │ └── index.ts └── tsconfig.json ``` # Files -------------------------------------------------------------------------------- /.env.example: -------------------------------------------------------------------------------- ``` 1 | CONTENTFUL_SPACE_ID=YOURSPCE_ID 2 | CONTENTFUL_ACCESS_TOKEN=YOUR_CONTENTFUL_TOKEN 3 | CONTENTFUL_ENVIRONMENT=YOUR_ENVIRONMENT 4 | CONTENTFUL_PREVIEW_ACCESS_TOKEN=YOURACCESS_TOKEN ``` -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- ``` 1 | # Dependency directories 2 | node_modules/ 3 | dist/ 4 | 5 | # Environment variables 6 | .env 7 | .env.local 8 | .env.*.local 9 | 10 | # Build files 11 | build/ 12 | 13 | # Logs 14 | logs 15 | *.log 16 | npm-debug.log* 17 | 18 | # Editor directories and files 19 | .idea/ 20 | .vscode/ 21 | *.swp 22 | *.swo 23 | 24 | # Operating System Files 25 | .DS_Store 26 | Thumbs.db ``` -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- ```markdown 1 | [](https://mseep.ai/app/tejedamiguel6-mcp-server-contenful) 2 | 3 | # Contentful MCP Server 4 | 5 | A Model Context Protocol (MCP) server that allows Claude to interact with Contentful CMS data directly. This integration enables Claude to fetch content types and entries from your Contentful space. 6 | [](https://mseep.ai/app/5fafc920-b065-43ab-946a-bbc57aadebe0) 7 | 8 | ## Features 9 | 10 | - Fetch all content types from your Contentful space 11 | - Retrieve entries for specific content types 12 | - Structured responses for easy consumption by AI assistants 13 | 14 | ## Prerequisites 15 | 16 | - Node.js (v16 or higher) 17 | - A Contentful account with API keys 18 | - Claude Desktop (to use the MCP server with Claude) 19 | 20 | ## Installation 21 | 22 | 1. Clone this repository: 23 | ```bash 24 | git clone https://github.com/yourusername/contentful-mcp-server.git 25 | cd contentful-mcp-server 26 | 27 | 2.Install dependencies: 28 | npm install 29 | 30 | Create a .env file in the root directory with your Contentful credentials: 31 | 4. CONTENTFUL_SPACE_ID=your_space_id 32 | CONTENTFUL_ACCESS_TOKEN=your_access_token 33 | CONTENTFUL_ENVIRONMENT=develop 34 | CONTENTFUL_PREVIEW_ACCESS_TOKEN=your_preview_token 35 | 36 | 37 | npm run build 38 | 39 | 40 | Or configure a build script in your package.json: 41 | "scripts": { 42 | "build": "tsc", 43 | "start": "node dist/index.js" 44 | } 45 | 46 | ##Configuration for Claude Desktop 47 | ``` 48 | { 49 | "mcpServers": { 50 | "contentful": { 51 | "command": "node", 52 | "args": [ 53 | "/absolute/path/to/contentful-mcp-server/dist/index.js" 54 | ] 55 | } 56 | } 57 | } 58 | ``` 59 | ``` -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- ```json 1 | { 2 | "compilerOptions": { 3 | "target": "ES2022", 4 | "module": "Node16", 5 | "moduleResolution": "Node16", 6 | "outDir": "./build", 7 | "rootDir": "./src", 8 | "strict": true, 9 | "esModuleInterop": true, 10 | "skipLibCheck": true, 11 | "forceConsistentCasingInFileNames": true 12 | }, 13 | "include": ["src/**/*"], 14 | "exclude": ["node_modules"] 15 | } ``` -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- ```json 1 | { 2 | "name": "contentful-mcp-server", 3 | "version": "1.0.0", 4 | "main": "index.js", 5 | "type": "module", 6 | "scripts": { 7 | "start": "ts-node --esm src/index.ts", 8 | "dev": "ts-node-esm src/index.ts", 9 | "build": "tsc", 10 | "test": "echo \"Error: no test specified\" && exit 1" 11 | }, 12 | "keywords": [], 13 | "author": "", 14 | "license": "ISC", 15 | "description": "", 16 | "dependencies": { 17 | "@modelcontextprotocol/sdk": "^1.7.0", 18 | "dotenv": "^16.4.7", 19 | "zod": "^3.24.2" 20 | }, 21 | "devDependencies": { 22 | "@types/node": "^22.13.10", 23 | "ts-node": "^10.9.2", 24 | "typescript": "^5.8.2" 25 | } 26 | } 27 | ``` -------------------------------------------------------------------------------- /src/index.ts: -------------------------------------------------------------------------------- ```typescript 1 | import * as dotenv from "dotenv"; 2 | import path from "path"; 3 | import { fileURLToPath } from "url"; 4 | 5 | const __filename = fileURLToPath(import.meta.url); 6 | const __dirname = path.dirname(__filename); 7 | 8 | // Load .env file from project root 9 | dotenv.config({ path: path.resolve(__dirname, "../.env") }); 10 | 11 | import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js"; 12 | import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js"; 13 | import { z } from "zod"; 14 | 15 | const CONTENTFUL_SPACE_ID = process.env.CONTENTFUL_SPACE_ID; 16 | const CONTENTFUL_ACCESS_TOKEN = process.env.CONTENTFUL_ACCESS_TOKEN; 17 | const CONTENTFUL_ENVIRONMENT = process.env.CONTENTFUL_ENVIRONMENT; 18 | 19 | // Validate required environment variables 20 | if ( 21 | !CONTENTFUL_SPACE_ID || 22 | !CONTENTFUL_ACCESS_TOKEN || 23 | !CONTENTFUL_ENVIRONMENT 24 | ) { 25 | console.error( 26 | "Missing required environment variables. Please check your .env file." 27 | ); 28 | process.exit(1); 29 | } 30 | 31 | const server = new McpServer({ 32 | name: "Contentful Tools", 33 | version: "1.0.0", 34 | }); 35 | 36 | server.tool("get-content-types", "get content types", {}, async () => { 37 | const restEndpoint = `https://cdn.contentful.com/spaces/${CONTENTFUL_SPACE_ID}/environments/${CONTENTFUL_ENVIRONMENT}/content_types`; 38 | try { 39 | const response = await fetch(restEndpoint, { 40 | headers: { 41 | Authorization: `Bearer ${CONTENTFUL_ACCESS_TOKEN}`, 42 | }, 43 | }); 44 | 45 | if (!response.ok) { 46 | throw new Error(`http error! status ${response.status}`); 47 | } 48 | const data = await response.json(); 49 | 50 | return { 51 | content: [ 52 | { 53 | type: "text", 54 | text: JSON.stringify( 55 | data.items.map((item: any) => ({ 56 | id: item.sys.id, 57 | name: item.name, 58 | })), 59 | null, 60 | 2 61 | ), 62 | }, 63 | ], 64 | }; 65 | } catch (error: any) { 66 | console.error("Error fetching content types:", error); 67 | return { 68 | content: [ 69 | { 70 | type: "text", 71 | text: `Error: ${error.message}`, 72 | }, 73 | ], 74 | }; 75 | } 76 | }); 77 | 78 | server.tool( 79 | "get-entries", 80 | "Get entries for a specific content type", 81 | { 82 | contentType: z.string(), 83 | }, 84 | async (parameters) => { 85 | const { contentType } = parameters; 86 | const entriesEndpoint = `https://cdn.contentful.com/spaces/${CONTENTFUL_SPACE_ID}/environments/${CONTENTFUL_ENVIRONMENT}/entries?content_type=${contentType}&limit=10`; 87 | 88 | try { 89 | const response = await fetch(entriesEndpoint, { 90 | headers: { 91 | Authorization: `Bearer ${CONTENTFUL_ACCESS_TOKEN}`, 92 | }, 93 | }); 94 | 95 | if (!response.ok) { 96 | throw new Error(`HTTP error! Status: ${response.status}`); 97 | } 98 | 99 | const data = await response.json(); 100 | 101 | return { 102 | content: [ 103 | { 104 | type: "text", 105 | text: JSON.stringify(data, null, 2), 106 | }, 107 | ], 108 | }; 109 | } catch (error: any) { 110 | console.error("Error fetching entries:", error); 111 | return { 112 | content: [ 113 | { 114 | type: "text", 115 | text: `Error: ${error.message}`, 116 | }, 117 | ], 118 | }; 119 | } 120 | } 121 | ); 122 | 123 | // Start the server 124 | async function main() { 125 | const transport = new StdioServerTransport(); 126 | await server.connect(transport); 127 | console.error( 128 | `CONTENTFUL MCP Server running on stdio - Connected to space: ${CONTENTFUL_SPACE_ID}` 129 | ); 130 | } 131 | 132 | main().catch((error) => { 133 | console.error("Fatal error in main():", error); 134 | process.exit(1); 135 | }); 136 | ```