Jump to content

Recommended Posts

Posted

I wanted a way to chat with my Processwire site and built a Module (PW MCP) and an MCP server to connect into it.

It's a private repo at the moment but it can be public if anyone finds it useful. It's basically a way to use the Cursor Chat Ui to query my site, fields, templates and content. 
Here's part of the readme which explains it better.

What Is It?
ProcessWire MCP is a bridge between ProcessWire and Cursor IDE (the AI-powered code editor). It lets you query your ProcessWire site's structure and content directly from Cursor's chat interface using natural language.
Instead of writing selectors or browsing the admin, you can just ask:

  • "What templates does this site have?"
  • "Show me the fields on the blog-post template"
  • "Search for pages containing 'summer'"
  • "Find all images with 'lake' in the filename"


Why I Built It
Cursor can see your template files and code in the local directory, but it can't see what's actually in your ProcessWire database — which templates and fields are registered, what pages exist, or what content they contain.

With ProcessWire MCP, the AI can:

  • Query the actual database schema (not just parse template files)
  • Look up page content by ID, path, or selector
  • Understand field configurations (types, settings, which templates use them)
  • Search across all text content and find files/images
  • Get RepeaterMatrix content with type labels
  • See file metadata (dimensions, descriptions, URLs)

It's the difference between seeing $page->body in code vs. knowing what that page's body actually contains.

Architecture

Cursor Chat → MCP Server (Node.js) → PHP CLI → ProcessWire API

The module consists of:

  • PwMcp — A ProcessWire module with a CLI interface
  • mcp-server — A Node.js server that speaks the Model Context Protocol

The CLI can also be used standalone for quick queries from terminal.

Available Commands
 

Command Description
health Check connection and get site info
list-templates List all templates with field counts
get-template [name] Get template details and fields
list-fields List all fields with types
get-field [name] Get field details and usage
get-page [id\|path] Get page by ID or path with all field values
query-pages [selector] Query pages using PW selectors
search [query] Search content across all text fields
search-files [query] Search files by name/extension
export-schema Export complete site schema

Example: Health Check

php site/modules/PwMcp/bin/pw-mcp.php health --pretty
{
  "status": "ok",
  "pwVersion": "3.0.241",
  "siteName": "www.example.com",
  "moduleLoaded": true,
  "counts": {
    "templates": 45,
    "fields": 72,
    "pages": 960
  }
}

Example: Content Search
Ask Cursor: "Search for pages containing 'summer'"

{
  "query": "summer",
  "count": 5,
  "results": [
    {
      "id": 1764,
      "title": "Lake District walks in summer",
      "path": "/guides/lake-district-summer/",
      "template": "page-guide",
      "matchedField": "Body",
      "snippet": "The Lake District offers some of the best walking trails in summer. From gentle lakeside strolls to challenging fell walks..."
    }
  ]
}

Example: File Search
Ask Cursor: "Find images with 'lake' in the filename"

{
  "query": "lake",
  "count": 5,
  "results": [
    {
      "filename": "lake-windermere-sunset.jpg",
      "url": "/site/assets/files/1070/lake-windermere-sunset.jpg",
      "size": 31207,
      "sizeStr": "30.5 kB",
      "description": "Sunset over Lake Windermere",
      "field": "Images",
      "page": {
        "id": 1070,
        "title": "Lake District walks in summer",
        "path": "/guides/lake-district-summer/"
      },
      "width": 500,
      "height": 626
    }
  ]
}

Example: Get Page with All Fields
Ask Cursor: "Get the page at /about/"

{
  "id": 1050,
  "name": "about",
  "path": "/about/",
  "url": "/about/",
  "template": "basic-page",
  "status": 1,
  "statusName": "published",
  "parent": {
    "id": 1,
    "path": "/",
    "title": "Home"
  },
  "numChildren": 5,
  "created": "2023-05-15T10:30:00+00:00",
  "modified": "2024-11-20T14:22:00+00:00",
  "fields": {
    "title": "About Us",
    "body": "<p>We are a team of dedicated professionals...</p>",
    "Images": {
      "_count": 2,
      "_files": ["team-photo.jpg", "office.jpg"]
    }
  }
}

Example: RepeaterMatrix Support
The module fully supports RepeaterMatrix fields, returning the actual content with type labels:

{
  "matrix": {
    "_count": 3,
    "_items": [
      {
        "_typeId": 1,
        "_typeLabel": "Body",
        "Body": "<h2>Welcome to our guide</h2><p>This guide covers...</p>",
        "Images": null
      },
      {
        "_typeId": 2,
        "_typeLabel": "FAQs",
        "faq_question": "What is the best time to visit?",
        "faq_answer": "The summer months offer the best weather for walking..."
      },
      {
        "_typeId": 3,
        "_typeLabel": "Call to Action",
        "cta_title": "Plan Your Visit",
        "cta_link": "/contact/"
      }
    ]
  }
}

So thats the first part done and working.

My next plan is to be able to 

1. PULL / convert a databse page into a local text file which lists all page properties, fields, template etc
2. edit the file as a local text file
3 PUSH the text file back into PW so that the original content picks up the changes

Just having fun and building something useful. Very likely there are similar solutions or better ways to handle this but this suits my workflow ATM.
Cheers
P


 

  • Like 2
Posted

Ok, Phase 2 is working....

I can now say to Cursor 
Get any page with "Lake Windermere" in the title
Processwire will create 2 local files in site/syncs
1 x a YAML version of the file listing all fields, content etc
1 x a sidecar file listing the meta data 

In the site/syn folder these pages are created using the matching directory structure of the page paths so it's essentially like the PW tree

I can then edit the YAML and tell Cursor to push the page back to PW and the page is updated

Posted

I always ask Cusror to summarise the changes because it's much better at writing than me so here they are:

 

Quote

What We Built Today

The Problem

Previously, to edit ProcessWire content, you had to either:
  • Log into the admin panel and use the web interface
  • Or ask the AI to read page content, but it couldn't write back

The Solution

We added Pull/Push sync — a Git-like workflow for ProcessWire content:
  1. Pull a page from ProcessWire → saves it as an editable YAML file
  1. Edit the YAML in your IDE (or have AI edit it)
  1. Push the changes back → updates the live page

Key Features

Feature Description
Natural language Just say "Pull the About page" in Cursor chat
Mirrored folders Pages save to site/syncs/[page-path]/ matching your site structure
Readable YAML Content is human-editable, not database dumps
Readable dates Dates show as 2026-01-27 not Unix timestamps
Dry-run by default Push shows what would change before applying
Conflict detection Warns if someone else edited the page since you pulled
Revision tracking Each pull stores a hash to detect remote changes
 
 

What Works Now

  • Pull any page by path or ID
  • Edit text fields, titles, SEO fields, dates
  • Push changes back to ProcessWire
  • Re-pull to get latest version

What's Not Yet Implemented

  • Editing RepeaterMatrix content (the complex page builders)
  • Uploading/changing images and files
  • Bulk pull/push of multiple pages
  • Creating new pages

Example Workflow

> You: "Pull the blog post about the Lake District" > AI: Finds and pulls it to site/syncs/news/posts/lake-district-walks/page.yaml > You: Edit the YAML file, save > You: "Push the Lake District walks page" > AI: Shows preview, then applies changes to ProcessWire

 

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now
×
×
  • Create New...