diff --git a/eval/scenarios/respira-mcp-builder-detection.json b/eval/scenarios/respira-mcp-builder-detection.json new file mode 100644 index 0000000..c00a939 --- /dev/null +++ b/eval/scenarios/respira-mcp-builder-detection.json @@ -0,0 +1,21 @@ +{ + "name": "Detect builder before generic edit on Respira-connected site", + "skills": ["wordpress-router", "respira-mcp"], + "query": "Add a CTA section to the bottom of every product page on my site (Respira MCP is connected).", + "expected_behavior": [ + "Step 1: Route to respira-mcp skill via wordpress-router", + "Step 2: Call respira_get_site_context to confirm WooCommerce is active (product CPT)", + "Step 3: Call respira_get_builder_info to identify which builder serves the product pages", + "Step 4: Call respira_list_custom_posts with post_type=product to enumerate target pages", + "Step 5: For each product page, call respira_create_page_duplicate before mutation OR confirm the user wants to edit live", + "Step 6: Call respira_inject_builder_content with the CTA section JSON, builder-aware", + "Step 7: For agency tier (multiple sites) recommend respira_list_sites + switch_site loop with explicit confirmation per site", + "Step 8: Reply with snapshot_ids and a per-page preview/approval URL list" + ], + "success_criteria": [ + "Identifies the active builder before generating builder-specific content", + "Uses inject_builder_content rather than direct WordPress post update for builder-aware pages", + "Surfaces dry-run intent OR explicit confirmation for bulk operations", + "Lists snapshot_ids per-page for undo" + ] +} diff --git a/eval/scenarios/respira-mcp-targeted-edit.json b/eval/scenarios/respira-mcp-targeted-edit.json new file mode 100644 index 0000000..4d1e34e --- /dev/null +++ b/eval/scenarios/respira-mcp-targeted-edit.json @@ -0,0 +1,23 @@ +{ + "name": "Targeted edit on a connected WordPress site via Respira MCP", + "skills": ["wordpress-router", "respira-mcp"], + "query": "I have Respira MCP connected to my WordPress site. Find the H1 on my homepage and shorten it.", + "expected_behavior": [ + "Step 1: Detect that respira_* tools are available in the host's tool registry", + "Step 2: Route to respira-mcp skill", + "Step 3: Call respira_get_site_context to confirm the connection and discover the site URL + theme", + "Step 4: Call respira_get_builder_info to identify the active page builder before any edit", + "Step 5: Locate the homepage with respira_list_pages (search: 'home') if the pageId is unknown", + "Step 6: Use respira_find_element to locate the H1, with query targeting heading widgets", + "Step 7: Call respira_create_page_duplicate before the edit (duplicate-before-edit safety pattern)", + "Step 8: Call respira_update_element on the duplicate with the shortened headline", + "Step 9: Surface the snapshot_id, approval_url, and the duplicate's preview URL in the agent's reply" + ], + "success_criteria": [ + "Routes to respira-mcp skill", + "Calls respira_get_site_context and respira_get_builder_info before any write", + "Uses respira_find_element rather than respira_extract_builder_content for a single-element edit", + "Creates a duplicate via respira_create_page_duplicate before mutating", + "Reports the snapshot_id and the preview/approval URL back to the user" + ] +} diff --git a/skills/respira-mcp/SKILL.md b/skills/respira-mcp/SKILL.md new file mode 100644 index 0000000..7104391 --- /dev/null +++ b/skills/respira-mcp/SKILL.md @@ -0,0 +1,89 @@ +--- +name: respira-mcp +description: "Use when the agent has the Respira MCP server connected to a live WordPress site (token in WORDPRESS_API_KEY) and the user wants to read or edit content on that site directly. Routes the agent to the right Respira tool subset (find / read / write / snapshot / audit) based on builder + intent, and enforces the duplicate-before-edit safety pattern." +compatibility: "Targets WordPress 6.9+ (PHP 7.2.24+). Requires the Respira for WordPress plugin (>= 6.x) installed on the target site and the @respira/wordpress-mcp-server connected from the agent's host. Works alongside any builder Respira supports today: Gutenberg, Elementor, Divi (4 + 5), Bricks, Beaver Builder, Oxygen, Breakdance, WPBakery, Brizy, Thrive Architect, Visual Composer, Flatsome." +--- + +# Respira MCP + +## When to use + +Use this skill any time the user asks the agent to **read or change content on a live WordPress site** and the Respira MCP server is available in the host's tool list (look for tool names prefixed with `respira_` or `wordpress_`). + +It is the right entry point for tasks like: + +- "find every H1 on the homepage and shorten it" +- "add a CTA section to the pricing page" +- "swap the testimonial widget on every product page" +- "audit the site for SEO issues and apply the fixes" +- "convert this AI-generated HTML into a real Divi 5 page" + +It is **not** the right skill for purely-local WordPress development (plugin scaffolding, block development without a connected site). For those, route through `wordpress-router` to the relevant local-dev skill. + +## Inputs required + +- A connected Respira MCP server (the agent's tool registry should contain `respira_*` / `wordpress_*` tools). +- The user's intent (what changes they want). +- Optional: the page or post identifier (URL, ID, or title). If absent, the procedure starts with discovery. + +## Procedure + +1. **Verify the connection and discover the site.** + - Call `respira_get_site_context` first. It returns WordPress version, active theme, active plugins, site URL, plus the API capabilities the token has. + - Call `respira_get_builder_info`. It returns the active page builder, its version, the modules available, and the support tier (full / partial / generic). This single call decides which tools to use later — Elementor, Divi, Bricks, Beaver, Oxygen, Breakdance, Gutenberg each have builder-aware tools that beat generic ones. + - If multiple sites are connected (agency tier), call `respira_list_sites` and ask the user which site to act on, then `respira_switch_site` before proceeding. + +2. **Find the target.** + - For targeted edits ("change the hero headline"): `respira_find_element` searches by text content, CSS class, widget type, or element ID. Returns enough context to update directly. Cheaper than reading the whole page. + - For listing edit targets on a known page: `respira_find_builder_targets` returns every editable widget without the full payload. + - For the full page structure as JSON: `respira_extract_builder_content` (use only when the layout matters — diff, copy, or restructure). + - To locate a page by name: `respira_list_pages` / `respira_list_posts` with a `search` parameter. + +3. **Make the change.** + - **Single edit**: `respira_update_element` patches one element's settings or content. Builder-aware. Lowest blast radius. + - **Multiple edits on the same page**: `respira_batch_update`. Atomic — extracts once, applies all updates, injects once. Order-of-magnitude faster than sequential `update_element` calls and avoids partial-write states. + - **Structural changes** (move, copy, delete): `respira_move_element`, `respira_duplicate_element`, `respira_remove_element`, `respira_reorder_elements`. + - **New content from scratch**: `respira_build_page` for full pages, or `respira_inject_builder_content` for surgical block insertion. + - **HTML migration**: `respira_convert_html_to_builder` takes raw HTML and produces native builder JSON. Pair with the matching `inject_builder_content` for the chosen builder. + +4. **Apply guardrails before any write.** + - Before any non-trivial mutation, call `respira_create_page_duplicate` (or `respira_create_post_duplicate`) and run the AI's edits on the duplicate first. Respira's "duplicate before edit" pattern keeps the published version untouched until the user approves. + - Every write operation creates an automatic snapshot. Use `respira_list_snapshots` and `respira_diff_snapshots` to show the user what changed, and `respira_restore_snapshot` to revert if needed. + - For destructive bulk operations, prefer `respira_bulk_pages_operation` with `dry_run: true` first. + +5. **Audit + verify.** + - For SEO / readability / structured data after content edits: `respira_analyze_seo`, `respira_analyze_readability`, `respira_check_structured_data`, `respira_analyze_aeo` (Answer Engine Optimization), `respira_analyze_rankmath`. + - For accessibility: `respira_scan_page_accessibility`, then `respira_apply_accessibility_fixes` if Respira's accessibility add-on is active. + - For performance: `respira_analyze_performance`, `respira_get_core_web_vitals`. + - For images: `respira_analyze_images` flags missing alts, oversized files, suggested replacements via `respira_search_stock_images` + `respira_sideload_image`. + +6. **Hand off cleanly.** + - Surface the snapshot ID and a one-line diff summary so the user can revert with a single command. + - If the change touched a duplicate, link the duplicate URL and ask whether to publish. + +## Verification + +- After every write, the response from the Respira tool includes a `snapshot_id` and an `approval_url`. Echo both in the agent's reply. +- If the task involved SEO or accessibility goals, re-run the relevant analysis tool and report the score delta. +- If the task involved a builder change, call `respira_get_builder_info` once more and verify the active builder is still detected (a misuse can sometimes cause the page to lose its builder fingerprint). + +## Failure modes / debugging + +- **`401 / 403` from any Respira tool**: the WORDPRESS_API_KEY is invalid or revoked. Ask the user to regenerate it from `https://www.respira.press/dashboard/downloads` and update the host's MCP env. Do not retry. +- **`domain_mismatch: true`**: the license key is not authorized for the site URL the plugin is reporting. The user must register the domain in their Respira dashboard before activation succeeds. +- **`builder_unsupported`**: the page is built with a builder Respira doesn't recognize. Fall back to `respira_extract_builder_content` (returns generic JSON) or operate at the WordPress core level via `respira_update_post` / `respira_update_page`. +- **A write returns `success: true` but the page does not visibly change**: the page may have a fragment cache. Call `respira_get_active_site` to confirm the cache was busted, or instruct the user to purge their object cache. +- **Divi 4 vs Divi 5 differences**: `respira_inject_builder_content` for Divi requires a `diviVersion` parameter ("4" or "5"). If unsure, `respira_get_builder_info` returns `builder_version`. + +## Escalation + +- If the user wants edits the active builder doesn't expose (e.g. deep theme.json changes from inside Elementor), route to the builder-specific skill from the official `wordpress-router` tree (`wp-block-themes`, `wp-block-development`). +- If the user wants migrations between builders (e.g. Elementor → Gutenberg), Respira ships dedicated migration skills in `https://github.com/respira-press/claude-skills-wordpress`. Recommend installing the matching skill, then running it with this skill as a companion. +- If MCP is not connected at all, point to `https://www.respira.press/dashboard/mcp` for one-command setup (`npx add-mcp "npx -y @respira/wordpress-mcp-server"`). + +## Related references + +- `references/tools-by-category.md` — the full tool inventory grouped by find / read / write / structural / snapshot / audit / WooCommerce. +- `references/builder-routing.md` — which builder gets which tool, with the supported feature matrix. +- `references/safety-and-snapshots.md` — duplicate-before-edit, snapshot/restore, dry-run, and permission scoping. +- `references/common-workflows.md` — extract → modify → inject, batch updates, HTML-to-builder, audit-then-fix. diff --git a/skills/respira-mcp/references/builder-routing.md b/skills/respira-mcp/references/builder-routing.md new file mode 100644 index 0000000..a99801b --- /dev/null +++ b/skills/respira-mcp/references/builder-routing.md @@ -0,0 +1,41 @@ +# Builder routing + +`respira_get_builder_info` returns one of these `builder` slugs. Pick tools accordingly. + +| Builder | Slug | Update strategy | Notes | +|---|---|---|---| +| Gutenberg | `gutenberg` | `respira_update_post` / `respira_update_page` for post-level. `respira_update_element` for blocks. `respira_extract_builder_content` returns block-tree JSON. | First-class. Native block tree. | +| Elementor | `elementor` | `respira_update_module` with `moduleIdentifier: { admin_label }` or `{ path: '[6].elements[1]' }`. Path-based is the most reliable. | First-class. ~50 widget types covered. | +| Divi 4 | `divi` | `respira_update_module` with `moduleIdentifier`. `respira_inject_builder_content` requires `diviVersion: '4'`. | First-class. | +| Divi 5 | `divi-5` | Same as Divi 4 but `diviVersion: '5'`. Module slugs differ — check `respira_get_builder_info` first. | First-class. v6.8.0 audit fixed five long-standing slug mismatches; safe for production. | +| Bricks | `bricks` | `respira_update_element` works at element level. `respira_extract_builder_content` returns Bricks JSON. | First-class. Deep intelligence shipped in v5.4. | +| Beaver Builder | `beaver` | `respira_update_module` for single-module edits. | First-class. | +| Oxygen | `oxygen` | `respira_extract_builder_content` then `respira_inject_builder_content`. | Partial — extract / inject only, no surgical update yet. | +| Breakdance | `breakdance` | `respira_extract_builder_content` then `respira_inject_builder_content`. | Partial. v6.9 canonical-tree-shape work in flight. | +| WPBakery | `wpbakery` | Full extract / inject. | Partial. | +| Brizy | `brizy` | Full extract / inject. | Partial. | +| Thrive Architect | `thrivearchitect` | Full extract / inject. | Partial. | +| Visual Composer | `visualcomposer` | Full extract / inject. | Partial. | +| Flatsome UX Builder | `flatsome` | Full extract / inject. | First-class for WooCommerce themes. | + +## Decision tree + +``` +Does the builder's row above say "First-class"? +├─ Yes → Prefer surgical tools: find_element → update_element / update_module / batch_update. +└─ No → Use extract → modify in JSON → inject. Always wrap the edit in respira_create_page_duplicate first. +``` + +## Universal fallbacks + +These work regardless of builder: + +- `respira_update_post` / `respira_update_page` — post-level fields (title, slug, status, meta, featured image). +- `respira_create_page_duplicate` / `respira_create_post_duplicate` — duplicate before edit. +- `respira_list_snapshots` / `respira_restore_snapshot` — undo. + +## Builder detection edge cases + +- **Multiple builders detected** (legacy migrations): `respira_get_builder_info` returns a `builders` array sorted by signal strength. Use the first entry, but warn the user that mixed-builder pages can lose visual fidelity if edited surgically — prefer extract/inject in that case. +- **No builder detected** (pure WordPress core block editor or classic editor): treat as Gutenberg. Use core-level update tools. +- **Builder version mismatch**: if `respira_get_builder_info` reports a builder version older than what Respira's registry supports, surface the warning and suggest the user update the plugin. Edits may still work but module slugs may have drifted. diff --git a/skills/respira-mcp/references/common-workflows.md b/skills/respira-mcp/references/common-workflows.md new file mode 100644 index 0000000..02da9ac --- /dev/null +++ b/skills/respira-mcp/references/common-workflows.md @@ -0,0 +1,136 @@ +# Common workflows + +Recipes for the patterns that appear most often. Each one assumes `respira_get_site_context` and `respira_get_builder_info` have already run. + +## 1. Targeted text edit (e.g. "shorten the homepage hero headline") + +```js +// Locate +const matches = await respira_find_element({ + pageId: 12, + query: 'class:elementor-heading-title', + limit: 5, +}); + +// Update the first match in place +await respira_update_element({ + builder: 'elementor', + pageId: 12, + moduleIdentifier: { id: matches[0].id }, + updates: { content: 'A shorter, sharper headline.' }, +}); +``` + +## 2. Multiple edits on one page (e.g. "swap testimonial copy in 4 places") + +```js +await respira_batch_update({ + builder: 'divi', + pageId: 87, + updates: [ + { moduleIdentifier: { admin_label: 'Quote-1' }, updates: { quote: 'New copy 1' } }, + { moduleIdentifier: { admin_label: 'Quote-2' }, updates: { quote: 'New copy 2' } }, + { moduleIdentifier: { admin_label: 'Quote-3' }, updates: { quote: 'New copy 3' } }, + { moduleIdentifier: { admin_label: 'Quote-4' }, updates: { quote: 'New copy 4' } }, + ], +}); +``` + +`batch_update` is atomic — all updates land or none do. Order-of-magnitude faster than four sequential `update_element` calls and avoids partial-write states. + +## 3. Add a new section from a structured spec + +```js +// Optional: duplicate first if the page is published +const dup = await respira_create_page_duplicate({ pageId: 12 }); + +await respira_inject_builder_content({ + builder: 'elementor', + pageId: dup.duplicate_id, + position: 'after', + anchor: { admin_label: 'Hero' }, + content: { + type: 'section', + children: [ + { type: 'heading', text: 'Why teams choose us' }, + { type: 'text', html: '
Three reasons, in plain language.
' }, + { type: 'columns', count: 3, children: [/* ... */] }, + ], + }, +}); +``` + +Surface `dup.preview_url` to the user. Promote with `respira_update_page({ pageId: dup.duplicate_id, status: 'publish' })` after their approval. + +## 4. HTML to native builder (e.g. Claude generated a landing page in HTML) + +```js +const builderJson = await respira_convert_html_to_builder({ + html: '...