Strapi MCP Server
by andychoi
README.md
# Strapi MCP Server (Strapi 5.x Only)
An MCP server for Strapi 5.x CMS that gives AI agents full access to content types, entries, media, and schema management through the [Model Context Protocol](https://modelcontextprotocol.io/).
## Features
- **Full CRUD** on collection types and single types
- **Media uploads** — base64 or local file path
- **Relation management** — connect/disconnect related entries
- **Schema management** — create, update, delete content types and components (dev mode)
- **Per-request auth** — override server-level credentials with a JWT or API token per tool call
- **Structured audit logging** — JSON audit trail for all write operations
- **Request correlation** — optional `requestId` for multi-agent tracing
- **Input validation** — content type UIDs, entry IDs, file paths validated against injection (OWASP MCP02)
- **Structured logging** — `LOG_LEVEL` env var controls verbosity (`error` / `warn` / `info` / `debug`)
- **Dev-mode gating** — schema management tools hidden unless `STRAPI_DEV_MODE=true` (20 tools in production, 25 in dev)
- **TTL-based cache** — content types cached with configurable `STRAPI_CACHE_TTL`
## Quick Start
### 1. Install
```bash
npm install mcp-strapi
```
Or from source:
```bash
git clone https://github.com/andychoi/mcp-strapi.git
cd mcp-strapi
npm install
npm run build
```
### 2. Configure
Create a `.env` file (add to `.gitignore`):
```dotenv
STRAPI_URL=http://localhost:1337
STRAPI_ADMIN_EMAIL=admin@example.com
STRAPI_ADMIN_PASSWORD=your_password
# STRAPI_API_TOKEN=your_api_token # Optional fallback
# STRAPI_DEV_MODE=true # Enable schema management tools
# LOG_LEVEL=debug # error | warn | info (default) | debug
# STRAPI_CACHE_TTL=300000 # Content type cache TTL in ms (default: 5 min)
```
### 3. Add to your MCP client
**Claude Desktop** (`~/Library/Application Support/Claude/claude_desktop_config.json` on macOS, `%APPDATA%/Claude/claude_desktop_config.json` on Windows):
```json
{
"mcpServers": {
"mcp-strapi": {
"command": "npx",
"args": ["mcp-strapi"],
"env": {
"STRAPI_URL": "http://localhost:1337",
"STRAPI_ADMIN_EMAIL": "admin@example.com",
"STRAPI_ADMIN_PASSWORD": "your_password"
}
}
}
}
```
**Cursor** (`~/.cursor/mcp.json`):
```json
{
"strapi-mcp": {
"command": "npx",
"args": ["mcp-strapi"],
"env": {
"STRAPI_URL": "http://localhost:1337",
"STRAPI_ADMIN_EMAIL": "admin@example.com",
"STRAPI_ADMIN_PASSWORD": "your_password"
}
}
}
```
If installed from source, replace `"npx"` / `["mcp-strapi"]` with `"node"` / `["/path/to/mcp-strapi/build/index.js"]`.
**Alternative — run directly:**
```bash
node --env-file=.env build/index.js
```
## Environment Variables
| Variable | Required | Default | Description |
|----------|----------|---------|-------------|
| `STRAPI_URL` | No | `http://localhost:1337` | Strapi instance URL |
| `STRAPI_ADMIN_EMAIL` | Recommended | — | Admin email for full functionality |
| `STRAPI_ADMIN_PASSWORD` | Recommended | — | Admin password |
| `STRAPI_API_TOKEN` | No | — | API token fallback (limited permissions) |
| `STRAPI_DEV_MODE` | No | `false` | Enable schema management tools |
| `LOG_LEVEL` | No | `info` | Log verbosity: `error`, `warn`, `info`, `debug` |
| `STRAPI_CACHE_TTL` | No | `300000` | Content type cache TTL in milliseconds (5 min) |
### Authentication Priority
Per request: **`authToken` parameter** > **admin JWT** > **API token**
Admin credentials are recommended for full functionality (schema access, publish/unpublish, content type management).
## Tools
All 20 production tools accept an optional `authToken` parameter for per-request auth override.
### Content (Collection Types)
| Tool | Description |
|------|-------------|
| `list_content_types` | List all content types with `kind` (collectionType/singleType) and `singularName` |
| `get_entries` | Get entries with filtering, pagination, sorting, population |
| `get_entry` | Get a specific entry by ID |
| `create_entry` | Create a new entry |
| `update_entry` | Update an existing entry |
| `delete_entry` | Delete an entry |
| `publish_entry` | Publish an entry (admin required) |
| `unpublish_entry` | Unpublish an entry (admin required) |
### Content (Single Types)
| Tool | Description |
|------|-------------|
| `get_single_type` | Get a single type entry (e.g., homepage, site-settings) |
| `update_single_type` | Update a single type entry |
| `delete_single_type` | Delete a single type entry |
| `publish_single_type` | Publish a single type (admin required) |
| `unpublish_single_type` | Unpublish a single type (admin required) |
### Media
| Tool | Description |
|------|-------------|
| `upload_media` | Upload a file via base64 (max ~750KB file / 1MB base64) |
| `upload_media_from_path` | Upload from local file path (max 10MB) |
### Schema & Relations
| Tool | Description |
|------|-------------|
| `get_content_type_schema` | Get schema (fields, types, relations) |
| `connect_relation` | Connect related entries to a relation field |
| `disconnect_relation` | Disconnect related entries from a relation field |
| `list_components` | List all Strapi components |
| `get_component_schema` | Get a component's schema |
### Dev Mode Only (`STRAPI_DEV_MODE=true`)
| Tool | Description |
|------|-------------|
| `create_content_type` | Create a new content type (admin required) |
| `update_content_type` | Update a content type's attributes (admin required) |
| `delete_content_type` | Delete a content type (admin required) |
| `create_component` | Create a new component (admin required) |
| `update_component` | Update a component (admin required) |
## Usage Examples
### List content types
```json
{
"tool": "list_content_types",
"arguments": {}
}
```
### Get entries with filtering and pagination
```json
{
"tool": "get_entries",
"arguments": {
"contentType": "api::article.article",
"filters": { "title": { "$contains": "hello" } },
"pagination": { "page": 1, "pageSize": 10 },
"sort": ["title:asc", "createdAt:desc"],
"populate": ["author", "categories"]
}
}
```
### Create an entry
```json
{
"tool": "create_entry",
"arguments": {
"contentType": "api::article.article",
"data": {
"title": "My New Article",
"content": "Article body text."
}
}
}
```
### Per-request auth token
Override server credentials with a role-specific JWT or API token:
```json
{
"tool": "get_entries",
"arguments": {
"contentType": "api::article.article",
"authToken": "eyJhbGciOiJIUzI1NiIs..."
}
}
```
Use cases:
- Operate as a specific role (admin / author / reader)
- Different content types need different permission levels
- Test access with a scoped API token
### Single type operations
```json
{
"tool": "get_single_type",
"arguments": {
"contentType": "api::homepage.homepage",
"options": "{\"populate\": [\"hero\", \"seo\"]}"
}
}
```
```json
{
"tool": "update_single_type",
"arguments": {
"contentType": "api::homepage.homepage",
"data": { "title": "Welcome", "description": "Updated" }
}
}
```
### Upload media
**Base64 (small files):**
```json
{
"tool": "upload_media",
"arguments": {
"fileData": "<base64-encoded-data>",
"fileName": "image.jpg",
"fileType": "image/jpeg"
}
}
```
**File path (recommended for larger files):**
```json
{
"tool": "upload_media_from_path",
"arguments": {
"filePath": "/path/to/image.jpg"
}
}
```
### Connect/disconnect relations
```json
{
"tool": "connect_relation",
"arguments": {
"contentType": "api::article.article",
"id": "1",
"relationField": "authors",
"relatedIds": [2, 3]
}
}
```
### Create a content type (dev mode)
```json
{
"tool": "create_content_type",
"arguments": {
"displayName": "Product",
"singularName": "product",
"pluralName": "products",
"kind": "collectionType",
"description": "Store products",
"draftAndPublish": true,
"attributes": {
"name": { "type": "string", "required": true },
"price": { "type": "decimal", "required": true },
"stock": { "type": "integer" }
}
}
}
```
### Request correlation ID
Pass a `requestId` to trace requests across multi-agent systems. It appears in audit log entries:
```json
{
"tool": "create_entry",
"arguments": {
"contentType": "api::article.article",
"data": { "title": "Traced Article" },
"requestId": "agent-abc-req-123"
}
}
```
## Enterprise Features
### Audit Logging
All write operations emit structured JSON audit events to stderr:
```json
{"audit":true,"timestamp":"2026-02-22T03:00:00.000Z","action":"create_entry","contentType":"api::article.article","entryId":"42","authMethod":"adminJwt","status":"success","requestId":"agent-abc-req-123"}
```
Fields: `timestamp`, `action`, `contentType`, `entryId`, `authMethod` (`authToken` | `adminJwt` | `apiToken` | `none`), `status` (`success` | `error`), `requestId`, `error`.
Audit events are always emitted regardless of `LOG_LEVEL`.
### Request Correlation IDs
Every tool accepts an optional `requestId` parameter. When provided, the ID is included in all audit log entries for that request, enabling end-to-end tracing in multi-agent deployments.
### Content Type Caching
Content types are cached with a configurable TTL (default: 5 minutes). Set `STRAPI_CACHE_TTL` in milliseconds to adjust. The cache is automatically invalidated when schema management tools modify content types.
### Security
- **No token logging** — tokens never appear in logs (OWASP MCP01)
- **Input validation** — regex validation for content type UIDs, entry IDs, component UIDs, file paths (OWASP MCP02)
- **Sanitized errors** — raw Strapi error details only logged at debug level
- **Dev-mode gating** — destructive schema tools hidden in production
## Resource URIs
Content types are exposed as MCP resources:
- `strapi://content-type/api::article.article` — all articles
- `strapi://content-type/api::article.article/1` — article with ID 1
- `strapi://content-type/api::article.article?filters={"title":{"$contains":"hello"}}` — filtered
## Troubleshooting
### Placeholder API Token Error
```
[Error] STRAPI_API_TOKEN appears to be a placeholder value...
```
Replace `"strapi_token"` or `"your-api-token-here"` with a real API token from Strapi admin > Settings > API Tokens.
### Connection Refused
```
Cannot connect to Strapi instance: Connection refused
```
Ensure Strapi is running (`npm run develop`), the URL is correct, and the database is up.
### Authentication Failed
```
Cannot connect to Strapi instance: Authentication failed
```
Verify admin email/password or API token permissions. Ensure the admin user is active.
### Context Window Overflow with Uploads
Use `upload_media_from_path` instead of `upload_media` for files larger than ~500KB. The base64 tool has a 1MB limit (~750KB file).
### Permission Errors (403)
Use admin credentials for full access. If using an API token, ensure it has "Full access" permissions.
## Debugging
```bash
npm run inspector
```
Opens the [MCP Inspector](https://github.com/modelcontextprotocol/inspector) for debugging tool calls in your browser.
## Development
```bash
npm install # Install dependencies
npm run build # Build
npm run watch # Build with auto-rebuild
```
For deployment details, see [DEPLOYMENT.md](./DEPLOYMENT.md).
## License
MIT
This server cannot be deployed