@diviops/mcp-server 1.4.0 → 1.5.2
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 +128 -299
- package/dist/envelope.d.ts +117 -0
- package/dist/envelope.js +171 -0
- package/dist/index.js +846 -428
- package/dist/wp-cli.d.ts +8 -0
- package/dist/wp-cli.js +81 -20
- package/dist/wp-client.d.ts +38 -0
- package/dist/wp-client.js +147 -2
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -1,35 +1,40 @@
|
|
|
1
|
-
#
|
|
1
|
+
# DiviOps MCP Server
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
**AI-driven WordPress site authoring — Divi-native, with the rest of WP in scope.**
|
|
4
|
+
|
|
5
|
+
Programmatic site authoring through Claude Code, Claude Desktop, and any other MCP client. Built on Divi 5 as the page authoring foundation, the suite also handles WordPress data models — custom post types, Secure Custom Fields, taxonomies, site audits, and hybrid sites where Divi authors the marketing pages and custom PHP templates handle the dynamic ones, with design tokens harmonized across both surfaces. Pairs with a WordPress companion plugin and ships alongside a Claude skill that knows the Divi 5 block format.
|
|
4
6
|
|
|
5
7
|
```
|
|
6
|
-
Claude Code <-> MCP Server (stdio) <-> WordPress REST API <->
|
|
8
|
+
Claude Code <-> MCP Server (stdio) <-> WordPress REST API <-> DiviOps Agent plugin
|
|
7
9
|
```
|
|
8
10
|
|
|
9
|
-
##
|
|
11
|
+
## Use cases
|
|
12
|
+
|
|
13
|
+
DiviOps fits multiple WordPress workflows where AI-driven authoring + management is the value:
|
|
14
|
+
|
|
15
|
+
- **Page building (Divi authoring)** — create + edit Divi pages, sections, modules, canvases via prompt; preset-driven design system reuse; Theme Builder layouts and templates.
|
|
16
|
+
- **SCF setup + management** — provision Secure Custom Fields field groups, sync schemas, export/import field group definitions; SCF data model becomes a tool surface, not an admin-UI flow.
|
|
17
|
+
- **CPT + post population** — register custom post types via wp-cli passthrough; bulk-populate posts and pages across any post type, not just Divi-built ones.
|
|
18
|
+
- **Data model reasoning** — schema introspection across Divi modules + SCF field groups + post meta; ask Claude what fields a post type carries, what attributes a module accepts, what tokens are defined.
|
|
19
|
+
- **WordPress site auditing** — preset audits, design-token usage scans, orphan detection (presets, variables, dangling references); broader site surveys via wp-cli (`wp option list`, `wp post list --format=json`, `wp user list`).
|
|
20
|
+
- **Hybrid sites (Divi + custom PHP)** — Divi authors the marketing pages; custom PHP templates handle dynamic ones (CPT listings, single-post views, member portals); design tokens harmonized across both surfaces via CSS custom properties driven from the Divi variable system.
|
|
10
21
|
|
|
11
|
-
|
|
12
|
-
- **PHP** >= 7.4
|
|
13
|
-
- **WordPress** >= 6.5
|
|
14
|
-
- **Divi 5** theme active
|
|
15
|
-
- **DiviOps Agent** WordPress plugin installed and active
|
|
22
|
+
## Quick start
|
|
16
23
|
|
|
17
|
-
|
|
24
|
+
Three steps to your first tool call.
|
|
18
25
|
|
|
19
|
-
### 1. Install the WordPress
|
|
26
|
+
### 1. Install the WordPress plugin
|
|
20
27
|
|
|
21
|
-
Download and activate the **DiviOps Agent** plugin — [direct zip](https://github.com/oaris-dev/diviops/raw/main/diviops-agent.zip) or browse the [public distribution repo](https://github.com/oaris-dev/diviops).
|
|
28
|
+
Download and activate the **DiviOps Agent** plugin — [direct zip](https://github.com/oaris-dev/diviops/raw/main/diviops-agent.zip) or browse the [public distribution repo](https://github.com/oaris-dev/diviops). Requires Divi 5.1+ on WordPress 6.5+.
|
|
22
29
|
|
|
23
30
|
### 2. Create an Application Password
|
|
24
31
|
|
|
25
|
-
|
|
26
|
-
- Enter a name (e.g
|
|
32
|
+
In **WP Admin → Users → Your Profile → Application Passwords**:
|
|
33
|
+
- Enter a name (e.g. "MCP Server")
|
|
27
34
|
- Click "Add New Application Password"
|
|
28
|
-
- Copy the generated password
|
|
35
|
+
- Copy the generated password and strip the spaces
|
|
29
36
|
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
### 3. Configure Claude Code
|
|
37
|
+
### 3. Register the MCP server with Claude
|
|
33
38
|
|
|
34
39
|
```bash
|
|
35
40
|
claude mcp add diviops-mcp \
|
|
@@ -39,317 +44,141 @@ claude mcp add diviops-mcp \
|
|
|
39
44
|
-- npx @diviops/mcp-server
|
|
40
45
|
```
|
|
41
46
|
|
|
42
|
-
|
|
47
|
+
Then ask Claude: **"List the pages on my site."** Claude calls `diviops_page_list` and renders the result. You're authoring with the suite.
|
|
43
48
|
|
|
44
|
-
|
|
45
|
-
```bash
|
|
46
|
-
claude mcp add diviops-mcp \
|
|
47
|
-
--env WP_URL=http://your-site.local \
|
|
48
|
-
--env WP_USER=your-wp-username \
|
|
49
|
-
--env WP_APP_PASSWORD=xxxxXXXXxxxxXXXXxxxxXXXX \
|
|
50
|
-
--env "WP_PATH=/path/to/wordpress" \
|
|
51
|
-
-- npx @diviops/mcp-server
|
|
52
|
-
```
|
|
49
|
+
For a deeper walkthrough (containerized environments, WP-CLI configuration, troubleshooting installation), see [setup-guide.md](../docs/setup-guide.md).
|
|
53
50
|
|
|
54
|
-
|
|
55
|
-
```bash
|
|
56
|
-
claude mcp add diviops-mcp \
|
|
57
|
-
--env WP_URL=https://site-name.ddev.site \
|
|
58
|
-
--env WP_USER=your-wp-username \
|
|
59
|
-
--env WP_APP_PASSWORD=xxxxXXXXxxxxXXXXxxxxXXXX \
|
|
60
|
-
--env "WP_CLI_CMD=ddev wp" \
|
|
61
|
-
-- npx @diviops/mcp-server
|
|
62
|
-
```
|
|
51
|
+
## Example workflow
|
|
63
52
|
|
|
64
|
-
|
|
53
|
+
> **You:** Create a hero section on a new page called "Spring Launch" with a heading, subheading, and a CTA button. Use my brand colors.
|
|
65
54
|
|
|
66
|
-
|
|
67
|
-
|----------|----------|-------------|
|
|
68
|
-
| `WP_URL` | Yes | WordPress site URL (e.g. `http://mysite.local`) |
|
|
69
|
-
| `WP_USER` | Yes | WordPress username with Editor or Admin role |
|
|
70
|
-
| `WP_APP_PASSWORD` | Yes | Application Password (spaces stripped) |
|
|
71
|
-
| `WP_PATH` | No | WordPress filesystem path for Local by Flywheel, or wrapper working directory when `WP_CLI_CMD` needs project context |
|
|
72
|
-
| `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` |
|
|
73
|
-
| `LOCAL_SITE_ID` | No | Override auto-detection of Local by Flywheel site ID |
|
|
74
|
-
| `DIVIOPS_WP_CLI_ALLOW` | No | Comma-separated list of extended WP-CLI commands to enable (see [WP-CLI Security](#wp-cli-security)) |
|
|
75
|
-
| `DIVIOPS_WP_CLI_SAFE_FS_ROOT` | No | Absolute path to constrain filesystem-touching commands (`wp export`, `acf export/import`). Defaults to `<WP_PATH>/.diviops-tmp/` in host mode. **Required** in `WP_CLI_CMD` wrapper mode (must be the container-namespace path, since the host path can't be inferred) |
|
|
76
|
-
| `DIVIOPS_WP_CLI_UNSAFE_FS` | No | Set to `1` to disable filesystem flag validation entirely. For trusted single-user local-dev setups where the `--dir` / positional-path safety checks get in the way |
|
|
77
|
-
|
|
78
|
-
### Local Development Environments
|
|
79
|
-
|
|
80
|
-
The server connects via standard WordPress REST API and works with any environment that exposes WordPress over HTTP with Application Password support.
|
|
81
|
-
|
|
82
|
-
| Environment | WP_URL | WP-CLI setup | Notes |
|
|
83
|
-
|-------------|--------|--------------|-------|
|
|
84
|
-
| **Local by Flywheel** | `http://site-name.local` | `WP_PATH=/path/to/site/app/public` | Site ID auto-detected, fully supported |
|
|
85
|
-
| **WordPress Studio** | `http://localhost:{port}` | `WP_CLI_CMD="studio wp --path=/path/to/site"` | Port auto-assigned (8881, 8882, ...). Uses SQLite, not MySQL |
|
|
86
|
-
| **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 |
|
|
87
|
-
| **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) |
|
|
88
|
-
| **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 |
|
|
89
|
-
| **Custom / Remote** | Your site URL | `WP_PATH=/path/to/site` or `WP_CLI_CMD="..."` | Works with any WP host |
|
|
90
|
-
|
|
91
|
-
> **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`:
|
|
92
|
-
> ```php
|
|
93
|
-
> define('WP_ENVIRONMENT_TYPE', 'local');
|
|
94
|
-
> ```
|
|
95
|
-
> Local by Flywheel sets this automatically.
|
|
96
|
-
|
|
97
|
-
> **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.
|
|
98
|
-
|
|
99
|
-
## Available Tools (66)
|
|
100
|
-
|
|
101
|
-
### Read (30)
|
|
102
|
-
| Tool | Description |
|
|
103
|
-
|------|-------------|
|
|
104
|
-
| `diviops_meta_ping` | Test WordPress connection and Divi version |
|
|
105
|
-
| `diviops_meta_info` | DiviOps server identity, version, license type, capabilities |
|
|
106
|
-
| `diviops_page_list` | List pages/posts with Divi status |
|
|
107
|
-
| `diviops_page_get` | Get page details and raw content |
|
|
108
|
-
| `diviops_page_get_layout` | Get parsed block tree (layout structure) |
|
|
109
|
-
| `diviops_section_get` | Get a single section's markup by admin label |
|
|
110
|
-
| `diviops_schema_list_modules` | List all available Divi modules |
|
|
111
|
-
| `diviops_schema_get_module` | Get attribute schema for a module. Default `mode: "single"` returns one module's schema (optimized; `raw: true` for full). `mode: "dump_all"` snapshots every Divi module in one call along with a `schema_version` hash over `*PresetAttrsMap.php` and a `divi_version` field — build-time entry point for the skill regen pipeline. |
|
|
112
|
-
| `diviops_schema_get_settings` | Get Divi site settings and theme options |
|
|
113
|
-
| `diviops_global_color_list` | Get global color palette |
|
|
114
|
-
| `diviops_global_font_list` | Get global font definitions |
|
|
115
|
-
| `diviops_meta_find_icon` | Search 1,989 icons by keyword (FA + Divi) |
|
|
116
|
-
| `diviops_template_list` | List available MCP prompt templates |
|
|
117
|
-
| `diviops_template_get` | Get a specific template's block markup |
|
|
118
|
-
| `diviops_preset_audit` | Audit presets with referenced/unreferenced analysis. Walks both page content and in-registry `groupPresets` chains; exposes `block_ref_count`, `group_ref_count`, `referenced_by_presets`. Also reports `orphan_default_pointers` — per-bucket `default` pointers referencing UUIDs missing from `items[]` (legacy damage from past unsafe deletes; clear via `diviops_preset_set_default` in bucket-addressed mode: `type` + `module` + `unset=true`) |
|
|
119
|
-
| `diviops_preset_scan_orphans` | List page-referenced preset UUIDs missing from the D5 registry (separates dangling orphans from D4-legacy refs) |
|
|
120
|
-
| `diviops_library_list` | List saved Divi Library items |
|
|
121
|
-
| `diviops_library_get` | Get a library item's block markup |
|
|
122
|
-
| `diviops_render_preview` | Render block markup to HTML for preview |
|
|
123
|
-
| `diviops_validate_blocks` | Validate block markup (structure, required attrs, known pitfalls) |
|
|
124
|
-
| `diviops_tb_template_list` | List Theme Builder templates with conditions and layout IDs |
|
|
125
|
-
| `diviops_tb_layout_get` | Get a Theme Builder layout's block markup (header/body/footer) |
|
|
126
|
-
| `diviops_variable_list` | List design token variables (filter by type or prefix) |
|
|
127
|
-
| `diviops_variable_scan_orphans` | Find `gvid-`/`gcid-` refs with no backing Variable Manager entry (orphans render as invalid CSS) + unused variables (defined, never referenced). Scans pages, Theme Builder layouts (header/body/footer), Divi Library items, canvas pages, and the preset registry |
|
|
128
|
-
| `diviops_variable_used_on_page` | Detect which `gvid-` (numeric/font) IDs a single page emits — the exact set Divi 5.4.0+ uses to scope selective `:root{--gvid-*}` CSS variable emission. Walks the same content stack the frontend assembles (post_content + active TB header/body/footer + appended canvases + presets). `gcid-` colors are out of scope (separate emission path). Use for per-page orphan validation, preflight before bulk variable rename, or to debug why a numeric/font variable doesn't render on a specific page. Read-only |
|
|
129
|
-
| `diviops_canvas_list` | List all canvas pages |
|
|
130
|
-
| `diviops_canvas_get` | Get canvas content |
|
|
131
|
-
| `diviops_scf_status` | Show SCF (Secure Custom Fields) sync status — pending JSON-vs-DB drift across field groups, post types, taxonomies, options pages. Wraps `wp scf json status` |
|
|
132
|
-
| `diviops_scf_field_group_list` | List all SCF/ACF field groups (post_name = ACF key, post_title, post_status, post_modified). Queries the `acf-field-group` post type via `wp post list` (works on SCF 6.8.4+ and older ACF) |
|
|
133
|
-
| `diviops_scf_field_group_get` | Fetch a single SCF/ACF field-group post by ACF key (`group_abc123` → post_name) or numeric WP post ID. For the parsed/structured field tree, use `diviops_scf_export --field-groups=<key> --stdout` |
|
|
134
|
-
|
|
135
|
-
### Write (34)
|
|
136
|
-
| Tool | Description |
|
|
137
|
-
|------|-------------|
|
|
138
|
-
| `diviops_page_create` | Create a new page with optional Divi content |
|
|
139
|
-
| `diviops_page_update_content` | Full page content rewrite |
|
|
140
|
-
| `diviops_page_trash` | Trash (default) or permanently delete (`force=true`) a page. Idempotent on already-trashed posts. Supports `dry_run` |
|
|
141
|
-
| `diviops_page_update_status` | Update post_status (publish/draft/private/pending/future). `future` requires `date_gmt` (ISO 8601 UTC); `publish` clears stale future dates so re-publishing takes effect immediately. Supports `dry_run` |
|
|
142
|
-
| `diviops_section_append` | Append a section to existing page (start or end) |
|
|
143
|
-
| `diviops_section_replace` | Replace a section by admin label |
|
|
144
|
-
| `diviops_section_remove` | Remove a section by admin label |
|
|
145
|
-
| `diviops_module_update` | Update specific module attributes by label or text match |
|
|
146
|
-
| `diviops_module_move` | Move a block before/after another block (reorder modules, sections) |
|
|
147
|
-
| `diviops_module_lock` | Lock a module so VB users cannot edit it (frontend renders normally) |
|
|
148
|
-
| `diviops_module_unlock` | Unlock a module by removing `attrs.locked` (matches VB's absence convention) |
|
|
149
|
-
| `diviops_module_clone` | Deep-copy a module + insert next to source within the same parent |
|
|
150
|
-
| `diviops_global_color_create` | Add a new global color to Divi's palette (writes canonical shape; closes ET's bundle Zod gap that drops `label`) |
|
|
151
|
-
| `diviops_global_color_update` | Update an existing global color by gcid (only provided fields change) |
|
|
152
|
-
| `diviops_global_color_delete` | Delete a global color (refuses if `usedInPosts` non-empty unless `force=true`; customizer-bound defaults always protected) |
|
|
153
|
-
| `diviops_preset_cleanup` | Remove spam/duplicate presets, bulk rename |
|
|
154
|
-
| `diviops_preset_create` | Write a new preset to the D5 registry (module or group type, supports `divi/column` etc.). Optional `make_default: true` sets it as the bucket's default; optional `priority` controls stack-merge order |
|
|
155
|
-
| `diviops_preset_reassign` | Rewrite `modulePreset` references across pages (dry-run by default; optional `strip_inline` removes redundant inline attrs) |
|
|
156
|
-
| `diviops_preset_update` | Update a specific preset (name, attrs, priority) |
|
|
157
|
-
| `diviops_preset_delete` | Delete a preset by ID. Refuses with HTTP 409 `preset_is_default` when the target is the registered default for its bucket — clear the pointer first via `diviops_preset_set_default` with `unset=true`, or pass `force=true` to delete and clear the pointer in one write |
|
|
158
|
-
| `diviops_preset_set_default` | Set or clear the per-module/group default preset. Two modes: by `preset_id` (UUID-addressed; auto-resolves bucket) or by `type` + `module` + `unset=true` (bucket-addressed clear, used to repair orphan default pointers when the UUID is gone from `items[]`). Defaults apply to NEW instances only — use `diviops_preset_reassign` for retroactive swaps |
|
|
159
|
-
| `diviops_library_save` | Save block markup to Divi Library |
|
|
160
|
-
| `diviops_tb_layout_update` | Update a Theme Builder layout's block markup |
|
|
161
|
-
| `diviops_tb_template_create` | Create Theme Builder template with header/footer and conditions |
|
|
162
|
-
| `diviops_variable_create` | Create a design token variable. For `type=numbers` fluid tokens, pass `min`+`max` shorthand (anchors default to 320px/1920px) or explicit `targets` like `{"320px":"20px","1920px":"60px"}` — server generates arithmetically-correct `clamp()` instead of hand-written math that silently under-reaches the stated max. All-px inputs emit px (root-agnostic). Rem inputs OR rem output require explicit opt-in: pass `output_unit="rem"` (accepts the 1rem=16px default) or `root_font_size_px:N` (declares your site's actual root font-size, e.g. `10` for `html { font-size: 62.5% }`, `20` for `html { font-size: 20px }`) |
|
|
163
|
-
| `diviops_variable_create_fluid_system` | Batch-emit a fluid typography + spacing + radius variable set in one call. Mirrors Divi 5.4.0's Variable Generator Modal at the algorithm level (clamp() math is identical to `diviops_variable_create`'s fluid mode) but layers profile-selectable anchors over it: `divi-default` (360→1350) matches ET's defaults, `wide` (320→1920) matches the diviops convention, `custom` takes explicit anchors. Each category is independent and optional. Typography uses modular-scale chains (named ratios `major-third`/`perfect-fifth`/`golden`/etc., or raw numbers) — h1 = largest, hN = base. Spacing/radius support `linear` or `geometric` step distributions. `dry_run: true` returns the full plan without persisting; `overwrite: false` (default) skips existing IDs. Single atomic write to the registry — mid-batch failures roll back cleanly |
|
|
164
|
-
| `diviops_variable_delete` | Delete a variable by ID. Returns HTTP 409 when live references exist unless `force=true` (use `diviops_variable_scan_orphans` to find reference locations). Returns HTTP 403 for Divi's customizer-bound defaults (`gcid-primary-color`, `gcid-secondary-color`, `gcid-heading-color`, `gcid-body-color`, `gcid-link-color` — managed via WP Customizer) |
|
|
165
|
-
| `diviops_canvas_create` | Create a canvas page |
|
|
166
|
-
| `diviops_canvas_duplicate` | Deep-copy a canvas (content + canvas-specific meta). Default copy title `<source> (Copy)` auto-suffixes on collision (Copy 2, …); explicit `title` collisions return 409. Supports `dry_run` |
|
|
167
|
-
| `diviops_canvas_update` | Update canvas content and/or metadata. Pass any subset of fields — `{canvas_post_id, title}` renames without touching content |
|
|
168
|
-
| `diviops_canvas_delete` | Delete a canvas page |
|
|
169
|
-
| `diviops_scf_export` | Export SCF schema (field groups, post types, taxonomies, options pages) as JSON to a directory under the safe-root, or to stdout. Wraps `wp scf json export` |
|
|
170
|
-
| `diviops_scf_import` | Import SCF schema from a JSON file (mutates DB; idempotent — existing items are updated). Wraps `wp scf json import <file>` |
|
|
171
|
-
| `diviops_scf_sync` | Apply pending JSON-on-disk SCF changes to the DB. Defaults to `dry_run: true` for safety. Wraps `wp scf json sync` |
|
|
172
|
-
|
|
173
|
-
### Utility (2)
|
|
174
|
-
| Tool | Description |
|
|
175
|
-
|------|-------------|
|
|
176
|
-
| `diviops_meta_wp_cli` | Run WP-CLI commands (allowlisted, requires `WP_PATH` or `WP_CLI_CMD`) |
|
|
177
|
-
| `diviops_meta_flush_cache` | Flush Divi's compiled CSS cache under `wp-content/et-cache/`. `wp cache flush` does NOT touch these files — the frontend can keep serving stale CSS after mutations. Delegates to Divi's native clearer (`ET_Core_PageResource::remove_static_resources`) when available — also clears Theme Builder / archive / taxonomy / home / notfound CSS, object cache, module features cache, post features cache, dynamic assets cache, Google Fonts cache, post meta caches. Falls back to a filesystem walk of numeric-named subdirs when Divi is inactive. Response includes `backend: "divi_native"` or `"fs_fallback"`. Exactly one selector required: `post_id`, `all`, or `after` (unix ts) — no default to `all` |
|
|
178
|
-
|
|
179
|
-
## WP-CLI Security
|
|
180
|
-
|
|
181
|
-
The `diviops_meta_wp_cli` tool validates every command against a safety allowlist before execution. Commands not on the list are rejected.
|
|
182
|
-
|
|
183
|
-
### Default allowlist (always available)
|
|
184
|
-
|
|
185
|
-
Read-only commands plus non-destructive writes needed for core MCP functionality and local development workflows:
|
|
186
|
-
|
|
187
|
-
| Category | Commands |
|
|
188
|
-
|----------|----------|
|
|
189
|
-
| Options | `option get`, `option list` |
|
|
190
|
-
| Posts | `post list`, `post get`, `post create`, `post update` |
|
|
191
|
-
| Post meta | `post meta get`, `post meta list`, `post meta set`, `post meta update` |
|
|
192
|
-
| Post types | `post-type list`, `post-type get` |
|
|
193
|
-
| Taxonomies | `taxonomy list`, `term list`, `term create`, `term update` |
|
|
194
|
-
| ACF / SCF | `acf export`, `acf import`, `acf field-group list`, `acf field-group get`, `scf json {status,sync,import,export}` (also aliased as `acf json …` per SCF 6.8.4+) |
|
|
195
|
-
| Users | `user list` |
|
|
196
|
-
| Cache | `cache flush`, `transient delete`, `rewrite flush` |
|
|
197
|
-
| Export | `export` (WXR data export to file) |
|
|
198
|
-
| Info | `cron event list`, `plugin list`, `theme list`, `menu list`, `site url` |
|
|
199
|
-
| Core (read-only) | `core version`, `core check-update`, `core is-installed`, `core verify-checksums`, `core language list` |
|
|
200
|
-
| DB (introspection) | `db columns`, `db size`, `db tables`, `db check`, `db search` |
|
|
201
|
-
|
|
202
|
-
### Extended commands (opt-in)
|
|
203
|
-
|
|
204
|
-
These commands carry higher risk and require explicit opt-in via the `DIVIOPS_WP_CLI_ALLOW` environment variable:
|
|
205
|
-
|
|
206
|
-
| Command | Risk | Why opt-in |
|
|
207
|
-
|---------|------|------------|
|
|
208
|
-
| `option update` | High | Can change site URL, admin email, or security settings |
|
|
209
|
-
| `option delete` | High | Permanently removes a WP option (no undo) |
|
|
210
|
-
| `post delete` | Medium | Permanently removes content |
|
|
211
|
-
| `post meta delete` | Medium | Removes metadata |
|
|
212
|
-
| `term delete` | Medium | Permanently removes taxonomy terms |
|
|
213
|
-
| `search-replace` | High | Bulk database modification — can corrupt content if misused |
|
|
214
|
-
| `import` | Medium | Bulk content ingestion from WXR files |
|
|
215
|
-
| `plugin activate` | Medium | Can enable untrusted plugins |
|
|
216
|
-
| `plugin deactivate` | Medium | Can disable security plugins |
|
|
217
|
-
| `eval-file` | Critical | Executes arbitrary PHP from a file path |
|
|
218
|
-
|
|
219
|
-
To enable extended commands, add `DIVIOPS_WP_CLI_ALLOW` to your MCP registration:
|
|
55
|
+
Claude orchestrates a few tool calls in sequence:
|
|
220
56
|
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
--env "WP_PATH=/path/to/wordpress" \
|
|
227
|
-
--env "DIVIOPS_WP_CLI_ALLOW=option update,post delete,search-replace" \
|
|
228
|
-
-- npx @diviops/mcp-server
|
|
229
|
-
```
|
|
57
|
+
1. `diviops_global_color_list` — discovers your brand palette.
|
|
58
|
+
2. `diviops_template_list` / `diviops_template_get` — pulls a verified hero template that matches the request.
|
|
59
|
+
3. `diviops_page_create` — creates `Spring Launch` as a draft with the hero block markup.
|
|
60
|
+
4. `diviops_validate_blocks` — confirms the markup is well-formed before save.
|
|
61
|
+
5. `diviops_render_preview` — returns the rendered HTML so you can verify before publishing.
|
|
230
62
|
|
|
231
|
-
|
|
63
|
+
The skill enforces the Divi block format, the design system, and the response contract throughout — you stay at the prompt level.
|
|
232
64
|
|
|
233
|
-
|
|
65
|
+
## Tools at a glance
|
|
234
66
|
|
|
235
|
-
|
|
67
|
+
The server exposes **66 tools** across the categories below. Each category links to representative tools; the full table lives in [server-reference.md](../docs/server-reference.md).
|
|
236
68
|
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
69
|
+
| Category | Use case | Tool prefixes |
|
|
70
|
+
|----------|----------|---------------|
|
|
71
|
+
| Page authoring | Create, edit, restructure pages | `page_*`, `section_*`, `module_*` |
|
|
72
|
+
| Design system | Manage colors, fonts, variables, presets | `variable_*`, `global_color_*`, `global_font_*`, `preset_*` |
|
|
73
|
+
| Library + templates | Reusable layouts + Theme Builder | `library_*`, `template_*`, `tb_*` |
|
|
74
|
+
| Schema introspection | Module attribute discovery | `schema_*` |
|
|
75
|
+
| Canvas / off-canvas | Popups, modals, menus | `canvas_*` |
|
|
76
|
+
| SCF integration | Secure Custom Fields sync | `scf_*` |
|
|
77
|
+
| Render + validate | Preview HTML, validate block markup | `render_preview`, `validate_blocks` |
|
|
78
|
+
| WP-CLI passthrough | Escape hatch for site ops | `meta_wp_cli` |
|
|
79
|
+
| Cache + meta | Connection probe, identity, icons, cache flush | `meta_*` |
|
|
240
80
|
|
|
241
|
-
|
|
81
|
+
See [server-reference.md](../docs/server-reference.md) for per-tool descriptions.
|
|
242
82
|
|
|
243
|
-
|
|
83
|
+
## Response contract
|
|
244
84
|
|
|
245
|
-
|
|
85
|
+
Tools return a standardized envelope. The shape lets clients branch on `ok` and machine-readable `error.code` without parsing freeform messages.
|
|
246
86
|
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
- **`--filename_format=` must be a filename template**, not a path — separators (`/`, `\`) are rejected so a crafted template can't escape `--dir`'s scope.
|
|
254
|
-
- **`acf export/import`'s positional path** must resolve under the safe root.
|
|
255
|
-
- **`scf json export`'s `--dir=` flag** must resolve under the safe root (or pass `--stdout` for in-memory transfer). **`scf json import`'s positional `<file>` path** must resolve under the safe root.
|
|
256
|
-
- **Wrapper mode (`WP_CLI_CMD`)**: the host-derived safe root doesn't correspond to the wrapper's filesystem (e.g., container paths like `/www/app`), so `DIVIOPS_WP_CLI_SAFE_FS_ROOT` is **required** and must be set to the container-namespace path. FS-sensitive commands are rejected with a clear error if it's missing.
|
|
257
|
-
- **Escape hatch**: `DIVIOPS_WP_CLI_UNSAFE_FS=1` disables validation entirely. Appropriate for trusted single-user local-dev setups that don't want the guard.
|
|
258
|
-
|
|
259
|
-
**EXTENDED-tier filesystem commands** (`import`, `eval-file`) are not flag-validated here — opt-in via `DIVIOPS_WP_CLI_ALLOW` signals the caller has accepted the path-scope risk. That constraint is a candidate future enhancement if the MCP server ships in multi-tenant contexts.
|
|
260
|
-
|
|
261
|
-
## Safety Patterns
|
|
262
|
-
|
|
263
|
-
High-risk or bulk destructive tools follow one of two conventions to guard against unintended mutation. Both are stateless (no session tokens between calls), but they guard differently: Pattern A is a **stateless gate** — the first call mutates when the safety check passes, refuses with an explanatory error when it fires. Pattern B is **preview-before-commit** — the first call never mutates; an explicit apply step is required. Tools without a gate (e.g., `diviops_page_update_content`) execute their mutation directly — whether to adopt a pattern is a per-tool design decision, not a retrofit requirement.
|
|
264
|
-
|
|
265
|
-
### Pattern A — `force: false/true` (refuse-with-override)
|
|
266
|
-
|
|
267
|
-
Tool refuses the operation with an explanatory error when a safety check fails; caller reviews the reason and retries with `force=true` to commit. Single round-trip on the happy path, single retry on the override path.
|
|
268
|
-
|
|
269
|
-
**Fit criteria:**
|
|
270
|
-
- Operation targets a **single item** (one variable, one preset, one page)
|
|
271
|
-
- Safety check is **binary** (safe / not safe)
|
|
272
|
-
- The reason for blocking is compact enough to fit in the error body (e.g., "3 live references")
|
|
273
|
-
- The caller can decide from the error alone — no full diff needed
|
|
274
|
-
|
|
275
|
-
**Current tools:**
|
|
276
|
-
| Tool | Guard | Override |
|
|
277
|
-
|------|-------|----------|
|
|
278
|
-
| `diviops_variable_delete` | HTTP 409 when live references exist | `force=true` |
|
|
279
|
-
| `diviops_preset_delete` | HTTP 409 `preset_is_default` when target is the registered default for its bucket | `force=true` (deletes + clears the `default` pointer in the same write) |
|
|
280
|
-
|
|
281
|
-
### Pattern B — `mode: "dry-run"/"apply"` (preview-then-commit)
|
|
282
|
-
|
|
283
|
-
Tool returns a preview of the changes it would make; caller reviews the diff, then re-invokes with the apply flag to commit. Two round-trips by design — the preview itself is the value.
|
|
87
|
+
```jsonc
|
|
88
|
+
// Success
|
|
89
|
+
{ "ok": true, "data": <payload> }
|
|
90
|
+
// Failure
|
|
91
|
+
{ "ok": false, "error": { "code": "<code>", "message": "<human>", "hint": "<optional>" } }
|
|
92
|
+
```
|
|
284
93
|
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
94
|
+
### Standard error codes
|
|
95
|
+
|
|
96
|
+
| code | HTTP | meaning |
|
|
97
|
+
|---|---|---|
|
|
98
|
+
| `not_found` | 404 | Target ID does not resolve |
|
|
99
|
+
| `invalid_input` | 400 | Schema violation, malformed args |
|
|
100
|
+
| `validation_failed` | 400 | `validate_blocks`-detected shape error |
|
|
101
|
+
| `conflict` | 409 | Uniqueness collision |
|
|
102
|
+
| `forbidden` | 403 | Row-level WordPress auth signal |
|
|
103
|
+
| `capability_missing` | 412 | Plugin version below required for this tool |
|
|
104
|
+
| `wp_error` | 500 | Underlying WordPress error |
|
|
105
|
+
| `divi_error` | 500 | Divi-specific error (block parser, validator, etc.) |
|
|
106
|
+
|
|
107
|
+
### Namespace-specific codes
|
|
108
|
+
|
|
109
|
+
Namespaces extend the vocabulary using the `<namespace>.<reason>` convention — e.g. `meta_wp_cli.command_failed`, `scf.not_configured`, `preset.bucket_mismatch`, `variable.customizer_default_immutable`. Namespace-prefixed codes carry structured `error.data` documenting the failure (exit codes, conflicting fields, reference counts, etc.). Per-tool descriptions name the codes each tool emits and the `error.data` shape that accompanies them.
|
|
110
|
+
|
|
111
|
+
### `dry_run` plan shape
|
|
112
|
+
|
|
113
|
+
Every write tool accepts `dry_run: boolean` (default `false`). When `true`, the response carries a uniform plan shape and no state is mutated:
|
|
114
|
+
|
|
115
|
+
```json
|
|
116
|
+
{
|
|
117
|
+
"ok": true,
|
|
118
|
+
"data": {
|
|
119
|
+
"dry_run": true,
|
|
120
|
+
"plan": {
|
|
121
|
+
"summary": "Would update 1 attr path(s) on module 'Hero CTA' (page #42, type divi/button).",
|
|
122
|
+
"changes": [
|
|
123
|
+
{ "kind": "module.update", "target": "page#42/divi/button/Hero CTA#button.decoration.font.font.desktop.value.color", "before": "#000", "after": "#ff0066" }
|
|
124
|
+
]
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
```
|
|
290
129
|
|
|
291
|
-
|
|
292
|
-
| Tool | Preview flag | Commit flag |
|
|
293
|
-
|------|--------------|-------------|
|
|
294
|
-
| `diviops_preset_reassign` | `mode: "dry-run"` (default) | `mode: "apply"` |
|
|
295
|
-
| `diviops_preset_cleanup` | `dry_run: true` (default) | `dry_run: false` |
|
|
130
|
+
`meta_wp_cli` and `scf_import` do not accept `dry_run` (raw passthrough / upstream gap respectively). For bulk preview-then-commit flows (preset reassign, preset cleanup), see [safety-patterns.md](../docs/safety-patterns.md).
|
|
296
131
|
|
|
297
|
-
|
|
132
|
+
### `_meta.idempotent` markers
|
|
298
133
|
|
|
299
|
-
|
|
134
|
+
Every tool's `_meta.idempotent` field documents how it behaves under repeat calls with identical inputs. Some tools are silent-success idempotent (e.g. `page_trash` on an already-trashed post returns `ok: true` with `data.already_trashed = true`); others are side-effect-equivalent (re-running produces the same final state via different intermediate effects). See [idempotency-audit.md](../docs/idempotency-audit.md) for the per-tool record.
|
|
300
135
|
|
|
301
|
-
|
|
136
|
+
## Configuration
|
|
302
137
|
|
|
303
|
-
|
|
138
|
+
### Environment variables
|
|
304
139
|
|
|
305
|
-
|
|
140
|
+
| Variable | Required | Description |
|
|
141
|
+
|----------|----------|-------------|
|
|
142
|
+
| `WP_URL` | Yes | WordPress site URL (e.g. `http://mysite.local`) |
|
|
143
|
+
| `WP_USER` | Yes | WordPress username with Editor or Admin role |
|
|
144
|
+
| `WP_APP_PASSWORD` | Yes | Application Password (spaces stripped) |
|
|
145
|
+
| `WP_PATH` | No | WordPress filesystem path for Local by Flywheel, or wrapper working directory when `WP_CLI_CMD` needs project context |
|
|
146
|
+
| `WP_CLI_CMD` | No | Custom WP-CLI command prefix for containerized environments (e.g. `ddev wp`, `npx wp-env run cli wp`) |
|
|
147
|
+
| `LOCAL_SITE_ID` | No | Override auto-detection of Local by Flywheel site ID |
|
|
148
|
+
| `DIVIOPS_WP_CLI_ALLOW` | No | Opt-in extended WP-CLI commands — see [wp-cli-security.md](../docs/wp-cli-security.md) |
|
|
149
|
+
| `DIVIOPS_WP_CLI_SAFE_FS_ROOT` | No | Path to constrain filesystem-touching wp-cli commands. **Required** in `WP_CLI_CMD` wrapper mode |
|
|
150
|
+
| `DIVIOPS_WP_CLI_UNSAFE_FS` | No | Set to `1` to disable filesystem flag validation entirely |
|
|
306
151
|
|
|
307
|
-
|
|
152
|
+
### Containerized environments
|
|
308
153
|
|
|
309
|
-
-
|
|
310
|
-
- "Show me the layout structure of page 42"
|
|
311
|
-
- "Create a new landing page with a hero section, 3-column features, and a CTA"
|
|
312
|
-
- "Save the hero section from page 312 to the Divi Library"
|
|
313
|
-
- "Validate this block markup before saving"
|
|
154
|
+
The server connects via standard WordPress REST API and works with any environment that exposes WordPress over HTTP with Application Password support — Local by Flywheel, DDEV, wp-env, WordPress Studio, DevKinsta, custom hosts. See [setup-guide.md](../docs/setup-guide.md) for environment-specific `WP_CLI_CMD` examples and HTTPS / `WP_ENVIRONMENT_TYPE` notes.
|
|
314
155
|
|
|
315
156
|
## Troubleshooting
|
|
316
157
|
|
|
317
|
-
|
|
318
|
-
Ensure `WP_URL`, `WP_USER`, and `WP_APP_PASSWORD` are all set. Check your `claude mcp add` command.
|
|
158
|
+
Common quick fixes — full reference in [troubleshooting.md](../docs/troubleshooting.md).
|
|
319
159
|
|
|
320
|
-
|
|
321
|
-
-
|
|
322
|
-
-
|
|
160
|
+
- **"Missing required environment variable(s)"** — ensure `WP_URL`, `WP_USER`, `WP_APP_PASSWORD` are all set on `claude mcp add`.
|
|
161
|
+
- **"Connection failed"** — verify the plugin is active by visiting `{WP_URL}/wp-json/diviops/v1/schema/settings`; test the credentials with `curl -u "user:pass" …`.
|
|
162
|
+
- **"This tool requires plugin capability"** — the plugin doesn't advertise the capability this tool needs. Update the plugin to the latest release.
|
|
163
|
+
- **Preset edits not visible on the frontend** — Divi serves frontend CSS from `wp-content/et-cache/{post_id}/`, which `wp cache flush` doesn't touch. Use `diviops_meta_flush_cache` after preset writes.
|
|
323
164
|
|
|
324
|
-
|
|
325
|
-
A specific tool failed at the per-tool capability gate (#486) because the active diviops-agent plugin doesn't advertise that capability. Update the plugin to ≥ 1.2.0 (the version that introduced the capability map). Other tools the older plugin does support keep working — the gate is per-tool, not a global floor.
|
|
165
|
+
## Learn more
|
|
326
166
|
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
-
|
|
332
|
-
-
|
|
333
|
-
|
|
334
|
-
### Testing manually
|
|
335
|
-
```bash
|
|
336
|
-
curl -u "username:apppassword" http://site.local/wp-json/diviops/v1/schema/settings
|
|
337
|
-
```
|
|
167
|
+
- [setup-guide.md](../docs/setup-guide.md) — full onboarding walkthrough (containerized envs, HTTPS, Application Passwords)
|
|
168
|
+
- [server-reference.md](../docs/server-reference.md) — full per-tool reference table
|
|
169
|
+
- [wp-cli-security.md](../docs/wp-cli-security.md) — allowlist, extended commands, FS validation
|
|
170
|
+
- [safety-patterns.md](../docs/safety-patterns.md) — Pattern A (refuse-with-override) + Pattern B (preview-then-commit) + universal `dry_run`
|
|
171
|
+
- [troubleshooting.md](../docs/troubleshooting.md) — common errors and resolutions
|
|
172
|
+
- [idempotency-audit.md](../docs/idempotency-audit.md) — repeat-call semantics per tool
|
|
173
|
+
- **`divi-5-builder` Claude skill** — block format rules, design patterns, workflow guidance (ships in the dist repo)
|
|
338
174
|
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
After `preset_update` / `preset_create`, the preset option is updated immediately but Divi serves frontend CSS from a **per-post static cache** that neither `wp cache flush` nor `wp transient delete --all` invalidates.
|
|
342
|
-
|
|
343
|
-
Cache location: `wp-content/et-cache/{post_id}/` — contains files like `et-divi-dynamic-tb-*-{post_id}-critical.css` with preset CSS baked in.
|
|
344
|
-
|
|
345
|
-
To force regeneration for a specific page:
|
|
346
|
-
```bash
|
|
347
|
-
rm -rf wp-content/et-cache/{post_id}/
|
|
348
|
-
```
|
|
349
|
-
|
|
350
|
-
Next visit re-renders and writes fresh CSS. Applies to: any change that affects preset-derived CSS output (preset_update, preset_create when used by an existing page, preset_reassign in apply mode).
|
|
175
|
+
## Requirements
|
|
351
176
|
|
|
352
|
-
|
|
177
|
+
- Node.js >= 18.0.0
|
|
178
|
+
- PHP >= 7.4
|
|
179
|
+
- WordPress >= 6.5
|
|
180
|
+
- Divi 5 theme active
|
|
181
|
+
- DiviOps Agent WordPress plugin installed and active
|
|
353
182
|
|
|
354
183
|
## License
|
|
355
184
|
|
|
@@ -0,0 +1,117 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Standardized response envelope (#489).
|
|
3
|
+
*
|
|
4
|
+
* Every diviops tool — server-local or plugin-routed — returns:
|
|
5
|
+
* success: { ok: true, data: T }
|
|
6
|
+
* error: { ok: false, error: { code: string, message: string, hint?: string } }
|
|
7
|
+
*
|
|
8
|
+
* Adoption is per-namespace (#489 + per-namespace follow-ups). Tools that
|
|
9
|
+
* have not adopted yet still pass legacy raw payloads through to consumers;
|
|
10
|
+
* the helpers here coexist with that shape during the rollout window.
|
|
11
|
+
*
|
|
12
|
+
* HTTP status stays orthogonal to envelope shape on the wire — the plugin
|
|
13
|
+
* emits 200 on ok:true and the real status (400/404/409/412/500) on ok:false.
|
|
14
|
+
* The server-side `wp.request` raises on non-2xx today, so error envelopes
|
|
15
|
+
* arrive as thrown errors carrying the JSON body; `wrapResponse` is the
|
|
16
|
+
* normalizer that maps both branches to the typed `DiviopsResponse<T>`.
|
|
17
|
+
*/
|
|
18
|
+
export type DiviopsSuccess<T> = {
|
|
19
|
+
ok: true;
|
|
20
|
+
data: T;
|
|
21
|
+
};
|
|
22
|
+
export type DiviopsErrorBody = {
|
|
23
|
+
code: string;
|
|
24
|
+
message: string;
|
|
25
|
+
hint?: string;
|
|
26
|
+
/**
|
|
27
|
+
* Optional structured payload attached to the error envelope. Used by
|
|
28
|
+
* tools whose failure mode carries machine-readable detail beyond a
|
|
29
|
+
* code/message pair — e.g. `meta_wp_cli` exposes
|
|
30
|
+
* `error.data = { exit_code, stdout, stderr }` so callers can branch
|
|
31
|
+
* on the wp-cli exit code without parsing the message string. Tools
|
|
32
|
+
* that don't need it omit the field entirely.
|
|
33
|
+
*
|
|
34
|
+
* Naming convention: when a namespace-specific error code attaches
|
|
35
|
+
* `data`, list the field shape in the tool's description and in
|
|
36
|
+
* tools.md "Response shape" so consumers know what to expect per
|
|
37
|
+
* code without runtime probing.
|
|
38
|
+
*/
|
|
39
|
+
data?: unknown;
|
|
40
|
+
};
|
|
41
|
+
export type DiviopsFailure = {
|
|
42
|
+
ok: false;
|
|
43
|
+
error: DiviopsErrorBody;
|
|
44
|
+
};
|
|
45
|
+
export type DiviopsResponse<T> = DiviopsSuccess<T> | DiviopsFailure;
|
|
46
|
+
/**
|
|
47
|
+
* Standard error code vocabulary. Plugin and server emit only these codes
|
|
48
|
+
* for the matching condition; namespace-specific extensions use the
|
|
49
|
+
* `<namespace>.<reason>` form (e.g. `library.import_conflict`).
|
|
50
|
+
*/
|
|
51
|
+
export declare const ErrorCodes: {
|
|
52
|
+
/** Target ID does not resolve. HTTP 404. */
|
|
53
|
+
readonly NOT_FOUND: "not_found";
|
|
54
|
+
/** Schema violation, malformed args. HTTP 400. */
|
|
55
|
+
readonly INVALID_INPUT: "invalid_input";
|
|
56
|
+
/** Underlying WordPress error (wraps WP_Error or REST framework error). HTTP 500. */
|
|
57
|
+
readonly WP_ERROR: "wp_error";
|
|
58
|
+
/** Divi-specific error (block parser, validator, etc.). HTTP 500. */
|
|
59
|
+
readonly DIVI_ERROR: "divi_error";
|
|
60
|
+
/** Plugin version below required for this tool (#486 handshake miss). HTTP 412. */
|
|
61
|
+
readonly CAPABILITY_MISSING: "capability_missing";
|
|
62
|
+
/** validate_blocks-detected shape error in submitted markup. HTTP 400. */
|
|
63
|
+
readonly VALIDATION_FAILED: "validation_failed";
|
|
64
|
+
/** Uniqueness collision (#542 / #543). HTTP 409. */
|
|
65
|
+
readonly CONFLICT: "conflict";
|
|
66
|
+
};
|
|
67
|
+
export type ErrorCode = (typeof ErrorCodes)[keyof typeof ErrorCodes] | string;
|
|
68
|
+
/**
|
|
69
|
+
* Typed throw used inside server-local handlers to short-circuit with a
|
|
70
|
+
* specific envelope error code. `wrapResponse` catches it and re-emits.
|
|
71
|
+
*/
|
|
72
|
+
export declare class DiviopsError extends Error {
|
|
73
|
+
readonly code: string;
|
|
74
|
+
readonly hint?: string;
|
|
75
|
+
readonly data?: unknown;
|
|
76
|
+
constructor(code: string, message: string, hint?: string, data?: unknown);
|
|
77
|
+
}
|
|
78
|
+
/**
|
|
79
|
+
* Throw a `DiviopsError`. Helper for ergonomics — `withCode(...)` reads
|
|
80
|
+
* better at call sites than `throw new DiviopsError(...)` and matches the
|
|
81
|
+
* kickoff's helper-API sketch. Pass `data` when the failure carries a
|
|
82
|
+
* structured payload (e.g. wp-cli exit code + captured streams).
|
|
83
|
+
*/
|
|
84
|
+
export declare function withCode(code: string, message: string, hint?: string, data?: unknown): never;
|
|
85
|
+
/**
|
|
86
|
+
* Type guard: is `value` already shaped as a `DiviopsResponse`?
|
|
87
|
+
*
|
|
88
|
+
* Plugin-routed tools return the envelope; server-local tools may return
|
|
89
|
+
* raw values. `wrapResponse` uses this to avoid double-wrapping.
|
|
90
|
+
*/
|
|
91
|
+
export declare function isEnveloped(value: unknown): value is DiviopsResponse<unknown>;
|
|
92
|
+
/**
|
|
93
|
+
* Run an async producer and normalize its outcome to a `DiviopsResponse<T>`.
|
|
94
|
+
*
|
|
95
|
+
* Behavior:
|
|
96
|
+
* - Producer returns an already-enveloped value → pass through unchanged
|
|
97
|
+
* (no double-wrap). Plugin-routed tools land here.
|
|
98
|
+
* - Producer returns a raw value → wrap as `{ok: true, data: <value>}`.
|
|
99
|
+
* Server-local tools land here.
|
|
100
|
+
* - Producer throws `DiviopsError` → emit `{ok: false, error: {code, message, hint?}}`.
|
|
101
|
+
* - Producer throws anything else → emit `{ok: false, error: {code: 'wp_error', message: e.message}}`.
|
|
102
|
+
* The fallback covers thrown HTTP errors from `wp.request` whose body
|
|
103
|
+
* is the plugin's envelope JSON; we attempt to parse the message and
|
|
104
|
+
* promote the embedded code/hint when present.
|
|
105
|
+
*/
|
|
106
|
+
export declare function wrapResponse<T>(producer: () => Promise<T | DiviopsResponse<T>>): Promise<DiviopsResponse<T>>;
|
|
107
|
+
/**
|
|
108
|
+
* Map the data of a `DiviopsResponse` through `fn` (success branch only).
|
|
109
|
+
* Errors pass through unchanged. Use to post-process a wrapped result
|
|
110
|
+
* (e.g. shrink schema, project fields) without unwrapping by hand.
|
|
111
|
+
*/
|
|
112
|
+
export declare function envelopeMap<T, U>(response: DiviopsResponse<T>, fn: (data: T) => U): DiviopsResponse<U>;
|
|
113
|
+
/**
|
|
114
|
+
* Serialize a `DiviopsResponse` as the JSON string an MCP tool emits in its
|
|
115
|
+
* `content[0].text` slot. Single emit point keeps the wire shape consistent.
|
|
116
|
+
*/
|
|
117
|
+
export declare function serializeEnvelope<T>(response: DiviopsResponse<T>): string;
|