@respira/wordpress-mcp-server 4.0.10 → 5.2.1
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 +302 -284
- package/dist/server.d.ts +19 -0
- package/dist/server.d.ts.map +1 -1
- package/dist/server.js +866 -11
- package/dist/server.js.map +1 -1
- package/dist/wordpress-client.d.ts +5 -0
- package/dist/wordpress-client.d.ts.map +1 -1
- package/dist/wordpress-client.js +12 -0
- package/dist/wordpress-client.js.map +1 -1
- package/package.json +14 -3
package/dist/server.js
CHANGED
|
@@ -14,7 +14,44 @@ export class RespiraWordPressServer {
|
|
|
14
14
|
sites = new Map();
|
|
15
15
|
defaultSiteId = null;
|
|
16
16
|
versionChecker;
|
|
17
|
-
static MCP_SERVER_VERSION = '
|
|
17
|
+
static MCP_SERVER_VERSION = '5.2.0';
|
|
18
|
+
/**
|
|
19
|
+
* Normalize a tool name: respira_* → wordpress_* for switch dispatch.
|
|
20
|
+
* Tracks whether the deprecated wordpress_* name was used.
|
|
21
|
+
*/
|
|
22
|
+
normalizeToolName(name) {
|
|
23
|
+
if (name.startsWith('respira_')) {
|
|
24
|
+
return { canonical: 'wordpress_' + name.slice('respira_'.length), deprecated: false };
|
|
25
|
+
}
|
|
26
|
+
if (name.startsWith('wordpress_')) {
|
|
27
|
+
return { canonical: name, deprecated: true };
|
|
28
|
+
}
|
|
29
|
+
// woocommerce_* and other names pass through unchanged.
|
|
30
|
+
return { canonical: name, deprecated: false };
|
|
31
|
+
}
|
|
32
|
+
/**
|
|
33
|
+
* Generate respira_* aliases for all wordpress_* tools.
|
|
34
|
+
* The wordpress_* versions get a deprecation prefix in their description.
|
|
35
|
+
*/
|
|
36
|
+
generateDualTools(tools) {
|
|
37
|
+
const result = [];
|
|
38
|
+
for (const tool of tools) {
|
|
39
|
+
if (tool.name.startsWith('wordpress_')) {
|
|
40
|
+
const respiraName = 'respira_' + tool.name.slice('wordpress_'.length);
|
|
41
|
+
// Add the respira_* version (preferred).
|
|
42
|
+
result.push({ ...tool, name: respiraName });
|
|
43
|
+
// Keep the wordpress_* version with deprecation notice.
|
|
44
|
+
result.push({
|
|
45
|
+
...tool,
|
|
46
|
+
description: `[Deprecated — use ${respiraName} instead] ${tool.description}`,
|
|
47
|
+
});
|
|
48
|
+
}
|
|
49
|
+
else {
|
|
50
|
+
result.push(tool);
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
return result;
|
|
54
|
+
}
|
|
18
55
|
constructor(siteConfigs) {
|
|
19
56
|
this.server = new Server({
|
|
20
57
|
name: 'respira-wordpress',
|
|
@@ -23,6 +60,181 @@ export class RespiraWordPressServer {
|
|
|
23
60
|
capabilities: {
|
|
24
61
|
tools: {},
|
|
25
62
|
},
|
|
63
|
+
instructions: `Respira MCP Server v${RespiraWordPressServer.MCP_SERVER_VERSION} — 151 tools for WordPress (130 core + 21 WooCommerce).
|
|
64
|
+
|
|
65
|
+
IMPORTANT: Always use Respira tools to read and edit WordPress content. NEVER access the database directly (SQL queries, wp_postmeta, wp_options), edit PHP files, or modify theme templates to change page content. Respira tools handle all content operations safely with snapshots, cache invalidation, and builder-native formats. Even for simple changes like updating a copyright year in a footer — use respira_find_element, not SQL.
|
|
66
|
+
|
|
67
|
+
## Tool naming
|
|
68
|
+
|
|
69
|
+
Always use respira_* tool names (e.g. respira_update_page, respira_find_element). The wordpress_* names are deprecated aliases and will be removed in v6.0.
|
|
70
|
+
|
|
71
|
+
## Step 1: Understand the site
|
|
72
|
+
|
|
73
|
+
Before making changes, check what you're working with:
|
|
74
|
+
- respira_get_site_context — WordPress version, theme, active plugins, site URL
|
|
75
|
+
- respira_get_builder_info — which page builder is active, its version, available modules, and support level
|
|
76
|
+
|
|
77
|
+
## Step 2: Find content
|
|
78
|
+
|
|
79
|
+
- respira_find_element — search by text content, CSS class, widget type, or element ID (BEST for targeted edits)
|
|
80
|
+
- respira_find_builder_targets — list all editable targets on a page (lighter than full extract)
|
|
81
|
+
- respira_extract_builder_content — get the full page structure as JSON (use when you need the complete layout)
|
|
82
|
+
- respira_list_pages / respira_list_posts — find pages/posts by title or search term
|
|
83
|
+
- respira_read_page / respira_read_post — get full content including meta and builder info
|
|
84
|
+
|
|
85
|
+
## Step 3: Make changes
|
|
86
|
+
|
|
87
|
+
For small edits (text, dates, colors, settings):
|
|
88
|
+
- respira_update_element — update a single element's settings or content
|
|
89
|
+
- respira_batch_update — update multiple elements on one page atomically (extracts once, injects once)
|
|
90
|
+
|
|
91
|
+
For structural changes (move, copy, delete):
|
|
92
|
+
- respira_move_element / respira_reorder_elements — rearrange elements within or across containers
|
|
93
|
+
- respira_duplicate_element — clone an element (inserted after the original)
|
|
94
|
+
- respira_remove_element — delete an element
|
|
95
|
+
|
|
96
|
+
For full page changes:
|
|
97
|
+
- respira_inject_builder_content — replace entire page content (extract first, modify, inject)
|
|
98
|
+
- respira_build_page — create a complete new page from a declarative builder-native structure
|
|
99
|
+
- respira_convert_html_to_builder — convert HTML/CSS prototypes into native builder content with fidelity scoring
|
|
100
|
+
- respira_bulk_pages_operation — apply changes across up to 100 pages (strip_inline_styles, find_and_replace, custom)
|
|
101
|
+
|
|
102
|
+
For adding new content:
|
|
103
|
+
- 27 widget shortcuts: respira_add_heading, respira_add_text, respira_add_button, respira_add_image, respira_add_video, respira_add_section, respira_add_divider, respira_add_spacer, respira_add_icon, respira_add_icon_list, respira_add_social_icons, respira_add_form, respira_add_map, respira_add_counter, respira_add_progress_bar, respira_add_testimonial, respira_add_tabs, respira_add_accordion, respira_add_toggle, respira_add_alert, respira_add_html, respira_add_menu, respira_add_sidebar, respira_add_search, respira_add_gallery, respira_add_slider, respira_add_pricing_table
|
|
104
|
+
|
|
105
|
+
## Common tasks — the right way
|
|
106
|
+
|
|
107
|
+
- "Change a date/text/heading on a page" → respira_find_element (search by the current text) + respira_update_element
|
|
108
|
+
- "Update the footer" → respira_find_element (search by text or CSS class like "footer" or "copyright") + respira_update_element. Footer content lives in the page builder, NOT in theme PHP files.
|
|
109
|
+
- "Change the copyright year" → respira_find_element with the old year as search text + respira_update_element with the new year
|
|
110
|
+
- "Update site title or tagline" → respira_update_option (option_name: "blogname" or "blogdescription")
|
|
111
|
+
- "Add a new section to a page" → respira_add_section or use widget shortcuts
|
|
112
|
+
- "Redesign/rebuild a page" → respira_extract_builder_content → modify the JSON → respira_inject_builder_content
|
|
113
|
+
- "Create a page from HTML" → respira_convert_html_to_builder
|
|
114
|
+
- "Create a page from scratch" → respira_build_page with builder-native structure
|
|
115
|
+
- "Update a navigation menu" → respira_list_menus → respira_list_menu_items → respira_update_menu_item or respira_create_menu_item
|
|
116
|
+
- "Find which page contains specific text" → respira_list_pages with search parameter, then respira_find_element on matching pages
|
|
117
|
+
- "Change an image" → respira_find_element (by type "image" or current alt text) + respira_update_element with new image URL
|
|
118
|
+
- "Add stock photos" → respira_search_stock_images + respira_sideload_image to download into Media Library
|
|
119
|
+
- "Check SEO" → respira_analyze_seo or respira_analyze_rankmath (for RankMath sites)
|
|
120
|
+
- "Optimize images" → respira_analyze_images for recommendations
|
|
121
|
+
- "Undo a change" → respira_list_snapshots + respira_restore_snapshot
|
|
122
|
+
- "Compare before/after" → respira_diff_snapshots
|
|
123
|
+
|
|
124
|
+
## Page builders — how each one works
|
|
125
|
+
|
|
126
|
+
Use respira_get_builder_info first to detect which builder is active. Then use the appropriate content format:
|
|
127
|
+
|
|
128
|
+
### Elementor (Full Intelligence)
|
|
129
|
+
- Content format: Array of sections containing columns containing widgets
|
|
130
|
+
- Each widget has a widgetType (e.g. "heading", "text-editor", "image", "button") and settings object
|
|
131
|
+
- Dynamic schemas: Respira reads Elementor's control registry at runtime, so it knows the exact settings, types, and valid values for every widget
|
|
132
|
+
- Settings validator catches typos: "Unknown setting 'titel'. Did you mean 'title'?"
|
|
133
|
+
- Responsive: settings support _mobile and _tablet suffixes (e.g. "align" for desktop, "align_mobile" for mobile)
|
|
134
|
+
- respira_find_element works with Elementor widget IDs, types, CSS classes, and text content
|
|
135
|
+
- respira_inject_builder_content expects Elementor-native JSON structure
|
|
136
|
+
- HTML-to-Elementor conversion creates native widgets with mapped CSS styles, responsive breakpoints, and optional global color/font creation
|
|
137
|
+
|
|
138
|
+
### Divi 5 (Full Intelligence)
|
|
139
|
+
- Content format: Block comments wrapping JSON module definitions inside <!-- wp:divi/placeholder -->
|
|
140
|
+
- Every module needs "builderVersion": "5.0.1"
|
|
141
|
+
- Hierarchy is strict: Section → Row → Column → Module (never skip levels)
|
|
142
|
+
- Self-closing modules end with " /-->" (space + slash), container modules need matching close tags
|
|
143
|
+
- Flex layout: flexColumnStructure count must match actual column children count
|
|
144
|
+
- Flex fractions: 24_24=100%, 12_24=50%, 8_24=33.3%, 6_24=25%
|
|
145
|
+
- Design tokens: color variables use $variable({"type":"color","value":{"name":"gcid-ID","settings":{}}})$ syntax — always include "settings":{}
|
|
146
|
+
- Heading title is plain text only (no HTML tags); rich text uses \\u003c/\\u003e encoding
|
|
147
|
+
- Button uses linkUrl/linkTarget (NOT Divi 4's url/newWindow)
|
|
148
|
+
- Blurb title must be {"text": "..."} object format, not a plain string
|
|
149
|
+
- 40+ module definitions and 7 page patterns available
|
|
150
|
+
- 13-point payload validation with 16 actionable error messages
|
|
151
|
+
|
|
152
|
+
### Divi 4 Legacy (Smart Defaults)
|
|
153
|
+
- Content format: Shortcode-based — [et_pb_section][et_pb_row][et_pb_column][et_pb_text]content[/et_pb_text][/et_pb_column][/et_pb_row][/et_pb_section]
|
|
154
|
+
- Hierarchy: Section → Row → Column → Module (same as Divi 5, but shortcodes instead of block comments)
|
|
155
|
+
- Module names use et_pb_ prefix: et_pb_text, et_pb_blurb, et_pb_image, et_pb_button, et_pb_cta, et_pb_slider, etc.
|
|
156
|
+
- Settings are shortcode attributes: [et_pb_text text_font="Roboto" text_text_color="#333333"]
|
|
157
|
+
- Button uses url/url_new_window attributes (different from Divi 5's linkUrl/linkTarget)
|
|
158
|
+
- IMPORTANT: When injecting content, set diviVersion to "4" in respira_inject_builder_content
|
|
159
|
+
- Respira auto-detects whether the site runs Divi 4 or Divi 5 — check respira_get_builder_info for the version
|
|
160
|
+
- Element-level operations work via shortcode tree parsing
|
|
161
|
+
|
|
162
|
+
### Gutenberg / Block Editor (Full Intelligence)
|
|
163
|
+
- Content format: Array of block objects with blockName (or type), attrs, and innerBlocks
|
|
164
|
+
- Accepts both simplified format ({"type": "core/heading", "attrs": {"content": "Hello"}}) and native format ({"blockName": "core/heading", ...})
|
|
165
|
+
- Wrapping in {"blocks": [...]} is accepted and auto-unwrapped
|
|
166
|
+
- Common blocks: core/heading, core/paragraph, core/image, core/button, core/columns, core/group, core/cover, core/list
|
|
167
|
+
- Style via attrs.style object or className for theme presets
|
|
168
|
+
|
|
169
|
+
### Beaver Builder (Smart Defaults)
|
|
170
|
+
- Content format: Rows containing column-groups containing columns containing modules
|
|
171
|
+
- Module types: heading, rich-text, photo, button, video, separator, html
|
|
172
|
+
- Settings use BB-native keys (e.g. "heading" for heading text, "photo_src" for image URL)
|
|
173
|
+
- Builder identifier: use "beaver" or "beaver-builder" in respira_inject_builder_content
|
|
174
|
+
- HTML-to-Beaver conversion maps to flat node structure with type mapping
|
|
175
|
+
|
|
176
|
+
### Bricks (Smart Defaults)
|
|
177
|
+
- Content format: Flat array of elements with parent/children references
|
|
178
|
+
- Each element has a "name" (e.g. "section", "container", "heading", "text-basic", "image", "button")
|
|
179
|
+
- Settings in element.settings object
|
|
180
|
+
- CSS regeneration happens automatically on inject and approval merge
|
|
181
|
+
- Cache invalidation on approval merge is automatic
|
|
182
|
+
|
|
183
|
+
### Breakdance (Smart Defaults)
|
|
184
|
+
- Tree-based content structure
|
|
185
|
+
- Uses element-level operations via the generic tree utility
|
|
186
|
+
- respira_find_element and respira_update_element work via default tree traversal
|
|
187
|
+
|
|
188
|
+
### Oxygen (Smart Defaults)
|
|
189
|
+
- Content stored in ct_builder_shortcodes and/or ct_builder_json meta
|
|
190
|
+
- Templates use ct_template post type
|
|
191
|
+
- respira_find_element works via tree traversal on the parsed structure
|
|
192
|
+
|
|
193
|
+
### WPBakery (Smart Defaults)
|
|
194
|
+
- Shortcode-based content format: [vc_row][vc_column][vc_column_text]content[/vc_column_text][/vc_column][/vc_row]
|
|
195
|
+
- Tree-based operations work via parsed shortcode tree
|
|
196
|
+
|
|
197
|
+
### Brizy, Thrive, Visual Composer (Basic Support)
|
|
198
|
+
- Basic read/write via extract/inject
|
|
199
|
+
- Element-level operations use best-effort tree traversal
|
|
200
|
+
- Some operations may return "not supported" for builder-specific features
|
|
201
|
+
|
|
202
|
+
## Site management tools
|
|
203
|
+
|
|
204
|
+
- respira_list_pages / respira_read_page — page CRUD with full builder + meta support
|
|
205
|
+
- respira_list_posts / respira_read_post — post CRUD
|
|
206
|
+
- respira_list_media / respira_upload_media / respira_update_media — media library management
|
|
207
|
+
- respira_search_stock_images / respira_sideload_image — find and import CC-licensed stock photos with auto-attribution
|
|
208
|
+
- respira_list_menus / respira_create_menu / respira_update_menu_item — navigation menus
|
|
209
|
+
- respira_list_options / respira_get_option / respira_update_option — WordPress settings (site title, tagline, timezone, etc.)
|
|
210
|
+
- respira_list_users / respira_create_user / respira_update_user — user management
|
|
211
|
+
- respira_list_comments / respira_create_comment — comment management
|
|
212
|
+
- respira_list_taxonomies / respira_list_terms / respira_create_term — categories, tags, custom taxonomies
|
|
213
|
+
- respira_list_custom_posts — custom post types (WooCommerce products, portfolio items, etc.)
|
|
214
|
+
- respira_list_plugins / respira_install_plugin / respira_activate_plugin — plugin management (experimental, must be enabled in settings)
|
|
215
|
+
|
|
216
|
+
## Analysis tools
|
|
217
|
+
|
|
218
|
+
- respira_analyze_seo — comprehensive SEO audit (meta tags, headings, images, links, schema)
|
|
219
|
+
- respira_analyze_rankmath — RankMath-specific score breakdown with ready-to-apply fixes
|
|
220
|
+
- respira_analyze_aeo — AI Engine Optimization (Perplexity, ChatGPT, AI search)
|
|
221
|
+
- respira_check_seo_issues — quick SEO issue check
|
|
222
|
+
- respira_check_structured_data — JSON-LD and schema validation
|
|
223
|
+
- respira_analyze_performance — page performance metrics
|
|
224
|
+
- respira_get_core_web_vitals — LCP, FID, CLS metrics
|
|
225
|
+
- respira_analyze_images — image optimization opportunities
|
|
226
|
+
- respira_analyze_readability — Flesch score, sentence/paragraph analysis
|
|
227
|
+
|
|
228
|
+
## Safety features
|
|
229
|
+
|
|
230
|
+
- Snapshots: respira_list_snapshots, respira_get_snapshot, respira_diff_snapshots, respira_restore_snapshot — every edit creates a rollback point
|
|
231
|
+
- Duplicate workflow: respira_create_page_duplicate creates a safe copy; edits go to the duplicate; user approves in wp-admin to go live
|
|
232
|
+
- Direct edit mode: when enabled in settings, writes go straight to the original (skip duplicate workflow)
|
|
233
|
+
- respira_validate_security — check content for XSS and security issues before saving
|
|
234
|
+
|
|
235
|
+
## WooCommerce (when addon installed)
|
|
236
|
+
|
|
237
|
+
21 tools for product management, categories, tags, orders, stock, and sales reports. All prefixed woocommerce_*.`,
|
|
26
238
|
});
|
|
27
239
|
this.versionChecker = new RespiraVersionChecker('@respira/wordpress-mcp-server', RespiraWordPressServer.MCP_SERVER_VERSION);
|
|
28
240
|
// Initialize WordPress clients for each site
|
|
@@ -255,7 +467,7 @@ export class RespiraWordPressServer {
|
|
|
255
467
|
},
|
|
256
468
|
{
|
|
257
469
|
name: 'wordpress_get_builder_info',
|
|
258
|
-
description: '
|
|
470
|
+
description: 'Detect which page builder is active on the site and get its version, support level (Full Intelligence / Smart Defaults / Basic), available modules/widgets, and capabilities. Call this first before working with builder content to understand what format to use.',
|
|
259
471
|
inputSchema: {
|
|
260
472
|
type: 'object',
|
|
261
473
|
properties: {},
|
|
@@ -596,7 +808,7 @@ export class RespiraWordPressServer {
|
|
|
596
808
|
},
|
|
597
809
|
{
|
|
598
810
|
name: 'wordpress_extract_builder_content',
|
|
599
|
-
description: 'Extract structured content from a page builder
|
|
811
|
+
description: 'Extract the full structured content from a page as builder-native JSON. Use this to see the complete page layout with all sections, columns, and widgets. For targeted searches, prefer find_element (faster). For a lightweight overview, prefer find_builder_targets. Returns the raw builder data structure that can be modified and passed back to inject_builder_content.',
|
|
600
812
|
inputSchema: {
|
|
601
813
|
type: 'object',
|
|
602
814
|
properties: {
|
|
@@ -615,7 +827,7 @@ export class RespiraWordPressServer {
|
|
|
615
827
|
},
|
|
616
828
|
{
|
|
617
829
|
name: 'wordpress_find_builder_targets',
|
|
618
|
-
description: '
|
|
830
|
+
description: 'List all editable builder modules/blocks on a page with their types, labels, and text content — without returning the full builder JSON. Use this for a quick overview of what is on a page. For finding a specific element by text or class, use find_element instead.',
|
|
619
831
|
inputSchema: {
|
|
620
832
|
type: 'object',
|
|
621
833
|
properties: {
|
|
@@ -642,13 +854,13 @@ export class RespiraWordPressServer {
|
|
|
642
854
|
},
|
|
643
855
|
{
|
|
644
856
|
name: 'wordpress_inject_builder_content',
|
|
645
|
-
description: 'REPLACE (or append to) the entire page builder layout. WARNING: By default this REPLACES all existing content — use mode:"append" to add content without destroying existing elements. For editing a single module, use wordpress_update_module instead. For Divi, diviVersion is required ("4" or "5").',
|
|
857
|
+
description: 'REPLACE (or append to) the entire page builder layout. WARNING: By default this REPLACES all existing content — use mode:"append" to add content without destroying existing elements. For editing a single module, use wordpress_update_module instead. Use exactly: gutenberg, divi, elementor, bricks, beaver, oxygen, breakdance, brizy, thrive, visual-composer, wpbakery. For Divi, diviVersion is required ("4" or "5").',
|
|
646
858
|
inputSchema: {
|
|
647
859
|
type: 'object',
|
|
648
860
|
properties: {
|
|
649
861
|
builder: {
|
|
650
862
|
type: 'string',
|
|
651
|
-
description: 'Builder
|
|
863
|
+
description: 'Builder identifier. Use exactly: gutenberg, divi, elementor, bricks, beaver, oxygen, breakdance, brizy, thrive, visual-composer, wpbakery.',
|
|
652
864
|
},
|
|
653
865
|
pageId: {
|
|
654
866
|
type: 'number',
|
|
@@ -656,7 +868,7 @@ export class RespiraWordPressServer {
|
|
|
656
868
|
},
|
|
657
869
|
content: {
|
|
658
870
|
type: 'object',
|
|
659
|
-
description: 'Structured content to inject. In "replace" mode (default), this REPLACES all existing content. In "append" mode, this is added after existing content.',
|
|
871
|
+
description: 'Structured content to inject in simplified format. In "replace" mode (default), this REPLACES all existing content. In "append" mode, this is added after existing content. For Gutenberg: array of blocks [{type: "core/heading", attrs: {level: 2}, content: "Hello"}, {type: "core/paragraph", attrs: {}, content: "Text here"}]. For Beaver Builder: {rows: [{cols: [{modules: [{type: "rich-text", settings: {text: "..."}}]}]}]}. For Elementor: array of widget objects. For Divi: Divi shortcode or block structure. Use extract_builder_content to see the format for any builder.',
|
|
660
872
|
},
|
|
661
873
|
mode: {
|
|
662
874
|
type: 'string',
|
|
@@ -842,6 +1054,7 @@ export class RespiraWordPressServer {
|
|
|
842
1054
|
operations: {
|
|
843
1055
|
type: 'array',
|
|
844
1056
|
description: 'Patch operations[]',
|
|
1057
|
+
items: { type: 'object' },
|
|
845
1058
|
},
|
|
846
1059
|
editTarget: {
|
|
847
1060
|
type: 'string',
|
|
@@ -2045,10 +2258,13 @@ export class RespiraWordPressServer {
|
|
|
2045
2258
|
destructiveHint: true,
|
|
2046
2259
|
},
|
|
2047
2260
|
];
|
|
2261
|
+
// --- v5.2.0 Elemental: new tools ---
|
|
2262
|
+
tools.push(...this.getElementalTools());
|
|
2048
2263
|
if (await this.isWooCommerceAddonAvailable()) {
|
|
2049
2264
|
tools.push(...this.getWooCommerceTools());
|
|
2050
2265
|
}
|
|
2051
|
-
|
|
2266
|
+
// Generate respira_* aliases for all wordpress_* tools.
|
|
2267
|
+
return this.generateDualTools(tools);
|
|
2052
2268
|
}
|
|
2053
2269
|
async isWooCommerceAddonAvailable() {
|
|
2054
2270
|
if (!this.currentSite) {
|
|
@@ -2133,7 +2349,7 @@ export class RespiraWordPressServer {
|
|
|
2133
2349
|
},
|
|
2134
2350
|
categories: {
|
|
2135
2351
|
type: 'array',
|
|
2136
|
-
items: {},
|
|
2352
|
+
items: { type: 'object' },
|
|
2137
2353
|
description: 'Alternative category payload (IDs, names, slugs, or objects with id/name/slug)',
|
|
2138
2354
|
},
|
|
2139
2355
|
tag_ids: {
|
|
@@ -2143,7 +2359,7 @@ export class RespiraWordPressServer {
|
|
|
2143
2359
|
},
|
|
2144
2360
|
tags: {
|
|
2145
2361
|
type: 'array',
|
|
2146
|
-
items: {},
|
|
2362
|
+
items: { type: 'object' },
|
|
2147
2363
|
description: 'Alternative tag payload (IDs, names, slugs, or objects with id/name/slug)',
|
|
2148
2364
|
},
|
|
2149
2365
|
},
|
|
@@ -2374,6 +2590,22 @@ export class RespiraWordPressServer {
|
|
|
2374
2590
|
];
|
|
2375
2591
|
}
|
|
2376
2592
|
async handleToolCall(name, args) {
|
|
2593
|
+
if (!this.currentSite) {
|
|
2594
|
+
throw new Error('No WordPress site configured');
|
|
2595
|
+
}
|
|
2596
|
+
// Normalize respira_* ↔ wordpress_* names.
|
|
2597
|
+
const { canonical, deprecated } = this.normalizeToolName(name);
|
|
2598
|
+
const result = await this.dispatchToolCall(canonical, args);
|
|
2599
|
+
if (deprecated && result && typeof result === 'object' && !Array.isArray(result)) {
|
|
2600
|
+
return {
|
|
2601
|
+
...result,
|
|
2602
|
+
_deprecation_notice: `Tool name "${name}" is deprecated. Use "respira_${name.slice('wordpress_'.length)}" instead. ` +
|
|
2603
|
+
'wordpress_* names will be removed in v6.0.',
|
|
2604
|
+
};
|
|
2605
|
+
}
|
|
2606
|
+
return result;
|
|
2607
|
+
}
|
|
2608
|
+
async dispatchToolCall(name, args) {
|
|
2377
2609
|
if (!this.currentSite) {
|
|
2378
2610
|
throw new Error('No WordPress site configured');
|
|
2379
2611
|
}
|
|
@@ -2682,10 +2914,633 @@ export class RespiraWordPressServer {
|
|
|
2682
2914
|
}
|
|
2683
2915
|
case 'woocommerce_sales_report':
|
|
2684
2916
|
return await this.currentSite.woocommerceSalesReport(args);
|
|
2685
|
-
|
|
2917
|
+
// --- v5.2.0 Elemental tools ---
|
|
2918
|
+
case 'wordpress_find_element': {
|
|
2919
|
+
const { post_id, ...rest } = args;
|
|
2920
|
+
return await this.currentSite.callRestV2('POST', `/builder/elements/find/${post_id}`, rest);
|
|
2921
|
+
}
|
|
2922
|
+
case 'wordpress_update_element': {
|
|
2923
|
+
const { post_id, ...rest } = args;
|
|
2924
|
+
return await this.currentSite.callRestV2('POST', `/builder/elements/update/${post_id}`, rest);
|
|
2925
|
+
}
|
|
2926
|
+
case 'wordpress_move_element': {
|
|
2927
|
+
const { post_id, ...rest } = args;
|
|
2928
|
+
return await this.currentSite.callRestV2('POST', `/builder/elements/move/${post_id}`, rest);
|
|
2929
|
+
}
|
|
2930
|
+
case 'wordpress_duplicate_element': {
|
|
2931
|
+
const { post_id, ...rest } = args;
|
|
2932
|
+
return await this.currentSite.callRestV2('POST', `/builder/elements/duplicate/${post_id}`, rest);
|
|
2933
|
+
}
|
|
2934
|
+
case 'wordpress_remove_element': {
|
|
2935
|
+
const { post_id, ...rest } = args;
|
|
2936
|
+
return await this.currentSite.callRestV2('POST', `/builder/elements/remove/${post_id}`, rest);
|
|
2937
|
+
}
|
|
2938
|
+
case 'wordpress_batch_update': {
|
|
2939
|
+
const { post_id, ...rest } = args;
|
|
2940
|
+
return await this.currentSite.callRestV2('POST', `/builder/elements/batch/${post_id}`, rest);
|
|
2941
|
+
}
|
|
2942
|
+
case 'wordpress_reorder_elements': {
|
|
2943
|
+
const { post_id, ...rest } = args;
|
|
2944
|
+
return await this.currentSite.callRestV2('POST', `/builder/elements/reorder/${post_id}`, rest);
|
|
2945
|
+
}
|
|
2946
|
+
case 'wordpress_build_page':
|
|
2947
|
+
return await this.currentSite.callRestV2('POST', '/builder/build', args);
|
|
2948
|
+
case 'wordpress_convert_html_to_builder':
|
|
2949
|
+
return await this.currentSite.callRestV2('POST', '/builder/convert', args);
|
|
2950
|
+
case 'wordpress_bulk_pages_operation':
|
|
2951
|
+
return await this.currentSite.callRestV2('POST', '/builder/bulk', args);
|
|
2952
|
+
case 'wordpress_search_stock_images':
|
|
2953
|
+
return await this.currentSite.callRestV2('GET', '/stock-images/search', args);
|
|
2954
|
+
case 'wordpress_sideload_image':
|
|
2955
|
+
return await this.currentSite.callRestV2('POST', '/stock-images/sideload', args);
|
|
2956
|
+
case 'wordpress_get_server_compatibility':
|
|
2957
|
+
return await this.currentSite.callRestV2('GET', '/server/compatibility');
|
|
2958
|
+
// Widget shortcuts — single dispatch via registry.
|
|
2959
|
+
default: {
|
|
2960
|
+
// Check if this is a widget shortcut (wordpress_add_*).
|
|
2961
|
+
if (name.startsWith('wordpress_add_')) {
|
|
2962
|
+
const shortcutName = name.slice('wordpress_'.length);
|
|
2963
|
+
const postId = args.post_id;
|
|
2964
|
+
if (!postId) {
|
|
2965
|
+
throw new Error(`Widget shortcut "${shortcutName}" requires a post_id parameter.`);
|
|
2966
|
+
}
|
|
2967
|
+
const { post_id: _pid, ...settings } = args;
|
|
2968
|
+
return await this.currentSite.callRestV2('POST', `/builder/widget/${shortcutName}/${postId}`, settings);
|
|
2969
|
+
}
|
|
2686
2970
|
throw new Error(`Unknown tool: ${name}`);
|
|
2971
|
+
}
|
|
2687
2972
|
}
|
|
2688
2973
|
}
|
|
2974
|
+
/**
|
|
2975
|
+
* v5.2.0 Elemental tool definitions.
|
|
2976
|
+
*/
|
|
2977
|
+
getElementalTools() {
|
|
2978
|
+
return [
|
|
2979
|
+
// --- Element Operations ---
|
|
2980
|
+
{
|
|
2981
|
+
name: 'wordpress_find_element',
|
|
2982
|
+
description: 'Find an element in a page by ID, type, CSS class, or text content. This is the PRIMARY tool for locating content to edit — use it instead of searching the database or reading PHP files. Works with all 11 page builders. Use identifier_type "content" to search by visible text (e.g. find a heading containing "2025" to update a year). Returns matching element(s) with their position, settings, and element ID for use with update_element.',
|
|
2983
|
+
inputSchema: {
|
|
2984
|
+
type: 'object',
|
|
2985
|
+
properties: {
|
|
2986
|
+
post_id: { type: 'number', description: 'Page/post ID' },
|
|
2987
|
+
identifier_type: {
|
|
2988
|
+
type: 'string',
|
|
2989
|
+
description: 'How to find the element',
|
|
2990
|
+
enum: ['id', 'type', 'class', 'content'],
|
|
2991
|
+
},
|
|
2992
|
+
identifier_value: { type: 'string', description: 'Value to search for' },
|
|
2993
|
+
match_content: {
|
|
2994
|
+
type: 'string',
|
|
2995
|
+
description: 'Optional text content to narrow results when searching by type/class',
|
|
2996
|
+
},
|
|
2997
|
+
},
|
|
2998
|
+
required: ['post_id', 'identifier_type', 'identifier_value'],
|
|
2999
|
+
},
|
|
3000
|
+
readOnlyHint: true,
|
|
3001
|
+
},
|
|
3002
|
+
{
|
|
3003
|
+
name: 'wordpress_update_element',
|
|
3004
|
+
description: 'Update settings or content on a specific element in a page. This is the PRIMARY tool for making content changes — use it for text edits, style changes, image swaps, link updates, etc. Works with all 11 page builders. First use find_element to locate the element, then pass the same identifier here with the updates object containing the new values.',
|
|
3005
|
+
inputSchema: {
|
|
3006
|
+
type: 'object',
|
|
3007
|
+
properties: {
|
|
3008
|
+
post_id: { type: 'number', description: 'Page/post ID' },
|
|
3009
|
+
identifier_type: {
|
|
3010
|
+
type: 'string',
|
|
3011
|
+
enum: ['id', 'type', 'class', 'content'],
|
|
3012
|
+
},
|
|
3013
|
+
identifier_value: { type: 'string', description: 'Value to match' },
|
|
3014
|
+
updates: {
|
|
3015
|
+
type: 'object',
|
|
3016
|
+
description: 'Key-value pairs of settings to update on the matched element',
|
|
3017
|
+
},
|
|
3018
|
+
},
|
|
3019
|
+
required: ['post_id', 'identifier_type', 'identifier_value', 'updates'],
|
|
3020
|
+
},
|
|
3021
|
+
},
|
|
3022
|
+
{
|
|
3023
|
+
name: 'wordpress_move_element',
|
|
3024
|
+
description: 'Move an element to a different container or position within the page.',
|
|
3025
|
+
inputSchema: {
|
|
3026
|
+
type: 'object',
|
|
3027
|
+
properties: {
|
|
3028
|
+
post_id: { type: 'number', description: 'Page/post ID' },
|
|
3029
|
+
element_id: { type: 'string', description: 'Element ID to move' },
|
|
3030
|
+
target_container_id: {
|
|
3031
|
+
type: 'string',
|
|
3032
|
+
description: 'ID of the container to move into',
|
|
3033
|
+
},
|
|
3034
|
+
position: {
|
|
3035
|
+
type: 'number',
|
|
3036
|
+
description: 'Position index within the target container (0-based)',
|
|
3037
|
+
},
|
|
3038
|
+
},
|
|
3039
|
+
required: ['post_id', 'element_id', 'target_container_id', 'position'],
|
|
3040
|
+
},
|
|
3041
|
+
},
|
|
3042
|
+
{
|
|
3043
|
+
name: 'wordpress_duplicate_element',
|
|
3044
|
+
description: 'Duplicate an element in a page. The copy is inserted immediately after the original.',
|
|
3045
|
+
inputSchema: {
|
|
3046
|
+
type: 'object',
|
|
3047
|
+
properties: {
|
|
3048
|
+
post_id: { type: 'number', description: 'Page/post ID' },
|
|
3049
|
+
element_id: { type: 'string', description: 'Element ID to duplicate' },
|
|
3050
|
+
},
|
|
3051
|
+
required: ['post_id', 'element_id'],
|
|
3052
|
+
},
|
|
3053
|
+
},
|
|
3054
|
+
{
|
|
3055
|
+
name: 'wordpress_remove_element',
|
|
3056
|
+
description: 'Remove an element from a page.',
|
|
3057
|
+
inputSchema: {
|
|
3058
|
+
type: 'object',
|
|
3059
|
+
properties: {
|
|
3060
|
+
post_id: { type: 'number', description: 'Page/post ID' },
|
|
3061
|
+
element_id: { type: 'string', description: 'Element ID to remove' },
|
|
3062
|
+
},
|
|
3063
|
+
required: ['post_id', 'element_id'],
|
|
3064
|
+
},
|
|
3065
|
+
destructiveHint: true,
|
|
3066
|
+
},
|
|
3067
|
+
{
|
|
3068
|
+
name: 'wordpress_batch_update',
|
|
3069
|
+
description: 'Apply multiple element operations to a page in a single atomic transaction. Extracts content once, applies all operations, and injects once.',
|
|
3070
|
+
inputSchema: {
|
|
3071
|
+
type: 'object',
|
|
3072
|
+
properties: {
|
|
3073
|
+
post_id: { type: 'number', description: 'Page/post ID' },
|
|
3074
|
+
operations: {
|
|
3075
|
+
type: 'array',
|
|
3076
|
+
description: 'Array of operations: [{action: "update"|"remove"|"move"|"duplicate", ...params}]',
|
|
3077
|
+
items: { type: 'object' },
|
|
3078
|
+
},
|
|
3079
|
+
},
|
|
3080
|
+
required: ['post_id', 'operations'],
|
|
3081
|
+
},
|
|
3082
|
+
},
|
|
3083
|
+
{
|
|
3084
|
+
name: 'wordpress_reorder_elements',
|
|
3085
|
+
description: 'Reorder child elements within a container.',
|
|
3086
|
+
inputSchema: {
|
|
3087
|
+
type: 'object',
|
|
3088
|
+
properties: {
|
|
3089
|
+
post_id: { type: 'number', description: 'Page/post ID' },
|
|
3090
|
+
container_id: { type: 'string', description: 'Container element ID' },
|
|
3091
|
+
new_order: {
|
|
3092
|
+
type: 'array',
|
|
3093
|
+
description: 'Array of element IDs in the desired order',
|
|
3094
|
+
items: { type: 'string' },
|
|
3095
|
+
},
|
|
3096
|
+
},
|
|
3097
|
+
required: ['post_id', 'container_id', 'new_order'],
|
|
3098
|
+
},
|
|
3099
|
+
},
|
|
3100
|
+
// --- Composite Tools ---
|
|
3101
|
+
{
|
|
3102
|
+
name: 'wordpress_build_page',
|
|
3103
|
+
description: 'Create a complete page from a declarative structure. Accepts builder-specific widget definitions and creates the page with all elements in one call. Returns page_id, edit_url, and preview_url.',
|
|
3104
|
+
inputSchema: {
|
|
3105
|
+
type: 'object',
|
|
3106
|
+
properties: {
|
|
3107
|
+
title: { type: 'string', description: 'Page title' },
|
|
3108
|
+
structure: {
|
|
3109
|
+
type: 'array',
|
|
3110
|
+
description: 'Array of widget definitions: [{type: "heading", settings: {title: "Hello"}}]',
|
|
3111
|
+
items: { type: 'object' },
|
|
3112
|
+
},
|
|
3113
|
+
status: {
|
|
3114
|
+
type: 'string',
|
|
3115
|
+
description: 'Page status',
|
|
3116
|
+
enum: ['draft', 'publish'],
|
|
3117
|
+
},
|
|
3118
|
+
builder: {
|
|
3119
|
+
type: 'string',
|
|
3120
|
+
description: 'Target builder (auto-detected if omitted)',
|
|
3121
|
+
},
|
|
3122
|
+
},
|
|
3123
|
+
required: ['title', 'structure'],
|
|
3124
|
+
},
|
|
3125
|
+
},
|
|
3126
|
+
{
|
|
3127
|
+
name: 'wordpress_convert_html_to_builder',
|
|
3128
|
+
description: 'Convert HTML into native page builder content. Extracts CSS styles, maps sections to builder widgets, and creates a page with a fidelity report showing conversion accuracy.',
|
|
3129
|
+
inputSchema: {
|
|
3130
|
+
type: 'object',
|
|
3131
|
+
properties: {
|
|
3132
|
+
html: { type: 'string', description: 'Full HTML string to convert' },
|
|
3133
|
+
builder: {
|
|
3134
|
+
type: 'string',
|
|
3135
|
+
description: 'Target builder (auto-detected if omitted)',
|
|
3136
|
+
},
|
|
3137
|
+
options: {
|
|
3138
|
+
type: 'object',
|
|
3139
|
+
description: 'Conversion options: {status, title, preserve_tokens, font_substitution, responsive}',
|
|
3140
|
+
},
|
|
3141
|
+
},
|
|
3142
|
+
required: ['html'],
|
|
3143
|
+
},
|
|
3144
|
+
},
|
|
3145
|
+
{
|
|
3146
|
+
name: 'wordpress_bulk_pages_operation',
|
|
3147
|
+
description: 'Apply an operation across up to 100 pages. Supports strip_inline_styles, find_and_replace, and custom operations. Each page gets a snapshot for rollback. Rate limited to 3/hr.',
|
|
3148
|
+
inputSchema: {
|
|
3149
|
+
type: 'object',
|
|
3150
|
+
properties: {
|
|
3151
|
+
page_ids: {
|
|
3152
|
+
type: 'array',
|
|
3153
|
+
description: 'Array of page IDs to process (max 100)',
|
|
3154
|
+
items: { type: 'number' },
|
|
3155
|
+
},
|
|
3156
|
+
operation: {
|
|
3157
|
+
type: 'object',
|
|
3158
|
+
description: 'Operation to apply: {type: "strip_inline_styles"|"find_and_replace"|"custom", options: {...}}',
|
|
3159
|
+
},
|
|
3160
|
+
options: {
|
|
3161
|
+
type: 'object',
|
|
3162
|
+
description: 'Execution options: {dry_run: boolean}',
|
|
3163
|
+
},
|
|
3164
|
+
},
|
|
3165
|
+
required: ['page_ids', 'operation'],
|
|
3166
|
+
},
|
|
3167
|
+
},
|
|
3168
|
+
// --- Stock Images ---
|
|
3169
|
+
{
|
|
3170
|
+
name: 'wordpress_search_stock_images',
|
|
3171
|
+
description: 'Search for free stock images via Openverse (Creative Commons). Returns image URLs, titles, authors, and licenses.',
|
|
3172
|
+
inputSchema: {
|
|
3173
|
+
type: 'object',
|
|
3174
|
+
properties: {
|
|
3175
|
+
query: { type: 'string', description: 'Search keywords' },
|
|
3176
|
+
license_type: {
|
|
3177
|
+
type: 'string',
|
|
3178
|
+
description: 'License filter',
|
|
3179
|
+
enum: ['commercial', 'modification', 'all'],
|
|
3180
|
+
},
|
|
3181
|
+
page: { type: 'number', description: 'Page number' },
|
|
3182
|
+
per_page: { type: 'number', description: 'Results per page (max 20)' },
|
|
3183
|
+
},
|
|
3184
|
+
required: ['query'],
|
|
3185
|
+
},
|
|
3186
|
+
readOnlyHint: true,
|
|
3187
|
+
},
|
|
3188
|
+
{
|
|
3189
|
+
name: 'wordpress_sideload_image',
|
|
3190
|
+
description: 'Download a stock image from an allowed domain and add it to the WordPress Media Library. Auto-generates CC attribution caption. Allowed domains: openverse.org, wordpress.org, unsplash.com, pexels.com, pixabay.com.',
|
|
3191
|
+
inputSchema: {
|
|
3192
|
+
type: 'object',
|
|
3193
|
+
properties: {
|
|
3194
|
+
url: { type: 'string', description: 'Image URL (HTTPS only, allowed domains only)' },
|
|
3195
|
+
title: { type: 'string', description: 'Attachment title' },
|
|
3196
|
+
alt: { type: 'string', description: 'Alt text' },
|
|
3197
|
+
caption: { type: 'string', description: 'Caption (auto-generated if omitted)' },
|
|
3198
|
+
},
|
|
3199
|
+
required: ['url'],
|
|
3200
|
+
},
|
|
3201
|
+
},
|
|
3202
|
+
// --- Server Compatibility ---
|
|
3203
|
+
{
|
|
3204
|
+
name: 'wordpress_get_server_compatibility',
|
|
3205
|
+
description: 'Check version compatibility between the MCP server and the WordPress plugin. Returns plugin version, supported version range, and available features.',
|
|
3206
|
+
inputSchema: {
|
|
3207
|
+
type: 'object',
|
|
3208
|
+
properties: {},
|
|
3209
|
+
},
|
|
3210
|
+
readOnlyHint: true,
|
|
3211
|
+
},
|
|
3212
|
+
// --- Widget Shortcuts (27 tools) ---
|
|
3213
|
+
...this.getWidgetShortcutTools(),
|
|
3214
|
+
];
|
|
3215
|
+
}
|
|
3216
|
+
/**
|
|
3217
|
+
* Generate tool definitions for all 27 widget shortcuts.
|
|
3218
|
+
*/
|
|
3219
|
+
getWidgetShortcutTools() {
|
|
3220
|
+
const shortcuts = [
|
|
3221
|
+
{
|
|
3222
|
+
name: 'add_heading',
|
|
3223
|
+
widget: 'heading',
|
|
3224
|
+
description: 'Add a heading widget to a page.',
|
|
3225
|
+
properties: {
|
|
3226
|
+
title: { type: 'string', description: 'Heading text' },
|
|
3227
|
+
tag: { type: 'string', description: 'HTML tag (h1-h6)', enum: ['h1', 'h2', 'h3', 'h4', 'h5', 'h6'] },
|
|
3228
|
+
alignment: { type: 'string', enum: ['left', 'center', 'right'] },
|
|
3229
|
+
},
|
|
3230
|
+
required: ['title'],
|
|
3231
|
+
},
|
|
3232
|
+
{
|
|
3233
|
+
name: 'add_text',
|
|
3234
|
+
widget: 'text-editor',
|
|
3235
|
+
description: 'Add a text/paragraph widget to a page.',
|
|
3236
|
+
properties: {
|
|
3237
|
+
content: { type: 'string', description: 'Text content (HTML allowed)' },
|
|
3238
|
+
alignment: { type: 'string', enum: ['left', 'center', 'right'] },
|
|
3239
|
+
},
|
|
3240
|
+
required: ['content'],
|
|
3241
|
+
},
|
|
3242
|
+
{
|
|
3243
|
+
name: 'add_button',
|
|
3244
|
+
widget: 'button',
|
|
3245
|
+
description: 'Add a button widget to a page.',
|
|
3246
|
+
properties: {
|
|
3247
|
+
text: { type: 'string', description: 'Button label' },
|
|
3248
|
+
url: { type: 'string', description: 'Link URL' },
|
|
3249
|
+
style: { type: 'string', enum: ['primary', 'secondary'] },
|
|
3250
|
+
alignment: { type: 'string', enum: ['left', 'center', 'right'] },
|
|
3251
|
+
},
|
|
3252
|
+
required: ['text'],
|
|
3253
|
+
},
|
|
3254
|
+
{
|
|
3255
|
+
name: 'add_image',
|
|
3256
|
+
widget: 'image',
|
|
3257
|
+
description: 'Add an image widget to a page.',
|
|
3258
|
+
properties: {
|
|
3259
|
+
src: { type: 'string', description: 'Image URL or media library attachment URL' },
|
|
3260
|
+
alt: { type: 'string', description: 'Alt text' },
|
|
3261
|
+
width: { type: 'string', description: 'Image width' },
|
|
3262
|
+
height: { type: 'string', description: 'Image height' },
|
|
3263
|
+
},
|
|
3264
|
+
required: ['src'],
|
|
3265
|
+
},
|
|
3266
|
+
{
|
|
3267
|
+
name: 'add_section',
|
|
3268
|
+
widget: 'section',
|
|
3269
|
+
description: 'Add a section container to a page.',
|
|
3270
|
+
properties: {
|
|
3271
|
+
background_color: { type: 'string', description: 'Background color (hex)' },
|
|
3272
|
+
padding: { type: 'string', description: 'CSS padding value' },
|
|
3273
|
+
},
|
|
3274
|
+
required: [],
|
|
3275
|
+
},
|
|
3276
|
+
{
|
|
3277
|
+
name: 'add_video',
|
|
3278
|
+
widget: 'video',
|
|
3279
|
+
description: 'Add a video widget to a page.',
|
|
3280
|
+
properties: {
|
|
3281
|
+
url: { type: 'string', description: 'Video URL (YouTube, Vimeo, or self-hosted)' },
|
|
3282
|
+
autoplay: { type: 'boolean', description: 'Auto-play video' },
|
|
3283
|
+
},
|
|
3284
|
+
required: ['url'],
|
|
3285
|
+
},
|
|
3286
|
+
{
|
|
3287
|
+
name: 'add_divider',
|
|
3288
|
+
widget: 'divider',
|
|
3289
|
+
description: 'Add a divider/horizontal rule to a page.',
|
|
3290
|
+
properties: {
|
|
3291
|
+
style: { type: 'string', enum: ['solid', 'dashed', 'dotted', 'double'] },
|
|
3292
|
+
color: { type: 'string', description: 'Divider color (hex)' },
|
|
3293
|
+
},
|
|
3294
|
+
required: [],
|
|
3295
|
+
},
|
|
3296
|
+
{
|
|
3297
|
+
name: 'add_spacer',
|
|
3298
|
+
widget: 'spacer',
|
|
3299
|
+
description: 'Add vertical spacing to a page.',
|
|
3300
|
+
properties: {
|
|
3301
|
+
height: { type: 'string', description: 'Spacer height (e.g. "50px")' },
|
|
3302
|
+
},
|
|
3303
|
+
required: [],
|
|
3304
|
+
},
|
|
3305
|
+
{
|
|
3306
|
+
name: 'add_icon',
|
|
3307
|
+
widget: 'icon',
|
|
3308
|
+
description: 'Add an icon widget to a page.',
|
|
3309
|
+
properties: {
|
|
3310
|
+
icon: { type: 'string', description: 'Icon name (e.g. "fa fa-star")' },
|
|
3311
|
+
size: { type: 'string', enum: ['sm', 'md', 'lg', 'xl'] },
|
|
3312
|
+
color: { type: 'string', description: 'Icon color (hex)' },
|
|
3313
|
+
},
|
|
3314
|
+
required: ['icon'],
|
|
3315
|
+
},
|
|
3316
|
+
{
|
|
3317
|
+
name: 'add_icon_list',
|
|
3318
|
+
widget: 'icon-list',
|
|
3319
|
+
description: 'Add an icon list widget to a page.',
|
|
3320
|
+
properties: {
|
|
3321
|
+
items: {
|
|
3322
|
+
type: 'array',
|
|
3323
|
+
description: 'List items: [{icon: "fa fa-check", text: "Item text"}]',
|
|
3324
|
+
items: { type: 'object' },
|
|
3325
|
+
},
|
|
3326
|
+
},
|
|
3327
|
+
required: ['items'],
|
|
3328
|
+
},
|
|
3329
|
+
{
|
|
3330
|
+
name: 'add_social_icons',
|
|
3331
|
+
widget: 'social-icons',
|
|
3332
|
+
description: 'Add social media icons to a page.',
|
|
3333
|
+
properties: {
|
|
3334
|
+
networks: {
|
|
3335
|
+
type: 'array',
|
|
3336
|
+
description: 'Social networks: [{network: "facebook", url: "..."}]',
|
|
3337
|
+
items: { type: 'object' },
|
|
3338
|
+
},
|
|
3339
|
+
},
|
|
3340
|
+
required: ['networks'],
|
|
3341
|
+
},
|
|
3342
|
+
{
|
|
3343
|
+
name: 'add_form',
|
|
3344
|
+
widget: 'form',
|
|
3345
|
+
description: 'Add a contact form to a page.',
|
|
3346
|
+
properties: {
|
|
3347
|
+
fields: {
|
|
3348
|
+
type: 'array',
|
|
3349
|
+
description: 'Form fields: [{type: "text", label: "Name", required: true}]',
|
|
3350
|
+
items: { type: 'object' },
|
|
3351
|
+
},
|
|
3352
|
+
submit_text: { type: 'string', description: 'Submit button text' },
|
|
3353
|
+
},
|
|
3354
|
+
required: ['fields'],
|
|
3355
|
+
},
|
|
3356
|
+
{
|
|
3357
|
+
name: 'add_map',
|
|
3358
|
+
widget: 'google-maps',
|
|
3359
|
+
description: 'Add a Google Maps widget to a page.',
|
|
3360
|
+
properties: {
|
|
3361
|
+
address: { type: 'string', description: 'Address or coordinates' },
|
|
3362
|
+
zoom: { type: 'number', description: 'Zoom level (1-20)' },
|
|
3363
|
+
},
|
|
3364
|
+
required: ['address'],
|
|
3365
|
+
},
|
|
3366
|
+
{
|
|
3367
|
+
name: 'add_counter',
|
|
3368
|
+
widget: 'counter',
|
|
3369
|
+
description: 'Add a number counter widget to a page.',
|
|
3370
|
+
properties: {
|
|
3371
|
+
number: { type: 'number', description: 'Target number to count to' },
|
|
3372
|
+
title: { type: 'string', description: 'Counter label' },
|
|
3373
|
+
prefix: { type: 'string', description: 'Prefix (e.g. "$")' },
|
|
3374
|
+
suffix: { type: 'string', description: 'Suffix (e.g. "+")' },
|
|
3375
|
+
},
|
|
3376
|
+
required: ['number'],
|
|
3377
|
+
},
|
|
3378
|
+
{
|
|
3379
|
+
name: 'add_progress_bar',
|
|
3380
|
+
widget: 'progress-bar',
|
|
3381
|
+
description: 'Add a progress bar widget to a page.',
|
|
3382
|
+
properties: {
|
|
3383
|
+
title: { type: 'string', description: 'Bar label' },
|
|
3384
|
+
percentage: { type: 'number', description: 'Progress percentage (0-100)' },
|
|
3385
|
+
color: { type: 'string', description: 'Bar color (hex)' },
|
|
3386
|
+
},
|
|
3387
|
+
required: ['title', 'percentage'],
|
|
3388
|
+
},
|
|
3389
|
+
{
|
|
3390
|
+
name: 'add_testimonial',
|
|
3391
|
+
widget: 'testimonial',
|
|
3392
|
+
description: 'Add a testimonial widget to a page.',
|
|
3393
|
+
properties: {
|
|
3394
|
+
content: { type: 'string', description: 'Testimonial text' },
|
|
3395
|
+
name: { type: 'string', description: 'Author name' },
|
|
3396
|
+
title: { type: 'string', description: 'Author title/role' },
|
|
3397
|
+
image: { type: 'string', description: 'Author photo URL' },
|
|
3398
|
+
},
|
|
3399
|
+
required: ['content', 'name'],
|
|
3400
|
+
},
|
|
3401
|
+
{
|
|
3402
|
+
name: 'add_tabs',
|
|
3403
|
+
widget: 'tabs',
|
|
3404
|
+
description: 'Add a tabbed content widget to a page.',
|
|
3405
|
+
properties: {
|
|
3406
|
+
tabs: {
|
|
3407
|
+
type: 'array',
|
|
3408
|
+
description: 'Tab definitions: [{title: "Tab 1", content: "Content..."}]',
|
|
3409
|
+
items: { type: 'object' },
|
|
3410
|
+
},
|
|
3411
|
+
},
|
|
3412
|
+
required: ['tabs'],
|
|
3413
|
+
},
|
|
3414
|
+
{
|
|
3415
|
+
name: 'add_accordion',
|
|
3416
|
+
widget: 'accordion',
|
|
3417
|
+
description: 'Add an accordion/FAQ widget to a page.',
|
|
3418
|
+
properties: {
|
|
3419
|
+
items: {
|
|
3420
|
+
type: 'array',
|
|
3421
|
+
description: 'Accordion items: [{title: "Question", content: "Answer"}]',
|
|
3422
|
+
items: { type: 'object' },
|
|
3423
|
+
},
|
|
3424
|
+
},
|
|
3425
|
+
required: ['items'],
|
|
3426
|
+
},
|
|
3427
|
+
{
|
|
3428
|
+
name: 'add_toggle',
|
|
3429
|
+
widget: 'toggle',
|
|
3430
|
+
description: 'Add a toggle (collapsible content) widget to a page.',
|
|
3431
|
+
properties: {
|
|
3432
|
+
title: { type: 'string', description: 'Toggle header text' },
|
|
3433
|
+
content: { type: 'string', description: 'Toggle body content' },
|
|
3434
|
+
},
|
|
3435
|
+
required: ['title', 'content'],
|
|
3436
|
+
},
|
|
3437
|
+
{
|
|
3438
|
+
name: 'add_alert',
|
|
3439
|
+
widget: 'alert',
|
|
3440
|
+
description: 'Add an alert/notice widget to a page.',
|
|
3441
|
+
properties: {
|
|
3442
|
+
message: { type: 'string', description: 'Alert message text' },
|
|
3443
|
+
type: { type: 'string', enum: ['info', 'success', 'warning', 'danger'] },
|
|
3444
|
+
dismissible: { type: 'boolean', description: 'Whether alert can be dismissed' },
|
|
3445
|
+
},
|
|
3446
|
+
required: ['message'],
|
|
3447
|
+
},
|
|
3448
|
+
{
|
|
3449
|
+
name: 'add_html',
|
|
3450
|
+
widget: 'html',
|
|
3451
|
+
description: 'Add a raw HTML widget to a page.',
|
|
3452
|
+
properties: {
|
|
3453
|
+
content: { type: 'string', description: 'Raw HTML content' },
|
|
3454
|
+
},
|
|
3455
|
+
required: ['content'],
|
|
3456
|
+
},
|
|
3457
|
+
{
|
|
3458
|
+
name: 'add_menu',
|
|
3459
|
+
widget: 'nav-menu',
|
|
3460
|
+
description: 'Add a navigation menu widget to a page.',
|
|
3461
|
+
properties: {
|
|
3462
|
+
menu_id: { type: 'number', description: 'WordPress menu ID' },
|
|
3463
|
+
},
|
|
3464
|
+
required: ['menu_id'],
|
|
3465
|
+
},
|
|
3466
|
+
{
|
|
3467
|
+
name: 'add_sidebar',
|
|
3468
|
+
widget: 'sidebar',
|
|
3469
|
+
description: 'Add a sidebar widget area to a page.',
|
|
3470
|
+
properties: {
|
|
3471
|
+
sidebar_id: { type: 'string', description: 'Sidebar/widget area ID' },
|
|
3472
|
+
},
|
|
3473
|
+
required: ['sidebar_id'],
|
|
3474
|
+
},
|
|
3475
|
+
{
|
|
3476
|
+
name: 'add_search',
|
|
3477
|
+
widget: 'search-form',
|
|
3478
|
+
description: 'Add a search form widget to a page.',
|
|
3479
|
+
properties: {
|
|
3480
|
+
placeholder: { type: 'string', description: 'Placeholder text' },
|
|
3481
|
+
},
|
|
3482
|
+
required: [],
|
|
3483
|
+
},
|
|
3484
|
+
{
|
|
3485
|
+
name: 'add_gallery',
|
|
3486
|
+
widget: 'gallery',
|
|
3487
|
+
description: 'Add an image gallery widget to a page.',
|
|
3488
|
+
properties: {
|
|
3489
|
+
images: {
|
|
3490
|
+
type: 'array',
|
|
3491
|
+
description: 'Array of image objects: [{url: "...", alt: "..."}]',
|
|
3492
|
+
items: { type: 'object' },
|
|
3493
|
+
},
|
|
3494
|
+
columns: { type: 'number', description: 'Number of columns (1-10)' },
|
|
3495
|
+
},
|
|
3496
|
+
required: ['images'],
|
|
3497
|
+
},
|
|
3498
|
+
{
|
|
3499
|
+
name: 'add_slider',
|
|
3500
|
+
widget: 'slides',
|
|
3501
|
+
description: 'Add an image/content slider to a page.',
|
|
3502
|
+
properties: {
|
|
3503
|
+
slides: {
|
|
3504
|
+
type: 'array',
|
|
3505
|
+
description: 'Slide definitions: [{image: "...", title: "...", description: "..."}]',
|
|
3506
|
+
items: { type: 'object' },
|
|
3507
|
+
},
|
|
3508
|
+
autoplay: { type: 'boolean', description: 'Auto-advance slides' },
|
|
3509
|
+
},
|
|
3510
|
+
required: ['slides'],
|
|
3511
|
+
},
|
|
3512
|
+
{
|
|
3513
|
+
name: 'add_pricing_table',
|
|
3514
|
+
widget: 'price-table',
|
|
3515
|
+
description: 'Add a pricing table widget to a page.',
|
|
3516
|
+
properties: {
|
|
3517
|
+
title: { type: 'string', description: 'Plan name' },
|
|
3518
|
+
price: { type: 'string', description: 'Price amount (e.g. "29")' },
|
|
3519
|
+
period: { type: 'string', description: 'Billing period (e.g. "/mo")' },
|
|
3520
|
+
features: {
|
|
3521
|
+
type: 'array',
|
|
3522
|
+
description: 'Feature list',
|
|
3523
|
+
items: { type: 'string' },
|
|
3524
|
+
},
|
|
3525
|
+
button_text: { type: 'string', description: 'CTA button text' },
|
|
3526
|
+
button_url: { type: 'string', description: 'CTA button URL' },
|
|
3527
|
+
},
|
|
3528
|
+
required: ['title', 'price'],
|
|
3529
|
+
},
|
|
3530
|
+
];
|
|
3531
|
+
return shortcuts.map((s) => ({
|
|
3532
|
+
name: `wordpress_${s.name}`,
|
|
3533
|
+
description: s.description,
|
|
3534
|
+
inputSchema: {
|
|
3535
|
+
type: 'object',
|
|
3536
|
+
properties: {
|
|
3537
|
+
post_id: { type: 'number', description: 'Page/post ID to add the widget to' },
|
|
3538
|
+
...s.properties,
|
|
3539
|
+
},
|
|
3540
|
+
required: ['post_id', ...s.required],
|
|
3541
|
+
},
|
|
3542
|
+
}));
|
|
3543
|
+
}
|
|
2689
3544
|
async run() {
|
|
2690
3545
|
const transport = new StdioServerTransport();
|
|
2691
3546
|
await this.server.connect(transport);
|