Skip to content

elabx/processwire-mcp

Repository files navigation

ProcessWire MCP Server

An extensible MCP (Model Context Protocol) server for ProcessWire CMS that enables AI assistants like Claude to interact with your ProcessWire installation.

Features

  • Full ProcessWire API access - Query, create, update, and delete pages
  • Template & field management - Inspect and modify your site's data structure
  • User & role management - Create users, manage roles and permissions
  • Module management - List, install, and configure modules
  • File management - Access page files and images
  • Extensible architecture - Third-party modules can register custom tools
  • Security-focused - Admin pages blocked, configurable restrictions

Requirements

  • PHP 8.1+
  • ProcessWire 3.0.200+
  • Composer

Installation

# Using Composer (recommended)
composer require elabx/processwire-mcp

# With DDEV
ddev composer require elabx/processwire-mcp

Usage

Running the Server

The server auto-detects your ProcessWire root when installed via Composer — no path flag needed:

# Auto-detects PW root (recommended)
vendor/bin/pw-mcp-server

# With DDEV
ddev exec php vendor/bin/pw-mcp-server

# Explicit path (if auto-detect doesn't work)
php vendor/bin/pw-mcp-server --pw-path=/path/to/processwire

# Using environment variable
PW_PATH=/path/to/processwire php vendor/bin/pw-mcp-server

Claude Code Configuration

Add to your project's .claude/settings.json or ~/.claude/settings.json:

{
  "mcpServers": {
    "processwire": {
      "command": "ddev",
      "args": ["exec", "php", "vendor/bin/pw-mcp-server"]
    }
  }
}

For non-DDEV setups:

{
  "mcpServers": {
    "processwire": {
      "command": "php",
      "args": ["vendor/bin/pw-mcp-server"]
    }
  }
}

Available Tools

Page Tools

Tool Description
find_pages Query pages with ProcessWire selectors
get_page Get single page by ID or path
create_page Create new page
update_page Update page field values
delete_page Trash or permanently delete page
get_children Get child pages of a parent
clone_page Clone a page, optionally with children
sort_pages Sort a page relative to a sibling
restore_page Restore a page from trash

Template Tools

Tool Description
find_templates Find templates using ProcessWire selector syntax
list_templates List all available templates
get_template_fields Get template fields and configuration
get_template_file Get template file path
create_template Create a new template
update_template Update template settings (label, restrictions, cache, etc.)
delete_template Delete a template (must have no pages)
clone_template Clone a template with all fields and settings
add_field_to_template Add a field to a template with positioning
remove_field_from_template Remove a field from a template

Field Tools

Tool Description
find_fields Find fields using ProcessWire selector syntax
list_fields List all fields, optionally filter by type
get_field Get field details and configuration
list_field_types List available field types
create_field Create a new field with type and settings
update_field Update field properties
delete_field Delete a field (must not be in use)
clone_field Clone a field with all settings

User Tools

Tool Description
list_users List users (optionally by role)
get_user Get user details
create_user Create a user with email, password, and roles
update_user Update user email, password, or roles
delete_user Delete a user
list_roles List all roles

Role Tools

Tool Description
create_role Create a new role with optional permissions
update_role Add or remove permissions from a role
delete_role Delete a role (must not be assigned to users)
list_permissions List all available permissions
create_permission Create a new permission
set_template_access Set role-based access control on a template

Module Tools

Tool Description
list_modules List all modules (optionally installed only)
get_module_info Get module details and requirements
install_module Install a module (requires confirmation)
uninstall_module Uninstall a module (requires confirmation)
get_module_config Get module configuration (sensitive values redacted)
save_module_config Save module configuration values

File Tools

Tool Description
get_page_files Get files/images attached to a page
get_image_variations Get image size variations
get_page_files_path Get page files directory path

Available Resources

Resources provide read-only access to ProcessWire data:

URI Description
templates://list All templates with their fields
fields://list All field definitions
users://list All users (basic info only)
roles://list All roles with permissions

Extending with Custom Tools

Third-party ProcessWire modules can register custom MCP tools by extending ProcessWireMcpTool:

1. Create Your Tool Class

<?php

namespace YourVendor\YourModule;

use Elabx\ProcessWireMcp\Tool\ProcessWireMcpTool;
use Mcp\Capability\Attribute\McpTool;

class MyCustomTools extends ProcessWireMcpTool
{
    public static function getToolInfo(): array
    {
        return [
            'name' => 'my_custom_tools',
            'description' => 'Custom tools for my module',
            'priority' => 100, // Lower = loaded first
        ];
    }

    #[McpTool(
        name: 'my_custom_operation',
        description: 'Does something custom with pages'
    )]
    public function myCustomOperation(string $selector): array
    {
        // Use ProcessWire APIs via inherited methods
        $count = $this->pages()->count($selector);

        return $this->success([
            'count' => $count,
        ]);
    }
}

2. Register via Hook

In your ProcessWire module:

<?php

namespace ProcessWire;

class MyModule extends WireData implements Module
{
    public static function getModuleInfo(): array
    {
        return [
            'title' => 'My Module',
            'requires' => ['ProcessWireMcp'],
            // ...
        ];
    }

    public function ready(): void
    {
        $this->addHookAfter('ProcessWireMcp::getToolClasses', function($event) {
            $classes = $event->return;
            $classes[] = \YourVendor\YourModule\MyCustomTools::class;
            $event->return = $classes;
        });
    }
}

Base Class Methods

ProcessWireMcpTool provides these helper methods:

// ProcessWire API access
$this->pages()      // Pages API
$this->templates()  // Templates API
$this->fields()     // Fields API
$this->users()      // Users API
$this->sanitizer()  // Sanitizer API
$this->get($name)   // Any wire service

// Security helpers
$this->isAdminPage($page)          // Check if page is admin
$this->assertNotAdminPage($page)   // Throw if admin page
$this->excludeAdminFromSelector($selector)  // Add admin exclusion

// Response helpers
$this->success($data, $message)    // Success response
$this->error($message, $code)      // Error response
$this->pageToArray($page, $fields) // Convert page to array

Security

The MCP server includes several security measures:

  1. Admin page protection - Pages under /processwire/ (ID 2) are blocked
  2. No password exposure - User tools never return password data
  3. Selector filtering - Configurable blocked selectors (via module config)
  4. System page protection - Cannot delete system pages (ID <= 7)
  5. File restrictions - Only ProcessWire-managed files accessible
  6. Module safety - Install/uninstall require explicit confirmation
  7. Config redaction - Sensitive module config values (passwords, keys) are redacted

Optional ProcessWire Module

Install the optional ProcessWireMcp module for:

  • Configuration UI in ProcessWire admin
  • Custom blocked selector configuration
  • Hook support for third-party extensions

Copy ProcessWireMcp.module.php to site/modules/ProcessWireMcp/ and install via ProcessWire admin.

Testing

Tests run against a live ProcessWire installation. You can use either Docker (self-contained) or DDEV (existing dev environment).

Docker (recommended for CI)

The Docker setup provisions a fresh ProcessWire install and runs the full test suite with a single command:

# Run all tests
docker compose -f docker-compose.test.yml run --rm tests

# Run a specific test class (use --filter, not file paths)
docker compose -f docker-compose.test.yml run --rm tests --filter=PageToolsTest

# Run multiple test classes
docker compose -f docker-compose.test.yml run --rm tests --filter='FieldToolsTest|TemplateToolsTest'

# Run with verbose output
docker compose -f docker-compose.test.yml run --rm tests --testdox

# Tear down containers
docker compose -f docker-compose.test.yml down --volumes

# Rebuild after Dockerfile changes
docker compose -f docker-compose.test.yml build --no-cache tests

Note: Use --filter to select tests, not file paths. Passing paths like tests/Tool/PageToolsTest.php will fail with "Test file not found" because the package is symlinked inside the container at a different location (/package via Composer path repo) than the PHPUnit working directory (/var/www/html).

DDEV

If you already have a DDEV project with ProcessWire:

Prerequisites

  • A working DDEV project with ProcessWire installed and a populated database
  • The package mounted into the DDEV container (e.g. via a docker-compose volume at /packages/ProcessWireMcp)
  • The package installed as a Composer dependency so its classes are autoloaded
  • PHPUnit available in the project's vendor (e.g. phpunit/phpunit in require-dev)
  • The test namespace registered in your project's autoload-dev:
{
  "autoload-dev": {
    "psr-4": {
      "Elabx\\ProcessWireMcp\\Tests\\": "/packages/ProcessWireMcp/tests/"
    }
  }
}

After changing autoload config, regenerate the autoloader:

ddev composer dump-autoload

Running Tests

ddev exec vendor/bin/phpunit --configuration /packages/ProcessWireMcp/phpunit.xml

With readable output:

ddev exec vendor/bin/phpunit --configuration /packages/ProcessWireMcp/phpunit.xml --testdox

Run a specific test class:

ddev exec vendor/bin/phpunit --configuration /packages/ProcessWireMcp/phpunit.xml --filter PageTools

How It Works

The test bootstrap (tests/bootstrap.php) loads the host project's Composer autoloader, then boots ProcessWire by including its index.php. This gives tests access to the full ProcessWire API with a real database connection — the same environment the MCP server runs in. The PW_PATH environment variable controls the ProcessWire root (defaults to /var/www/html).

tests/ProcessWireTestCase.php is the base class for all tests. It exposes $this->wire() which returns the bootstrapped ProcessWire instance.

Writing Tests

Extend ProcessWireTestCase and instantiate tool classes directly:

<?php

namespace Elabx\ProcessWireMcp\Tests\Tool;

use Elabx\ProcessWireMcp\Tests\ProcessWireTestCase;
use Elabx\ProcessWireMcp\Tool\Core\PageTools;

class PageToolsTest extends ProcessWireTestCase
{
    private PageTools $tools;

    protected function setUp(): void
    {
        parent::setUp();
        $this->tools = new PageTools();
        $this->tools->setWire($this->wire());
    }

    public function testFindPagesReturnsResults(): void
    {
        $result = $this->tools->findPages('limit=3');

        $this->assertTrue($result['success']);
        $this->assertArrayHasKey('pages', $result['data']);
    }
}

License

MIT License

Contributing

Contributions are welcome! Please open an issue or submit a pull request.

Credits

Built with the MCP PHP SDK.

About

No description, website, or topics provided.

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

 
 
 

Contributors