WP Astro MCP is a Model Context Protocol server that turns WordPress sites into production-ready Astro projects. It connects to your WordPress REST API, extracts everything (posts, pages, CPTs, SEO, ACF, menus, media), converts HTML to clean Markdown, scaffolds a complete Astro project, and pushes to GitHub — all through conversational commands in Claude Code. Migrating WordPress to Astro involves
Add this skill
npx mdskills install vapvarun/wp-astro-mcpComprehensive WordPress-to-Astro migration MCP with 55+ tools covering extraction, transformation, and deployment
1# WP Astro MCP23> Migrate any WordPress site to Astro — from a single blog to a network of 12 sites with 6,000+ posts. Fully automated via Claude Code.45[](LICENSE)6[](https://nodejs.org)7[](https://modelcontextprotocol.io)89**WP Astro MCP** is a [Model Context Protocol](https://modelcontextprotocol.io) server that turns WordPress sites into production-ready Astro projects. It connects to your WordPress REST API, extracts everything (posts, pages, CPTs, SEO, ACF, menus, media), converts HTML to clean Markdown, scaffolds a complete Astro project, and pushes to GitHub — all through conversational commands in Claude Code.1011---1213## Why This Exists1415Migrating WordPress to Astro involves dozens of tedious steps: fetching content via API, cleaning up page builder markup, resolving shortcodes, building frontmatter, setting up content collections, handling media URLs, generating redirects, deploying. Each site has its own plugins, page builders, and content patterns.1617This MCP server handles all of it. Tell Claude to migrate your site, and it orchestrates 55 specialized tools to get it done.1819---2021## Table of Contents2223- [Quick Start](#quick-start)24- [How It Works](#how-it-works)25- [Use Cases](#use-cases)26- [Full Workflow](#full-workflow)27- [Tool Reference](#tool-reference)28- [Configuration](#configuration)29- [Architecture](#architecture)30- [FAQ](#faq)31- [Troubleshooting](#troubleshooting)32- [Contributing](#contributing)3334---3536## Quick Start3738### 1. Install3940```bash41git clone https://github.com/vapvarun/wp-astro-mcp.git42cd wp-astro-mcp43npm install44npm run build45```4647### 2. Add to Claude Code4849Add to your MCP config (`~/.claude.json` or project `.mcp.json`):5051```json52{53 "mcpServers": {54 "wp-astro-mcp": {55 "command": "node",56 "args": ["/path/to/wp-astro-mcp/dist/index.js"]57 }58 }59}60```6162### 3. Generate a WordPress Application Password6364Go to your WordPress admin → **Users → Profile → Application Passwords**. Enter a name, click "Add New", and copy the password.6566### 4. Start Migrating6768In Claude Code, just say:6970```71Add my WordPress site example.com with username admin72and app password "xxxx xxxx xxxx xxxx xxxx xxxx"73```7475Claude will register the site, auto-detect its capabilities (SEO plugin, page builder, ACF, post types), and guide you through the migration.7677---7879## How It Works8081```82WordPress Site WP Astro MCP Astro Project83┌─────────────┐ REST API ┌──────────────┐ Files ┌─────────────┐84│ Posts │────────────────►│ Extract │───────────────►│ content/ │85│ Pages │ │ Transform │ │ blog/ │86│ CPTs │ │ Scaffold │ │ pages/ │87│ Media │ │ Write │ │ layouts/ │88│ SEO │ │ │ │ pages/ │89│ ACF │ │ SQLite state │ │ astro.config│90│ Menus │ │ for resume │ │ package.json│91└─────────────┘ └──────────────┘ └─────────────┘92 │ │93 │ GitHub API │94 ▼ ▼95 ┌──────────────┐ ┌─────────────┐96 │ git init │ │ Vercel │97 │ create repo │──────────────│ Netlify │98 │ push │ │ Cloudflare │99 └──────────────┘ └─────────────┘100```101102### The Conversion Pipeline103104Every post goes through a 13-step conversion:1051061. **Sanitize** — DOMPurify removes XSS vectors while preserving content1072. **Resolve shortcodes** — 20+ built-in handlers (gallery, video, WPBakery, Divi, CF7) + custom per-site rules1083. **Clean page builders** — Strip Elementor/WPBakery/Divi wrapper divs, keep content1094. **Process Gutenberg** — Remove block comments (`<!-- wp:paragraph -->`), preserve content1105. **Normalize HTML** — Decode entities, remove empty paragraphs, clean inline styles1116. **Convert to Markdown** — Turndown with 12 WordPress-specific rules (captions, galleries, code blocks, embeds)1127. **Rewrite links** — Internal WordPress URLs → Astro paths using URL map1138. **Rewrite media** — Swap domains for media URLs (e.g., `example.com` → `app.example.com`)1149. **Clean artifacts** — Remove conversion leftovers, fix double-encoded entities11510. **Process embeds** — YouTube/Vimeo iframes → plain URLs11611. **Handle galleries** — WordPress galleries → image grids11712. **Fix whitespace** — Ensure proper spacing around headings, lists, code blocks11813. **Validate** — Flag remaining HTML, broken images, content loss119120---121122## Use Cases123124### Migrate a Personal Blog125126You have a WordPress blog with 200 posts and want to move to Astro for better performance.127128```1291. "Add my site myblog.com with username admin and password xxxx"1302. "Analyze the site"1313. "Set output to C:/projects/myblog-astro with Vercel deployment"1324. "Preview some converted posts"1335. "Scaffold the Astro project and export all content"1346. "Push to GitHub"135```136137### Migrate a Business Website138139A company site with Elementor, Yoast SEO, ACF custom fields, and 500 pages.140141```1421. "Add site company.com" (auto-detects Elementor, Yoast, ACF)1432. "Run a content audit" (finds shortcodes, page builder usage, complexity)1443. "Configure shortcodes for CF7 forms and custom widgets"1454. "Preview 5 pages to check Elementor conversion quality"1465. "Export all content, media URLs pointing to app.company.com"1476. "Generate Netlify redirects and push to GitHub"148```149150### Migrate Multiple Sites151152An agency managing 12 WordPress sites that all need to move to Astro.153154```1551. "Add all my sites" (register each with credentials)1562. "List all sites" (see capabilities and content counts)1573. "Analyze buddyxtheme.com" (1,941 posts, RankMath, Elementor)1584. "Export buddyxtheme.com with year/month directories"1595. "Now do vapvarun.com" (switch sites seamlessly)160```161162### Incremental Updates163164Your WordPress site is still active and content keeps changing. Run exports periodically.165166```1671. "Export only posts modified after 2024-01-01"1682. "Resume the export" (picks up where it left off)1693. "Validate the export" (check for missing files or failures)170```171172### Go-Live Domain Swap173174WordPress moves from `example.com` to `app.example.com` (backend), and Astro takes over `example.com`.175176```1771. During development: media URLs point to example.com (no changes needed)1782. At go-live: "Rewrite all media URLs from example.com to app.example.com"1793. "Generate redirects for Vercel"180```181182---183184## Full Workflow185186Here's the complete migration workflow, step by step:187188### Phase 1: Connect & Discover189190```191site_add → Register site, auto-detect WP version, SEO plugin,192 page builder, ACF, WooCommerce, post types, taxonomies193site_analyze → Count all content, estimate migration time194site_export_config → Set output directory, media strategy, deploy platform195```196197### Phase 2: Audit & Prepare198199```200content_audit → Sample posts, detect shortcodes, blocks, page builders,201 embeds, galleries, tables, forms — assess complexity202shortcode_scan → Find all shortcodes in use across the site203shortcode_configure → Set handling rules (strip, keep content, map to component)204cache_terms → Pre-cache all taxonomy terms in SQLite205cache_authors → Pre-cache all authors in SQLite206```207208### Phase 3: Preview & Verify209210```211convert_preview → Convert 3-5 sample posts, review Markdown quality212convert_post → Convert a specific post to inspect in detail213extract_post → View raw WordPress data for debugging214```215216### Phase 4: Scaffold & Export217218```219scaffold_project → Create Astro project (config, layouts, pages, collections, RSS)220export_plan → Pre-flight check: content counts, config validation, time estimate221export_start → Begin batch export (processes first batch, creates SQLite job)222export_resume → Continue processing (call repeatedly until done)223export_progress → Check completion percentage and failures224export_retry → Re-process any failed posts225export_validate → Verify all output files exist and are valid226```227228### Phase 5: Finalize & Publish229230```231generate_redirects → Create redirect rules (Netlify, Vercel, Cloudflare, Apache, Nginx)232media_audit → Check all media references, find broken URLs233media_rewrite → Bulk swap media domains for go-live234github_init → Initialize git repository235github_create_repo → Create GitHub repo (public or private)236github_commit → Stage and commit all changes237github_push → Push to GitHub238github_deploy_config → Generate Vercel/Netlify/Cloudflare config239```240241---242243## Tool Reference244245### Site Management (9 tools)246247| Tool | Description |248|------|-------------|249| `site_add` | Register a WordPress site with credentials. Auto-detects WP version, REST namespaces, SEO plugin (Yoast/RankMath/AIOSEO), page builder (Elementor/WPBakery/Divi/Beaver/Bricks/Oxygen), ACF, WooCommerce, post types, taxonomies. |250| `site_test` | Re-test connection and refresh detected capabilities. |251| `site_list` | List all registered sites with status, version, and content stats. |252| `site_get` | Get full details for a site (credentials are masked). |253| `site_update` | Update site credentials or settings. |254| `site_remove` | Deactivate a site (soft delete, can be reactivated). |255| `site_set_default` | Set a site as default (used when site_id is omitted). |256| `site_analyze` | Deep analysis: count all content types, detect capabilities, estimate migration time, recommend REST API vs WXR. |257| `site_export_config` | Configure per-site export: output dir, content format (md/mdx), media strategy, filters, component library, deploy platform, rate limit. |258259### Content Extraction (13 tools)260261| Tool | Description |262|------|-------------|263| `extract_posts` | Fetch posts with pagination. Any post type, status/date filters, embedded author/media/terms. |264| `extract_post` | Fetch single post with full content (edit context) and content analysis. |265| `extract_all_ids` | Lightweight fetch of all post IDs for two-phase strategy on large sites. |266| `extract_terms` | Fetch taxonomy terms (categories, tags, custom) with pagination. |267| `extract_authors` | Fetch all site authors with avatars. |268| `extract_media` | Fetch media items — single by ID or paginated list. |269| `extract_menus` | Fetch navigation menus (WP 5.9+ and classic). |270| `extract_comments` | Fetch approved comments, optionally filtered by post. |271| `extract_settings` | Fetch site settings (title, tagline, timezone, permalink structure). |272| `extract_widgets` | Fetch sidebar/widget areas (WP 5.8+). |273| `cache_terms` | Bulk cache ALL terms for ALL taxonomies in SQLite. Run before export. |274| `cache_authors` | Bulk cache ALL authors in SQLite. Run before export. |275| `content_audit` | Sample posts, analyze shortcodes/blocks/page builders/embeds, assess complexity distribution. |276277### Transform (6 tools)278279| Tool | Description |280|------|-------------|281| `convert_post` | Convert a single post to Astro Markdown with full frontmatter and issue report. |282| `convert_preview` | Convert a sample batch to preview output quality before full export. |283| `convert_html` | Convert raw HTML to Markdown. Useful for testing conversion rules. |284| `shortcode_list` | List all configured shortcode handling rules for a site. |285| `shortcode_configure` | Set how a shortcode is handled: strip, keep_content, remove, component, html. |286| `shortcode_scan` | Scan posts for all shortcodes in use, find unconfigured ones. |287288### Output & Media (7 tools)289290| Tool | Description |291|------|-------------|292| `scaffold_project` | Create complete Astro project: package.json, astro.config, layouts, content collections, pages, RSS, deploy config. |293| `write_post` | Convert and write a single post as Markdown to the content directory. Supports dry_run. |294| `write_batch` | Convert and write a page of posts. Use with pagination for incremental writing. |295| `generate_redirects` | Generate redirect rules from WordPress→Astro URL map. Supports Netlify, Vercel, Cloudflare, Apache, Nginx. |296| `media_audit` | Scan exported files for media references. Report domains, counts, broken refs. |297| `media_rewrite` | Bulk rewrite media domains in all content files (for go-live domain swap). |298| `list_output` | List files in output directory with stats (counts, sizes, collections). |299300### GitHub (6 tools)301302| Tool | Description |303|------|-------------|304| `github_init` | Initialize git repository with initial commit. |305| `github_create_repo` | Create GitHub repository (personal or org, public or private) and set remote. |306| `github_commit` | Stage all changes and commit with auto-generated message. |307| `github_push` | Push to remote repository. |308| `github_status` | Show git status: branch, changes, remotes, recent commits. |309| `github_deploy_config` | Generate deploy platform config (Vercel, Netlify, Cloudflare Pages). |310311### Export Pipeline (7 tools)312313| Tool | Description |314|------|-------------|315| `export_plan` | Pre-flight check: content counts, config validation, estimated time, recommended batch size. |316| `export_start` | Start batch export. Fetches all post IDs, registers in SQLite, processes first batch. |317| `export_resume` | Continue an in-progress export. Call repeatedly until complete. |318| `export_progress` | Show completion percentage, posts done/failed/pending, recent failures. |319| `export_retry` | Reset failed posts to pending and reprocess them. |320| `export_validate` | Verify output: check files exist, count issues, confirm completeness. |321| `export_cleanup` | Delete export job data from database (does not delete files). |322323### Content Sync (7 tools)324325WordPress is a living CMS — people publish new posts, update content, change images, and delete old pages daily. The sync tools keep your Astro site current without re-running a full export.326327| Tool | Description |328|------|-------------|329| `sync_check` | Compare WordPress vs local files. Report new, updated, and deleted posts without making changes. |330| `sync_pull` | Fetch and write only changed content. Handles new posts, updated posts, and slug changes. |331| `sync_delete` | Remove local files for posts deleted/trashed in WordPress. Cleans up URL map entries. |332| `sync_full` | Complete sync in one command: check → pull → delete → optionally commit to git. |333| `sync_status` | Show sync history: last sync time, changes made, error counts. |334| `sync_schedule` | Generate automated sync config: GitHub Actions workflow, cron script, Netlify/Vercel webhooks. |335| `sync_reset` | Clear sync tracking to force a full re-check on next sync. |336337**How it works:**3381. Queries WordPress REST API for posts modified after the last sync3392. Compares `modified_gmt` timestamps against stored values in SQLite3403. New posts (not in DB) → fetched, converted, written as new Markdown files3414. Updated posts (newer `modified_gmt`) → re-fetched, re-converted, file overwritten3425. Deleted posts (404 from WordPress) → local file removed, URL map cleaned up3436. Slug changes → old file deleted, new file written, redirects updated344345**Sync workflows:**346```347# Manual sync — check what changed, then pull348sync_check → sync_pull → github_commit → github_push349350# One-command sync with auto-commit351sync_full → github_push352353# Automated daily sync via GitHub Actions354sync_schedule (platform: github-actions, interval: daily)355356# Real-time sync via WordPress webhooks357sync_schedule (platform: vercel) # or netlify358```359360---361362## Configuration363364### Site Config (`config/sites.json`)365366```json367{368 "sites": [369 {370 "id": "my-blog",371 "name": "My WordPress Blog",372 "url": "https://example.com",373 "username": "admin",374 "app_password": "xxxx xxxx xxxx xxxx xxxx xxxx",375 "default": true,376 "export": {377 "output_dir": "C:/projects/my-blog-astro",378 "content_format": "md",379 "media_strategy": "rewrite",380 "media_domain": "app.example.com",381 "include_statuses": ["publish"],382 "year_month_dirs": true,383 "component_library": "starwind",384 "deploy_platform": "vercel",385 "rate_limit": 10386 }387 }388 ],389 "github_token": "ghp_your_github_token_here",390 "global_settings": {391 "default_rate_limit": 10,392 "default_content_format": "md",393 "default_component_library": "starwind",394 "default_deploy_platform": "vercel"395 }396}397```398399### Export Config Options400401| Option | Values | Description |402|--------|--------|-------------|403| `output_dir` | Path | Where the Astro project is created |404| `content_format` | `md`, `mdx`, `json` | Markdown, MDX, or JSON output. **Use `json` for sites with 500+ posts** — avoids Astro's markdown parsing OOM. |405| `media_strategy` | `keep`, `rewrite`, `download` | How to handle media URLs |406| `media_domain` | Domain | New domain for media (used with `rewrite` strategy) |407| `include_post_types` | Array | Only export these post types |408| `exclude_post_types` | Array | Skip these post types |409| `include_statuses` | Array | Post statuses to include (default: `["publish"]`) |410| `include_drafts` | Boolean | Include draft posts |411| `include_comments` | Boolean | Export comments |412| `year_month_dirs` | Boolean | Organize posts in `YYYY/MM/` directories |413| `date_after` | ISO date | Only export content after this date |414| `date_before` | ISO date | Only export content before this date |415| `exclude_categories` | Array | Category slugs to skip |416| `exclude_tags` | Array | Tag slugs to skip |417| `component_library` | `starwind`, `fulldev`, `webcoreui`, `none` | Astro UI component library |418| `deploy_platform` | `vercel`, `netlify`, `cloudflare`, `none` | Deploy target |419| `rate_limit` | Number | API requests per second (default: 10) |420421### Environment Variables422423| Variable | Default | Description |424|----------|---------|-------------|425| `WP_ASTRO_MODE` | `router` | `router` (3 tools) or `full` (all 48) |426| `WP_ASTRO_CONFIG` | `config/sites.json` | Config file path |427| `WP_ASTRO_DB` | `data/wp-astro.db` | SQLite database path |428| `WP_ASTRO_LOG_LEVEL` | `info` | `debug`, `info`, `warn`, `error` |429430---431432## Architecture433434### Project Structure435436```437src/438 index.ts — MCP server entry point (stdio transport)439 types/440 index.ts — All TypeScript type definitions441 turndown-plugin-gfm.d.ts — Type declarations for turndown-plugin-gfm442 config/443 sites.ts — SiteManager singleton (multi-site config)444 database.ts — DatabaseManager singleton (SQLite state)445 tools/446 index.ts — Tool aggregation and mode switching447 router.ts — 3 router tools (wp_astro_run/help/describe)448 sites.ts — 9 site management tools449 extract.ts — 13 content extraction tools450 transform.ts — 6 transform tools451 output.ts — 7 output & media tools452 github.ts — 6 GitHub tools453 export.ts — 7 export pipeline tools454 schemas/455 sites.ts — Zod schemas for site tools456 extract.ts — Zod schemas for extract tools457 transform.ts — Zod schemas for transform tools458 output.ts — Zod schemas for output tools459 github.ts — Zod schemas for GitHub tools460 export.ts — Zod schemas for export tools461 services/462 wp-rest-client.ts — WordPress REST API client463 content-analyzer.ts — Content analysis engine464 html-to-markdown.ts — 13-step conversion pipeline465 shortcode-resolver.ts — Shortcode parser and resolver466 link-rewriter.ts — URL rewriting service467 frontmatter-builder.ts — Astro frontmatter generator468 astro-scaffolder.ts — Project structure generator469 content-writer.ts — File writer and media tools470 utils/471 errors.ts — Error classes and response formatters472 logger.ts — Logger singleton (stderr)473config/474 sites.json — Site credentials (gitignored)475 sites.example.json — Config template476data/477 wp-astro.db — SQLite database (gitignored)478```479480### Key Patterns481482- **Router mode**: 3 meta-tools expose 48 actions via `wp_astro_run`, keeping the tool list clean for Claude483- **Singleton managers**: SiteManager, DatabaseManager, Logger — initialized once, shared everywhere484- **Token bucket rate limiting**: Per-site rate limiters with automatic backoff on 429 responses485- **HTTP connection pooling**: Keep-alive agents with 10 max sockets per site486- **SQLite state machine**: Export jobs and per-post state for crash recovery and resumability487- **Zod validation**: All tool inputs validated before processing488- **Error hierarchy**: `WPAstroError` base class with specific subclasses for clean error reporting489490### Database Schema491492```sql493export_jobs — Tracks each migration run (site, status, progress counts)494export_posts — Per-post state (pending/in_progress/completed/failed, retry count)495cached_terms — Pre-fetched taxonomy terms for fast lookups496cached_authors — Pre-fetched authors for fast lookups497url_map — WordPress URL → Astro URL mappings (for redirects and link rewriting)498shortcode_map — Per-site shortcode handling rules499audit_log — Timestamped operation log for debugging500```501502---503504## FAQ505506### General507508**Q: Does this work with any WordPress site?**509Yes. It connects via the standard WordPress REST API, which is available on all WordPress sites since version 4.7. You just need a username and application password.510511**Q: How many posts can it handle?**512It's designed for sites with 2,000-6,000+ posts. The SQLite-backed export engine processes in batches with full resumability — if it gets interrupted, just run `export_resume` and it picks up where it left off.513514**Q: Does it download media/images?**515By default, no. Media stays on your WordPress server. The `rewrite` strategy swaps the domain in URLs (e.g., when WordPress moves to `app.example.com` and Astro takes over the main domain). A `download` strategy is planned for fully self-contained sites.516517**Q: What Astro version does it target?**518Astro 5.x with content collections using the glob loader pattern.519520### WordPress Compatibility521522**Q: Does it work with Elementor?**523Yes. It detects Elementor from REST API namespaces and post meta, strips the wrapper `<div>` elements (sections, columns, widgets), and preserves the actual content.524525**Q: Does it work with WPBakery / Divi / Beaver Builder?**526Yes. Page builder detection covers WPBakery (`[vc_row]` shortcodes), Divi (`et_pb_` classes), Beaver Builder (`fl-` classes), Bricks, and Oxygen. Builder markup is cleaned and content is extracted.527528**Q: Does it preserve Yoast/RankMath SEO data?**529Yes. SEO metadata (title, description, canonical URL, OG image, robots, focus keyword) is extracted from Yoast (`yoast_head_json`) or RankMath (`rank_math_seo`) and included in the frontmatter.530531**Q: Does it handle ACF fields?**532Yes. ACF data from the REST API is normalized: images become `{url, alt, width, height}`, post objects become `{wpId, slug, title}`, repeater fields become arrays, and groups are flattened.533534**Q: What about custom post types?**535All registered post types are auto-detected and can be exported. Each CPT gets its own content collection directory.536537**Q: Does it handle WordPress shortcodes?**538Yes. 20+ shortcodes are handled out of the box (gallery, video, audio, caption, WPBakery elements, Divi modules, Contact Form 7, WPForms, Gravity Forms, buttons, tabs, accordions). You can configure additional shortcodes per-site with `shortcode_configure`.539540### Content Sync & Ongoing Updates541542**Q: How do I keep the Astro site updated when WordPress content changes?**543Use the sync tools: `sync_check` to see what changed, `sync_pull` to fetch updates, or `sync_full` to do everything in one command. Sync detects new posts, updated content, slug changes, and deleted posts by comparing `modified_gmt` timestamps.544545**Q: Can I automate the sync?**546Yes. `sync_schedule` generates GitHub Actions workflows (cron-based), cron scripts, or Netlify/Vercel webhooks for real-time sync on every WordPress post save.547548**Q: What happens when a post is updated in WordPress?**549On the next sync, the tool detects the newer `modified_gmt` timestamp, re-fetches the post from the REST API, re-converts it to Markdown with updated frontmatter (categories, SEO, ACF, images), and overwrites the local file.550551### Export & Deployment552553**Q: Can I preview before doing a full export?**554Yes. Run `convert_preview` to see 3-5 sample posts converted to Markdown. Run `content_audit` to see a complexity report before committing to a full export.555556**Q: What if the export fails halfway?**557Every post's state is tracked in SQLite. Run `export_resume` to continue from where it stopped. Run `export_retry` to reprocess any failed posts.558559**Q: Does it generate redirects?**560Yes. `generate_redirects` creates redirect rules from the WordPress→Astro URL map. Supports Netlify (`_redirects`), Vercel (`vercel.json`), Cloudflare (`_redirects`), Apache (`.htaccess`), and Nginx.561562**Q: Can I deploy to Vercel/Netlify/Cloudflare?**563Yes. The scaffolder generates platform-specific config files, and `github_deploy_config` creates the deploy configuration. Push to GitHub and connect your deploy platform.564565**Q: Can I manage multiple sites?**566Yes. Register as many sites as you want. Each site has its own config, export settings, and state. Set a default site or specify `site_id` per command.567568### Technical569570**Q: What's the difference between `router` and `full` mode?**571In `router` mode (default), only 3 tools are exposed to Claude: `wp_astro_run`, `wp_astro_help`, `wp_astro_describe`. This saves tokens. In `full` mode, all 55 tools are exposed directly. Set via `WP_ASTRO_MODE` env var.572573**Q: Does it handle rate limiting?**574Yes. Each site has a token-bucket rate limiter (default: 10 req/s). If WordPress returns a 429, the rate is automatically halved. Configurable via `rate_limit` in export config.575576**Q: Is content sanitized for XSS?**577Yes. All HTML passes through DOMPurify before conversion. Only safe tags and attributes are allowed. No `<script>`, `onclick`, or `javascript:` URLs survive.578579**Q: Where is state stored?**580In a SQLite database at `data/wp-astro.db` (gitignored). Contains export job state, cached terms/authors, URL mappings, shortcode rules, and audit logs. WAL mode enabled for concurrent reads.581582---583584## Troubleshooting585586### "Authentication failed"587- Verify your application password is correct (generate a new one at `/wp-admin/profile.php`)588- Make sure the username matches exactly (case-sensitive)589- Check that the REST API is not blocked by a security plugin590591### "Cannot connect to site"592- Verify the site URL is correct and includes `https://`593- Check that `/wp-json/` is accessible (try visiting `https://yoursite.com/wp-json/` in a browser)594- Some hosts block REST API access — check with your hosting provider595596### "Rate limited"597- The default rate limit is 10 requests/second. Lower it via `site_export_config` if your host is strict598- The server automatically halves the rate on 429 responses599600### "Export stalled"601- Run `export_progress` to check the current state602- Run `export_resume` to continue processing603- Run `export_retry` if posts failed604605### "Page builder content looks wrong"606- Run `content_audit` to see which builders are detected607- Run `convert_preview` to inspect specific posts608- The pipeline strips builder wrappers and keeps content — some complex layouts may need manual review609610### Astro build OOM with many posts (500+)611612If the **generated Astro site** runs out of memory during `astro build`, the problem is Astro's content collection parsing 500+ markdown files. Each `.md` file goes through Astro's markdown pipeline, which eats ~2MB per file.613614**Fix:** Switch to JSON mode in your export config:615```616site_export_config → content_format: "json"617```618619This writes a single `src/data/blog.json` instead of individual `.md` files. Astro imports the JSON directly — no markdown parsing, no OOM. Content stays as HTML and is rendered via `set:html`.620621| Posts | Recommended Format |622|-------|-------------------|623| < 500 | `md` (content collections) |624| 500-2000 | `json` (recommended) |625| 2000+ | `json` (required for CI/CD) |626627### Build fails with "JavaScript heap out of memory"628629If the **MCP server build** (`npm run build`) runs out of memory, the problem is TypeScript compilation on CI/CD platforms with limited memory (Cloudflare Pages ~4GB, Netlify ~3GB).630631**Quick fix:** Use the built-in memory-optimized build scripts:632```bash633npm run build # 4GB heap limit634npm run build:low-mem # 2GB heap + incremental compilation635```636637**Per-platform fixes:**638639| Platform | Solution |640|----------|----------|641| Cloudflare Pages | Set build command to `npm run build` or add env var `NODE_OPTIONS=--max-old-space-size=3072` |642| Netlify | Add env var `NODE_OPTIONS=--max-old-space-size=3072` in Site Settings → Build & Deploy → Environment |643| Vercel | Usually fine (8GB default). If failing, add `NODE_OPTIONS=--max-old-space-size=4096` in Project Settings → Environment Variables |644| GitHub Actions | Add `env: NODE_OPTIONS: --max-old-space-size=4096` to your build step |645646**Still failing?** Pre-build locally and commit the `dist/` folder — skip compilation on CI entirely.647648See [docs/faq.md](docs/faq.md#build--deployment) for detailed CI/CD setup guides per platform.649650---651652## Requirements653654- **Node.js** 18 or later655- **WordPress** 4.7+ with REST API enabled656- **Application password** (WordPress 5.6+ built-in, or via plugin for older versions)657- **GitHub token** (optional, for repo creation — generate at github.com/settings/tokens)658659---660661## Contributing662663Contributions are welcome. Please:6646651. Fork the repository6662. Create a feature branch6673. Make your changes6684. Run `npm run build` to verify6695. Submit a pull request670671---672673## License674675MIT License. See [LICENSE](LICENSE) for details.676
Full transparency — inspect the skill content before installing.