@diviops/mcp-server 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md ADDED
@@ -0,0 +1,228 @@
1
+ # Divi 5 MCP Server
2
+
3
+ MCP server that exposes Divi 5 Visual Builder operations as tools for Claude Code and Claude Desktop.
4
+
5
+ ```
6
+ Claude Code <-> MCP Server (stdio) <-> WordPress REST API <-> Divi MCP Plugin
7
+ ```
8
+
9
+ ## Requirements
10
+
11
+ - **Node.js** >= 18.0.0
12
+ - **WordPress** >= 5.6 (Application Passwords support)
13
+ - **Divi 5** theme active
14
+ - **DiviOps Agent** WordPress plugin installed and active
15
+
16
+ ## Setup
17
+
18
+ ### 1. Install the WordPress Plugin
19
+
20
+ Download and activate the **DiviOps Agent** plugin from the [releases page](https://github.com/oaris-dev/diviops-internal/releases).
21
+
22
+ ### 2. Create an Application Password
23
+
24
+ Go to **WP Admin -> Users -> Your Profile -> Application Passwords**:
25
+ - Enter a name (e.g., "MCP Server")
26
+ - Click "Add New Application Password"
27
+ - Copy the generated password
28
+
29
+ > **Tip:** Strip spaces from Application Passwords before use. WordPress generates them with spaces for readability but accepts them without. Spaces in shell commands can cause parsing issues.
30
+
31
+ ### 3. Configure Claude Code
32
+
33
+ ```bash
34
+ claude mcp add diviops-mcp -- env \
35
+ WP_URL=http://your-site.local \
36
+ WP_USER=your-wp-username \
37
+ WP_APP_PASSWORD=xxxxXXXXxxxxXXXXxxxxXXXX \
38
+ npx @diviops/mcp-server
39
+ ```
40
+
41
+ **With WP-CLI** (optional — enables `diviops_wp_cli` tool):
42
+ ```bash
43
+ claude mcp add diviops-mcp -- env \
44
+ WP_URL=http://your-site.local \
45
+ WP_USER=your-wp-username \
46
+ WP_APP_PASSWORD=xxxxXXXXxxxxXXXXxxxxXXXX \
47
+ WP_PATH="/path/to/wordpress" \
48
+ npx @diviops/mcp-server
49
+ ```
50
+
51
+ **With Docker-based WP-CLI** (optional — uses a custom command prefix):
52
+ ```bash
53
+ claude mcp add diviops-mcp -- env \
54
+ WP_URL=https://site-name.ddev.site \
55
+ WP_USER=your-wp-username \
56
+ WP_APP_PASSWORD=xxxxXXXXxxxxXXXXxxxxXXXX \
57
+ WP_CLI_CMD="ddev wp" \
58
+ npx @diviops/mcp-server
59
+ ```
60
+
61
+ ### Environment Variables
62
+
63
+ | Variable | Required | Description |
64
+ |----------|----------|-------------|
65
+ | `WP_URL` | Yes | WordPress site URL (e.g. `http://mysite.local`) |
66
+ | `WP_USER` | Yes | WordPress username with Editor or Admin role |
67
+ | `WP_APP_PASSWORD` | Yes | Application Password (spaces stripped) |
68
+ | `WP_PATH` | No | WordPress filesystem path for Local by Flywheel, or wrapper working directory when `WP_CLI_CMD` needs project context |
69
+ | `WP_CLI_CMD` | No | Custom WP-CLI command prefix for containerized environments, e.g. `ddev wp`, `npx wp-env run cli wp`, `docker exec -u www-data devkinsta_fpm wp --path=/www/kinsta/public/sitename` |
70
+ | `LOCAL_SITE_ID` | No | Override auto-detection of Local by Flywheel site ID |
71
+ | `DIVIOPS_WP_CLI_ALLOW` | No | Comma-separated list of extended WP-CLI commands to enable (see [WP-CLI Security](#wp-cli-security)) |
72
+
73
+ ### Local Development Environments
74
+
75
+ The server connects via standard WordPress REST API and works with any environment that exposes WordPress over HTTP with Application Password support.
76
+
77
+ | Environment | WP_URL | WP-CLI setup | Notes |
78
+ |-------------|--------|--------------|-------|
79
+ | **Local by Flywheel** | `http://site-name.local` | `WP_PATH=/path/to/site/app/public` | Site ID auto-detected, fully supported |
80
+ | **WordPress Studio** | `http://localhost:{port}` | `WP_CLI_CMD="studio wp --path=/path/to/site"` | Port auto-assigned (8881, 8882, ...). Uses SQLite, not MySQL |
81
+ | **DDEV** | `https://site-name.ddev.site` | `WP_CLI_CMD="ddev wp"` plus `WP_PATH=/path/to/project` | Wrapper runs from `WP_PATH` so DDEV can resolve the site |
82
+ | **wp-env** | `http://localhost:8888` | `WP_CLI_CMD="npx wp-env run cli wp"` plus `WP_PATH=/path/to/project` | Wrapper runs from `WP_PATH`; requires `WP_ENVIRONMENT_TYPE=local` (see below) |
83
+ | **DevKinsta** | `https://site-name.local` | `WP_CLI_CMD="docker exec -u www-data devkinsta_fpm wp --path=/www/kinsta/public/sitename"` | HTTPS with self-signed certs |
84
+ | **Custom / Remote** | Your site URL | `WP_PATH=/path/to/site` or `WP_CLI_CMD="..."` | Works with any WP host |
85
+
86
+ > **Application Passwords on HTTP:** WordPress requires HTTPS for Application Passwords unless `WP_ENVIRONMENT_TYPE` is set to `'local'`. HTTPS environments (DDEV, DevKinsta) work out of the box. HTTP environments (wp-env, WordPress Studio) need this in `wp-config.php`:
87
+ > ```php
88
+ > define('WP_ENVIRONMENT_TYPE', 'local');
89
+ > ```
90
+ > Local by Flywheel sets this automatically.
91
+
92
+ > **WP-CLI note:** `WP_PATH` keeps the existing Local by Flywheel behavior by running `wp` directly on the host filesystem. For Docker-based environments (DDEV, wp-env, DevKinsta, WordPress Studio), set `WP_CLI_CMD` to the wrapper command instead. When `WP_CLI_CMD` is set, the server executes the wrapper from `WP_PATH` if provided, otherwise from its current working directory. The MCP server still validates the requested WP-CLI subcommand against its allowlist before executing either path.
93
+
94
+ ## Available Tools (43)
95
+
96
+ ### Read (24)
97
+ | Tool | Description |
98
+ |------|-------------|
99
+ | `diviops_test_connection` | Test WordPress connection and Divi version |
100
+ | `diviops_server_info` | DiviOps server identity, version, license type, capabilities |
101
+ | `diviops_list_pages` | List pages/posts with Divi status |
102
+ | `diviops_get_page` | Get page details and raw content |
103
+ | `diviops_get_page_layout` | Get parsed block tree (layout structure) |
104
+ | `diviops_get_section` | Get a single section's markup by admin label |
105
+ | `diviops_list_modules` | List all available Divi modules |
106
+ | `diviops_get_module_schema` | Get attribute schema for a module (optimized by default, `raw: true` for full) |
107
+ | `diviops_get_settings` | Get Divi site settings and theme options |
108
+ | `diviops_get_global_colors` | Get global color palette |
109
+ | `diviops_get_global_fonts` | Get global font definitions |
110
+ | `diviops_find_icon` | Search 1,989 icons by keyword (FA + Divi) |
111
+ | `diviops_list_templates` | List available MCP prompt templates |
112
+ | `diviops_get_template` | Get a specific template's block markup |
113
+ | `diviops_preset_audit` | Audit presets with referenced/unreferenced analysis |
114
+ | `diviops_list_library` | List saved Divi Library items |
115
+ | `diviops_get_library_item` | Get a library item's block markup |
116
+ | `diviops_render_preview` | Render block markup to HTML for preview |
117
+ | `diviops_validate_blocks` | Validate block markup (structure, required attrs, known pitfalls) |
118
+ | `diviops_list_tb_templates` | List Theme Builder templates with conditions and layout IDs |
119
+ | `diviops_get_tb_layout` | Get a Theme Builder layout's block markup (header/body/footer) |
120
+ | `diviops_list_variables` | List design token variables (filter by type or prefix) |
121
+ | `diviops_list_canvases` | List all canvas pages |
122
+ | `diviops_get_canvas` | Get canvas content |
123
+
124
+ ### Write (17)
125
+ | Tool | Description |
126
+ |------|-------------|
127
+ | `diviops_create_page` | Create a new page with optional Divi content |
128
+ | `diviops_update_page_content` | Full page content rewrite |
129
+ | `diviops_append_section` | Append a section to existing page (start or end) |
130
+ | `diviops_replace_section` | Replace a section by admin label |
131
+ | `diviops_remove_section` | Remove a section by admin label |
132
+ | `diviops_update_module` | Update specific module attributes by label or text match |
133
+ | `diviops_move_module` | Move a block before/after another block (reorder modules, sections) |
134
+ | `diviops_preset_cleanup` | Remove spam/duplicate presets, bulk rename |
135
+ | `diviops_preset_update` | Update a specific preset (name, attrs) |
136
+ | `diviops_preset_delete` | Delete a preset by ID |
137
+ | `diviops_save_to_library` | Save block markup to Divi Library |
138
+ | `diviops_update_tb_layout` | Update a Theme Builder layout's block markup |
139
+ | `diviops_create_tb_template` | Create Theme Builder template with header/footer and conditions |
140
+ | `diviops_create_variable` | Create a design token variable |
141
+ | `diviops_delete_variable` | Delete a variable by ID |
142
+ | `diviops_create_canvas` | Create a canvas page |
143
+ | `diviops_update_canvas` | Update canvas content |
144
+ | `diviops_delete_canvas` | Delete a canvas page |
145
+
146
+ ### Utility (1)
147
+ | Tool | Description |
148
+ |------|-------------|
149
+ | `diviops_wp_cli` | Run WP-CLI commands (allowlisted, requires `WP_PATH` or `WP_CLI_CMD`) |
150
+
151
+ ## WP-CLI Security
152
+
153
+ The `diviops_wp_cli` tool validates every command against a safety allowlist before execution. Commands not on the list are rejected.
154
+
155
+ ### Default allowlist (always available)
156
+
157
+ Read-only commands plus non-destructive writes needed for core MCP functionality:
158
+
159
+ | Category | Commands |
160
+ |----------|----------|
161
+ | Options | `option get`, `option list` |
162
+ | Posts | `post list`, `post get`, `post create`, `post update` |
163
+ | Post meta | `post meta get`, `post meta list`, `post meta set`, `post meta update` |
164
+ | Users | `user list` |
165
+ | Cache | `cache flush`, `transient delete`, `rewrite flush` |
166
+ | Info | `cron event list`, `plugin list`, `theme list`, `menu list`, `term list`, `term create`, `site url` |
167
+
168
+ ### Extended commands (opt-in)
169
+
170
+ These commands carry higher risk and require explicit opt-in via the `DIVIOPS_WP_CLI_ALLOW` environment variable:
171
+
172
+ | Command | Risk | Why opt-in |
173
+ |---------|------|------------|
174
+ | `option update` | High | Can change site URL, admin email, or security settings |
175
+ | `post delete` | Medium | Permanently removes content |
176
+ | `post meta delete` | Medium | Removes metadata |
177
+ | `plugin activate` | Medium | Can enable untrusted plugins |
178
+ | `plugin deactivate` | Medium | Can disable security plugins |
179
+ | `eval-file` | Critical | Executes arbitrary PHP from a file path |
180
+
181
+ To enable extended commands, add `DIVIOPS_WP_CLI_ALLOW` to your MCP registration:
182
+
183
+ ```bash
184
+ claude mcp add diviops-mcp -- env \
185
+ WP_URL=http://your-site.local \
186
+ WP_USER=admin \
187
+ WP_APP_PASSWORD=xxxx \
188
+ WP_PATH="/path/to/wordpress" \
189
+ DIVIOPS_WP_CLI_ALLOW="option update,post delete" \
190
+ npx @diviops/mcp-server
191
+ ```
192
+
193
+ Only list the specific commands you need. Unknown entries are ignored with a warning.
194
+
195
+ ## Example Usage
196
+
197
+ After setup, Claude can:
198
+
199
+ - "List all my Divi pages"
200
+ - "Show me the layout structure of page 42"
201
+ - "Create a new landing page with a hero section, 3-column features, and a CTA"
202
+ - "Save the hero section from page 312 to the Divi Library"
203
+ - "Validate this block markup before saving"
204
+
205
+ ## Troubleshooting
206
+
207
+ ### "Missing required environment variable(s)"
208
+ Ensure `WP_URL`, `WP_USER`, and `WP_APP_PASSWORD` are all set. Check your `claude mcp add` command.
209
+
210
+ ### "Connection failed" error
211
+ - Verify the WP plugin is active: visit `{WP_URL}/wp-json/diviops/v1/settings` in your browser
212
+ - Check Application Password is correct (try with curl first)
213
+
214
+ ### "Version mismatch" error
215
+ The MCP server and WP plugin versions are incompatible. Update whichever side is older.
216
+
217
+ ### "Permission denied" errors
218
+ - The WP user must have `edit_posts` capability (Editor or Admin role)
219
+ - Write operations (presets, library, theme builder) require `manage_options` (Admin role)
220
+
221
+ ### Testing manually
222
+ ```bash
223
+ curl -u "username:apppassword" http://site.local/wp-json/diviops/v1/settings
224
+ ```
225
+
226
+ ## License
227
+
228
+ MIT
@@ -0,0 +1,26 @@
1
+ /**
2
+ * Version compatibility between MCP server and WP plugin.
3
+ */
4
+ /** Minimum WP plugin version this server requires. */
5
+ export declare const MIN_PLUGIN_VERSION = "1.0.0-beta.22";
6
+ /**
7
+ * Compare two semver-like version strings (supports pre-release tags).
8
+ *
9
+ * Returns:
10
+ * -1 if a < b
11
+ * 0 if a === b
12
+ * 1 if a > b
13
+ *
14
+ * Pre-release versions (e.g. 1.0.0-beta.22) sort before their release (1.0.0).
15
+ */
16
+ export declare function compareVersions(a: string, b: string): -1 | 0 | 1;
17
+ export interface HandshakeResult {
18
+ compatible: boolean;
19
+ plugin_version: string;
20
+ min_server: string;
21
+ divi: {
22
+ active: boolean;
23
+ version: string | null;
24
+ };
25
+ capabilities: string[];
26
+ }
@@ -0,0 +1,68 @@
1
+ /**
2
+ * Version compatibility between MCP server and WP plugin.
3
+ */
4
+ /** Minimum WP plugin version this server requires. */
5
+ export const MIN_PLUGIN_VERSION = '1.0.0-beta.22';
6
+ /**
7
+ * Compare two semver-like version strings (supports pre-release tags).
8
+ *
9
+ * Returns:
10
+ * -1 if a < b
11
+ * 0 if a === b
12
+ * 1 if a > b
13
+ *
14
+ * Pre-release versions (e.g. 1.0.0-beta.22) sort before their release (1.0.0).
15
+ */
16
+ export function compareVersions(a, b) {
17
+ const parseVersion = (v) => {
18
+ const [core, pre] = v.split('-', 2);
19
+ const parts = core.split('.').map(Number);
20
+ return { parts, pre: pre ?? null };
21
+ };
22
+ const va = parseVersion(a);
23
+ const vb = parseVersion(b);
24
+ // Compare numeric parts.
25
+ const maxLen = Math.max(va.parts.length, vb.parts.length);
26
+ for (let i = 0; i < maxLen; i++) {
27
+ const na = va.parts[i] ?? 0;
28
+ const nb = vb.parts[i] ?? 0;
29
+ if (na < nb)
30
+ return -1;
31
+ if (na > nb)
32
+ return 1;
33
+ }
34
+ // Equal numeric parts — pre-release sorts before release.
35
+ if (va.pre === null && vb.pre === null)
36
+ return 0;
37
+ if (va.pre !== null && vb.pre === null)
38
+ return -1;
39
+ if (va.pre === null && vb.pre !== null)
40
+ return 1;
41
+ // Both have pre-release — compare lexicographically with numeric awareness.
42
+ const aParts = va.pre.split('.');
43
+ const bParts = vb.pre.split('.');
44
+ const preLen = Math.max(aParts.length, bParts.length);
45
+ for (let i = 0; i < preLen; i++) {
46
+ const ap = aParts[i];
47
+ const bp = bParts[i];
48
+ if (ap === undefined)
49
+ return -1;
50
+ if (bp === undefined)
51
+ return 1;
52
+ const an = Number(ap);
53
+ const bn = Number(bp);
54
+ if (!isNaN(an) && !isNaN(bn)) {
55
+ if (an < bn)
56
+ return -1;
57
+ if (an > bn)
58
+ return 1;
59
+ }
60
+ else {
61
+ if (ap < bp)
62
+ return -1;
63
+ if (ap > bp)
64
+ return 1;
65
+ }
66
+ }
67
+ return 0;
68
+ }
@@ -0,0 +1,11 @@
1
+ #!/usr/bin/env node
2
+ /**
3
+ * Divi 5 MCP Server
4
+ *
5
+ * Exposes Divi Visual Builder operations as MCP tools for Claude.
6
+ * Requires the companion WordPress plugin "diviops-agent" to be active.
7
+ *
8
+ * Auth: WordPress Application Passwords (Basic Auth).
9
+ * Config: Environment variables WP_URL, WP_USER, WP_APP_PASSWORD.
10
+ */
11
+ export {};