Skip to content

Repository files navigation

wp-blockmarkup-mcp-server

An experimental local/remote MCP server for discovering, validating, and indexing Gutenberg block schemas and markup.

Caution

Early-stage notice: This repository is an experimental fork/refactor built on the original author's work. It is useful for local development and evaluation, but APIs, storage details, validation coverage, and deployment guidance may change. Validate generated content in your own WordPress environment before using it in production.

Important

AI-assisted refactor: The changes in this fork — including the Docker deployment, the HTTP transport, the Git/source policy modules, the test suite, and the rewritten documentation — were produced with the help of AI coding agents operating under human review. Upstream code is attributed as required by the MIT License; see NOTICE.

Project lineage

The original project is maintained by its author at pluginslab/wp-blockmarkup-mcp. This repository is a separate fork/refactor by Zaher Ghaibeh; it is not a replacement for, or an official release of, the upstream project.

Fork base: this fork was created from upstream commit e490fcd (full SHA e490fcded2b910f79eeb0ed087773a96e0d6dd30, upstream v1.1.1, 2026-05-13). Code, documentation, or configuration in this repository that is not attributed to Zaher Ghaibeh originates from that commit or earlier. Newer upstream commits are not part of this fork's base — check the upstream repository directly for changes after that point.

The upstream project remains the reference for the original implementation and its history. This fork keeps that core idea—extract real block source code instead of asking an AI model to guess Gutenberg markup—and adds a local-first workflow, stronger source and runtime boundaries, and container deployment.

What is different in this fork?

Area Original project This fork/refactor
Distribution npm-oriented examples Clone the repository and run it locally; no npm package distribution is intended
Runtime Primarily local MCP usage Local stdio plus an authenticated, read-only streamable HTTP mode
Deployment Local process Docker image and Docker Compose deployment with persistent SQLite storage
Source credentials Caller-selected token configuration Server-owned WP_BLOCKMARKUP_GITHUB_TOKEN for private GitHub sources
Source safety Basic repository configuration Canonical github.com URL checks, safe ref validation, path-containment checks, and bounded Git execution
HTTP safety Local integration focus Bearer authentication, origin checks, body limits, rate limits, and rejection of batch requests
Indexing and validation Core extraction and markup validation Structured indexing outcomes (success, degraded, failure), capability-aware markup generation, and stricter Gutenberg delimiter/save-function validation
Maintenance Upstream release flow Hermetic tests and linting for the fork; changes are still experimental and may diverge from upstream

The two repositories may evolve independently. Check both repositories before assuming that a feature, command, or database migration exists in the other one.

Why this exists

AI assistants often generate Gutenberg content from incomplete or stale training data. They can invent attributes, use the wrong CSS class conventions, ignore nesting rules, or emit static HTML for dynamic blocks. WordPress then reports invalid blocks and asks for recovery.

This fork and refactor also make the project practical to host online as a shared team service. Instead of every developer running a separate local copy, a team can deploy one authenticated, continuously indexed instance and use it from their MCP-compatible clients. Local-first execution remains useful for development and testing, but shared online hosting is an intended way to operate the project.

This server indexes the actual source code of WordPress core, WooCommerce, or another block-based plugin so an MCP-compatible assistant can:

  1. Search for relevant blocks.
  2. Read their attributes, supports, variations, and save patterns.
  3. Generate markup from verified examples.
  4. Validate markup before sending it to WordPress.

Indexed data includes block metadata, attribute schemas, support configurations, variations, UI mappings, save-function patterns, block type classification, and validation status.

Quick start: run from a clone

This project is intentionally local-first. Clone it, install dependencies, and run the checked-out source:

git clone https://github.com/zaherg/wp-blockmarkup-mcp-server.git
cd wp-blockmarkup-mcp-server
npm ci

Node.js 22, 24, or 26 is required. The repository is not intended to be installed from or published as an npm package.

Index a source

Run the CLI from the checkout. The CLI's internal program name (used in --help output) is wp-blocks:

node src/cli.js source:add \
  --name gutenberg \
  --type github-public \
  --repo https://github.com/WordPress/gutenberg \
  --branch trunk

Other source types:

# Public GitHub repository
node src/cli.js source:add \
  --name woocommerce-blocks \
  --type github-public \
  --repo https://github.com/woocommerce/woocommerce \
  --subfolder plugins/woocommerce-blocks \
  --branch trunk

# Private GitHub repository. The token is owned by the server process.
export WP_BLOCKMARKUP_GITHUB_TOKEN=your-fine-grained-read-token
node src/cli.js source:add \
  --name my-private-blocks \
  --type github-private \
  --repo https://github.com/your-org/your-blocks \
  --branch main

# A local plugin under active development
node src/cli.js source:add \
  --name my-local-blocks \
  --type local-folder \
  --path /absolute/path/to/wp-content/plugins/my-blocks

Useful commands:

node src/cli.js source:list
node src/cli.js index --source gutenberg
node src/cli.js search "image gallery"
node src/cli.js schema core/paragraph
node src/cli.js validate '<!-- wp:paragraph --><p>Hello</p><!-- /wp:paragraph -->'
node src/cli.js stats
node src/cli.js rebuild-index

Connect a local MCP client

For a client that starts MCP servers with a command, point it at the checked-out file. For example, a Claude Code-style configuration is:

{
  "mcpServers": {
    "wp-blockmarkup": {
      "command": "node",
      "args": ["/absolute/path/to/wp-blockmarkup-mcp-server/src/mcp-server.js"]
    }
  }
}

The default stdio server exposes both read tools and local source-management tools. Keep the process on a trusted machine when using administrative operations. See CLI vs remote: who can do what below for a side-by-side comparison of every path.

Docker installation and deployment

The repository includes a Dockerfile and Compose file. The published image is available for convenience, while the source remains the canonical way to run and modify this experimental fork:

git clone https://github.com/zaherg/wp-blockmarkup-mcp-server.git
cd wp-blockmarkup-mcp-server

# Required for HTTP mode. Use a random value with at least 32 UTF-8 bytes.
printf 'MCP_AUTH_TOKEN=%s\n' "$(openssl rand -hex 32)" > .env

docker compose pull
docker compose up -d
docker compose logs -f wp-blockmarkup-mcp-server

The default Compose binding is 127.0.0.1:3000; the MCP endpoint is http://127.0.0.1:3000/mcp. The SQLite database is stored in the named wp-data volume and survives container recreation.

Index a GitHub source inside the running container:

docker compose exec wp-blockmarkup-mcp-server node src/cli.js source:add \
  --name woocommerce-blocks \
  --type github-public \
  --repo https://github.com/woocommerce/woocommerce \
  --subfolder plugins/woocommerce-blocks \
  --branch trunk \
  --no-index

docker compose exec wp-blockmarkup-mcp-server node src/cli.js index --source woocommerce-blocks
docker compose exec wp-blockmarkup-mcp-server node src/cli.js stats

For private repositories, add WP_BLOCKMARKUP_GITHUB_TOKEN to .env and use a fine-grained, read-only token limited to the required repositories. Never put credentials in the repository URL.

Build the image locally

To build instead of pulling, uncomment build: . in docker-compose.yml and comment out its image: line, then run:

docker compose build
docker compose up -d

The Dockerfile uses Node 24 slim and supports the common linux/amd64 and linux/arm64 platforms. See DEPLOYMENT.md for ingress, TLS, volume, environment-variable, and multi-architecture guidance.

Connect an HTTP MCP client

Configure the client as a streamable HTTP MCP server and send the same bearer token (the value of MCP_AUTH_TOKEN in .env):

{
  "mcpServers": {
    "wp-blockmarkup": {
      "url": "http://127.0.0.1:3000/mcp",
      "headers": {
        "Authorization": "Bearer replace-with-the-value-from-.env"
      }
    }
  }
}

Hosted HTTP intentionally exposes read-only MCP tools by default. Set MCP_ENABLE_HTTP_ADMIN=true together with a valid MCP_AUTH_TOKEN to expose the four admin tools over HTTP. Admin remains off by default, and enabling it without a token fails startup even on loopback. If the service is bound beyond loopback, put it behind HTTPS and a trusted ingress, keep the backend hop private, and configure a strong token and allowed origins.

MCP tools

Every tool the server exposes is listed below. Dynamic blocks are represented with valid self-closing comment markup and report attributes_only because their final HTML is rendered by PHP on the WordPress site.

MCP tool Local CLI equivalent Local stdio MCP Remote HTTP MCP
search_blocks search (equivalent search path) yes yes
get_block_schema schema (includes schema, attributes, variations, and examples) yes yes
get_block_markup partial via schema examples; no dedicated CLI command yes yes
validate_markup partial via validate (structural validation only) yes yes
list_block_attributes partial via schema; no dedicated CLI command yes yes
search_variations no direct CLI equivalent yes yes
source_list source:list yes yes, only with MCP_ENABLE_HTTP_ADMIN=true
source_add source:add, all source types yes, all source types yes, only with MCP_ENABLE_HTTP_ADMIN=true; GitHub types only
source_remove source:remove yes yes, only with MCP_ENABLE_HTTP_ADMIN=true
source_index index yes yes, only with MCP_ENABLE_HTTP_ADMIN=true; guarded by the shared-principal budget and MCP-process lock

get_block_markup returns both the readable Markdown content response and a machine-readable structuredContent object. The structured result contains block_name, count, and an ordered examples array. Each example exposes title, nullable description, markup, validation_status, and features_used (an array of strings). Stdio and Streamable HTTP share this same tool contract.

Remote admin entries are gated by MCP_ENABLE_HTTP_ADMIN at server startup. Per-client authorization is not supported in this release; see DEPLOYMENT.md for the two-deployment pattern.

CLI vs remote: who can do what

Local CLI (node src/cli.js) runs on the same machine as the SQLite index and the GitHub token, talks to the database directly, and is the only path that can run an immediate rebuild-index or operate without a running MCP server. It can also manage local-folder sources, as can trusted stdio MCP. Use the CLI for setup, recovery, and one-off maintenance you might otherwise perform with sqlite3 against the database.

Local stdio MCP is started by an MCP client on a trusted machine. It exposes all ten tools, including the four admin ones, and there is no opt-in flag; the assumption is that whoever can spawn the process already has the same privileges as the user account running it. Use it when a single developer wants an assistant to read blocks and manage sources without leaving their workstation.

Remote HTTP MCP is the multi-client path. By default it is read-only, exactly as before. When MCP_ENABLE_HTTP_ADMIN=true is set, startup requires MCP_AUTH_TOKEN even on loopback and the server exposes the four admin tools to any holder of that token. Because the opt-in is server-wide, every authenticated client is the same principal. Treat the bearer as a database root password: use HTTPS termination, a private ingress hop, ingress-side flood controls, a strong allowed-origins list, and preferably two deployments (admin-on for operators, admin-off for downstream assistants).

Two things the remote path will not do, even with the opt-in on:

  1. Add a local-folder source. local-folder requires a path on the server's filesystem and is not safe to expose to a network caller, so it is rejected at the HTTP boundary. Use the CLI or stdio.
  2. Run two source_index / source_remove MCP operations in parallel inside one server process. The in-process lock covers HTTP and stdio MCP calls; a second call gets a clean isError: true result with the active operation and acquisition time. It does not coordinate separate CLI processes or server replicas, so operators must not run those concurrently against the same data directory.

How indexing and validation work

The indexer discovers block.json files, parses metadata and JavaScript ASTs, classifies blocks as static, dynamic, or hybrid, generates feature-specific examples, and stores the results in SQLite with FTS5 search.

Generated examples are checked in tiers:

  1. Structural validation uses the official WordPress block serialization parser, checks delimiter syntax, JSON attributes, known attributes, types, enums, and nested blocks.
  2. Save-function validation checks static/hybrid output against extracted wrapper, class, style, and InnerBlocks patterns.

Validation statuses include verified, structural_only, attributes_only, and invalid. Indexing reports success, degraded, or failure so partial source failures are not presented as complete success.

Storage and security notes

By default, local data is stored under ~/.wp-blockmarkup-mcp-server/:

~/.wp-blockmarkup-mcp-server/
  blocks.db   # SQLite database and FTS index
  cache/      # cached Git repositories

Important runtime controls include:

  • MCP_TRANSPORT (stdio by default, http for hosted mode)
  • MCP_HOST and MCP_PORT
  • MCP_AUTH_TOKEN (required for non-loopback HTTP and at least 32 UTF-8 bytes; also required on loopback when MCP_ENABLE_HTTP_ADMIN=true)
  • MCP_ENABLE_HTTP_ADMIN (opt-in admin tools over HTTP; defaults to off)
  • MCP_ALLOWED_ORIGINS
  • MCP_BODY_LIMIT_BYTES
  • MCP_RATE_LIMIT_REQUESTS and MCP_RATE_LIMIT_WINDOW_MS
  • MCP_ADMIN_RATE_LIMIT_REQUESTS and MCP_ADMIN_RATE_LIMIT_WINDOW_MS (shared-principal budget for source_* calls when admin is enabled)
  • WP_BLOCKMARKUP_GITHUB_TOKEN for private GitHub sources

Git operations run with bounded timeouts and isolated credential/config handling. Repository URLs are restricted to credential-free HTTPS github.com URLs, refs are normalized, and indexed subfolders must remain inside the checked-out source.

Development

Use Worktrunk (wt) to manage isolated development worktrees. Create a dedicated worktree for each feature or fix instead of working directly on main. The repository hooks are defined in .config/wt.toml; run wt --help for the commands available in your installation.

npm ci
npm test
npm run lint:check

npm run lint:check runs Biome over src and tests (see biome.json for the rule set). The formatter and linter are both active. npm run lint:fix applies safe auto-fixes; npm run format:fix reformats the source.

The project currently targets Node.js 22, 24, and 26 in CI. This fork is still experimental; passing tests do not guarantee compatibility with every WordPress release, plugin, theme, MCP client, or production ingress.

Relationship to theme-aware content generation

This server describes block schemas and valid markup patterns. It does not know the active site's color palette, typography scale, spacing tokens, or other theme.json values. Pair it with a WordPress/site MCP that can read the active theme, then validate the final markup here before publishing it.

License

MIT. See LICENSE for the full text.

This project is a fork/refactor of pluginslab/wp-blockmarkup-mcp, originally authored by Marcel Schmitz and released under the MIT License. The original copyright notice is preserved in the LICENSE file as required by that license. See NOTICE for the fork attribution statement.

The fork uses the npm name @zaherg/wp-blockmarkup-mcp-server to distinguish it from the upstream wp-blockmarkup-mcp; this fork is not intended to be installed from or published to npm — run it from a clone.

About

wp-blockmarkup-mcp-server is a local/remote MCP server that extracts, validates, and indexes every Gutenberg block from WordPress core, WooCommerce, or any block-based plugin you work with.

Resources

Stars

1 star

Watchers

1 watching

Forks

Releases

Packages

Used by

Contributors

Languages