npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

ocyris-strapi-builder-mcp

v1.0.4

Published

MCP plugin that exposes Strapi content types and data to LLMs for agentic UI building

Downloads

553

Readme

Strapi Builder MCP

A Strapi v5 plugin that exposes your content types, components, and data via the Model Context Protocol (MCP) — enabling AI tools like Claude Code, Claude Desktop, and Cursor to understand, query, and manage your Strapi backend.

How It Works

sequenceDiagram
    participant AI as AI Tool (Claude Code / Cursor)
    participant MCP as MCP Endpoint
    participant Auth as Auth Middleware
    participant Tools as Tool Registry
    participant Strapi as Strapi Internal APIs

    AI->>MCP: POST /api/strapi-builder-mcp/mcp
    MCP->>Auth: Check Bearer token
    Auth-->>MCP: Authenticated
    MCP->>Tools: Route to tool handler
    Tools->>Strapi: Query content types / documents
    Strapi-->>Tools: Data
    Tools-->>AI: JSON response (sanitized)

The plugin adds a single HTTP endpoint to your Strapi server. Any MCP-compatible client connects to it and discovers available tools automatically. The AI can then explore your API, query data, and (in development mode) create content and manage permissions.

Architecture

graph TB
    subgraph "MCP Endpoint"
        Controller["Controller<br/>POST /api/strapi-builder-mcp/mcp"]
        Transport["StreamableHTTPServerTransport<br/>Session management"]
        Server["MCP Server<br/>Tool routing"]
    end

    subgraph "Auth"
        Middleware["Bearer Token Middleware"]
    end

    subgraph "Tool Registry"
        direction TB
        Read["Read Tools (11)<br/>Always available"]
        Write["Write Tools (4)<br/>Dev mode only"]
    end

    subgraph "Services"
        CT["ContentType Service<br/>Schema introspection"]
        Doc["Document Service<br/>CRUD operations"]
        Comp["Component Service<br/>Component listing"]
        Perm["Permission Service<br/>Public access control"]
    end

    subgraph "Utilities"
        Sanitize["Output Sanitizer<br/>Strip large fields"]
        Errors["Error Formatter<br/>Actionable messages"]
        Knowledge["Knowledge Base<br/>Strapi v5 patterns"]
    end

    Controller --> Middleware
    Middleware --> Transport
    Transport --> Server
    Server --> Read
    Server --> Write
    Read --> CT
    Read --> Doc
    Read --> Comp
    Read --> Perm
    Read --> Knowledge
    Write --> Doc
    Write --> Perm
    Doc --> Sanitize
    Server --> Errors

Directory Structure

server/src/
├── index.ts                    # Entry point (register, bootstrap, destroy)
├── bootstrap.ts                # Auth middleware + session management
├── destroy.ts                  # Graceful shutdown
├── register.ts                 # Registration phase
├── config/index.ts             # Plugin configuration
├── routes/content-api.ts       # HTTP route definitions
├── controllers/mcp.ts          # Request handler (StreamableHTTPServerTransport)
├── services/
│   ├── content-type.ts         # Schema introspection (getAll, isValidUid)
│   ├── document.ts             # Document CRUD (findMany, findOne)
│   ├── component.ts            # Component listing with pagination
│   └── permission.ts           # Public permission queries + updates
└── mcp/
    ├── server.ts               # MCP Server setup + tool registration
    ├── schemas/index.ts        # Zod validation for tool inputs
    ├── utils/
    │   ├── sanitize.ts         # Output sanitization + large field stripping
    │   └── errors.ts           # Actionable error message formatting
    └── tools/
        ├── index.ts            # Tool registry + dispatcher
        ├── explore-api.ts      # Complete API map with permissions
        ├── list-content-types.ts
        ├── list-components.ts
        ├── find-many.ts        # Query documents with filters
        ├── find-one.ts         # Fetch single document
        ├── get-page-structure.ts
        ├── get-api-response.ts # Test real REST API response
        ├── verify-endpoint.ts  # Endpoint health check
        ├── health-check.ts     # Server status + diagnostics
        ├── get-media.ts        # Media library browser
        ├── search-knowledge.ts # Strapi v5 knowledge base
        ├── create-entry.ts     # Create document (dev only)
        ├── update-entry.ts     # Update document (dev only)
        ├── delete-entry.ts     # Delete document (dev only)
        └── set-permissions.ts  # Manage public access (dev only)

Installation

npm install ocyris-strapi-builder-mcp

Add to your Strapi config:

// config/plugins.ts
export default () => ({
  'strapi-builder-mcp': {
    enabled: true,
  },
});

Rebuild and restart:

npm run build
npm run develop

The endpoint is available at POST /api/strapi-builder-mcp/mcp.

Authentication

All requests require a Bearer token:

  1. Go to Settings > API Tokens in Strapi admin
  2. Create a new token (Full Access recommended)
  3. Use it in requests: Authorization: Bearer your-token
flowchart LR
    A[MCP Client] -->|"Authorization: Bearer token"| B[Auth Middleware]
    B -->|Valid| C[MCP Server]
    B -->|Missing/Invalid| D[401 Unauthorized]

Connecting AI Tools

Claude Code

claude mcp add strapi \
  --transport http \
  http://localhost:1337/api/strapi-builder-mcp/mcp \
  -H "Authorization: Bearer your-token"

Verify: claude mcp list

Then use it:

"Use the strapi MCP to explore my API and build a blog page"

Claude Desktop

// claude_desktop_config.json
{
  "mcpServers": {
    "strapi": {
      "url": "http://localhost:1337/api/strapi-builder-mcp/mcp",
      "headers": {
        "Authorization": "Bearer your-token"
      }
    }
  }
}

Cursor

// .cursor/mcp.json
{
  "mcpServers": {
    "strapi": {
      "url": "http://localhost:1337/api/strapi-builder-mcp/mcp",
      "headers": {
        "Authorization": "Bearer your-token"
      }
    }
  }
}

Custom Agents (Vercel AI SDK)

import { createMCPClient } from '@ai-sdk/mcp'

const client = await createMCPClient({
  transport: {
    type: 'http',
    url: 'http://localhost:1337/api/strapi-builder-mcp/mcp',
    headers: {
      Authorization: 'Bearer your-token',
    },
  },
})

const tools = await client.tools() // Auto-discovers all available tools

Tools Reference

Read Tools (always available)

explore_api

Returns a complete API map — like a dynamic OpenAPI spec. Shows every endpoint with fields, relations, components, dynamic zones, and public access status.

{ "name": "explore_api", "arguments": { "verbose": true } }

Response includes isPublic and publicActions per endpoint:

{
  "endpoints": [
    {
      "name": "Article",
      "uid": "api::article.article",
      "endpoint": "/api/articles",
      "isPublic": true,
      "publicActions": ["find", "findOne"],
      "fields": ["title", "slug", "content"],
      "relations": [{ "field": "author", "target": "api::author.author" }]
    }
  ]
}

health_check

Server diagnostics: environment, uptime, plugins, content type counts, public permissions, and whether write tools are enabled.

{ "name": "health_check" }

find_many

Query documents with filtering, sorting, pagination, and population.

{
  "name": "find_many",
  "arguments": {
    "uid": "api::article.article",
    "filters": { "title": { "$containsi": "hello" } },
    "populate": "*",
    "sort": "createdAt:desc",
    "pagination": { "page": 1, "pageSize": 10 },
    "status": "published"
  }
}

Large content fields are stripped by default. Use includeContent: true to include them.

find_one

Fetch a single document by ID.

{
  "name": "find_one",
  "arguments": {
    "uid": "api::article.article",
    "documentId": "abc123",
    "populate": "*"
  }
}

get_page_structure

Structured summary of a content type — fields, dynamic zones, components, relations, and sample data. Great for generating TypeScript types.

{
  "name": "get_page_structure",
  "arguments": { "contentType": "landing-page" }
}

get_api_response

Test the actual REST API response exactly as the frontend would receive it. Detects auto-population middleware.

{
  "name": "get_api_response",
  "arguments": { "contentType": "article", "limit": 2 }
}

verify_endpoint

Health check for a specific endpoint: does it exist, is it published, is it auto-populated, what block types are available.

{
  "name": "verify_endpoint",
  "arguments": { "contentType": "article" }
}

get_media

Browse the media library with pagination and MIME type filtering.

{
  "name": "get_media",
  "arguments": { "mime": "image", "page": 1, "pageSize": 10 }
}

search_strapi_knowledge_sources

Search a bundled Strapi v5 knowledge base covering data fetching, population, filtering, dynamic zones, permissions, media handling, i18n, and more.

{
  "name": "search_strapi_knowledge_sources",
  "arguments": { "query": "how to populate dynamic zones" }
}

list_content_types / list_components

List all content types or components with full schema definitions.

Write Tools (development mode only)

flowchart TD
    A[Tool Request] --> B{NODE_ENV?}
    B -->|development| C[Execute Write Tool]
    B -->|production| D["Blocked: 'Only available in development mode'"]

Write tools are automatically hidden from the tool list in production. In development, they appear alongside read tools.

create_entry

{
  "name": "create_entry",
  "arguments": {
    "uid": "api::article.article",
    "data": { "title": "New Article", "slug": "new-article" },
    "status": "published"
  }
}

update_entry

{
  "name": "update_entry",
  "arguments": {
    "uid": "api::article.article",
    "documentId": "abc123",
    "data": { "title": "Updated Title" }
  }
}

delete_entry

{
  "name": "delete_entry",
  "arguments": {
    "uid": "api::article.article",
    "documentId": "abc123"
  }
}

set_permissions

Configure which actions are publicly accessible without authentication.

{
  "name": "set_permissions",
  "arguments": {
    "contentType": "article",
    "actions": ["find", "findOne"]
  }
}

Request Flow

flowchart TB
    A[MCP Client Request] --> B[Koa Middleware]
    B --> C{Bearer Token?}
    C -->|No| D[401 Unauthorized]
    C -->|Yes| E[Controller]
    E --> F{Session exists?}
    F -->|No| G[Create new session<br/>+ MCP Server + Transport]
    F -->|Yes| H[Reuse session]
    G --> I[StreamableHTTPServerTransport]
    H --> I
    I --> J[MCP Server]
    J --> K{Tool type?}
    K -->|ListTools| L[Return tool list<br/>filtered by environment]
    K -->|CallTool| M{Write tool in prod?}
    M -->|Yes| N[Blocked with error]
    M -->|No| O[Validate input with Zod]
    O --> P[Execute handler]
    P --> Q[Sanitize output]
    Q --> R[Return JSON response]

Session Management

stateDiagram-v2
    [*] --> Active: New request (no session ID)
    Active --> Active: Subsequent requests (same session ID)
    Active --> Expired: 4 hour timeout
    Expired --> [*]: Cleanup on next request
    Active --> Closed: Plugin destroy / server shutdown
    Closed --> [*]: Graceful cleanup
  • Sessions stored in-memory (Map<string, { server, transport }>)
  • Session ID via mcp-session-id HTTP header
  • 4-hour timeout per session
  • Expired sessions cleaned up probabilistically (~1% of requests)
  • All sessions closed gracefully on plugin destroy

Context Optimization

The plugin is designed to minimize token usage for LLM agents:

  • Large field stripping: Rich text, markdown, HTML, and long descriptions are automatically truncated to [N chars stripped] in find_many results
  • Truncation threshold: Strings over 300 characters are truncated
  • Override: Pass includeContent: true to get full content
  • Depth limit: Recursive stripping stops at depth 10
  • Output sanitization: Uses Strapi's built-in content API sanitizer to respect permissions (fail-closed)

Error Handling

All errors include actionable guidance:

| Error | Message | |-------|---------| | 403 Forbidden | "Use set_permissions tool to enable public access, or check API token permissions" | | 401 Unauthorized | "Verify Bearer token is valid and hasn't expired" | | 404 Not Found | "Use list_content_types to see available types" | | Connection refused | "Verify Strapi is running and URL is correct" | | Write in production | "Tool X is only available in development mode" |

CORS Configuration

The MCP protocol requires the mcp-session-id header. Update config/middlewares.ts:

export default [
  'strapi::logger',
  'strapi::errors',
  'strapi::security',
  {
    name: 'strapi::cors',
    config: {
      origin: '*',
      headers: ['Content-Type', 'Authorization', 'Accept', 'mcp-session-id'],
      expose: ['mcp-session-id'],
    },
  },
  'strapi::poweredBy',
  'strapi::query',
  'strapi::body',
  'strapi::session',
  'strapi::favicon',
  'strapi::public',
];

If using ngrok, also add ngrok-skip-browser-warning to headers.

Configuration

// config/plugins.ts
export default () => ({
  'strapi-builder-mcp': {
    enabled: true, // Set to false to disable entirely
  },
});

Requirements

  • Strapi v5.x
  • Node.js 18+

License

MIT